diff --git a/crates/icrate/examples/browser.rs b/crates/icrate/examples/browser.rs index c56c32468..f3fcf0a40 100644 --- a/crates/icrate/examples/browser.rs +++ b/crates/icrate/examples/browser.rs @@ -1,5 +1,5 @@ #![deny(unsafe_op_in_unsafe_fn)] -use core::{cell::OnceCell, ptr::NonNull}; +use core::cell::OnceCell; #[allow(deprecated)] use icrate::{ @@ -19,26 +19,23 @@ use icrate::{ WebKit::{WKNavigation, WKNavigationDelegate, WKWebView}, }; use objc2::{ - declare::{Ivar, IvarDrop}, - declare_class, msg_send, msg_send_id, + declare_class, msg_send_id, mutability::MainThreadOnly, rc::Id, runtime::{AnyObject, ProtocolObject, Sel}, - sel, ClassType, + sel, ClassType, DeclaredClass, }; -type IdCell = Box>>; - macro_rules! idcell { ($name:ident => $this:expr) => { - $this.$name.set($name).expect(&format!( + $this.ivars().$name.set($name).expect(&format!( "ivar should not already be initialized: `{}`", stringify!($name) )); }; ($name:ident <= $this:expr) => { #[rustfmt::skip] - let Some($name) = $this.$name.get() else { + let Some($name) = $this.ivars().$name.get() else { unreachable!( "ivar should be initialized: `{}`", stringify!($name) @@ -47,31 +44,28 @@ macro_rules! idcell { }; } +#[derive(Default)] +struct Ivars { + nav_url: OnceCell>, + web_view: OnceCell>, + window: OnceCell>, +} + declare_class!( - struct Delegate { - nav_url: IvarDrop, "_nav_url">, - web_view: IvarDrop, "_web_view">, - window: IvarDrop, "_window">, - } - mod ivars; + struct Delegate; + // SAFETY: + // - The superclass NSObject does not have any subclassing requirements. + // - Main thread only mutability is correct, since this is an application delegate. + // - `Delegate` does not implement `Drop`. unsafe impl ClassType for Delegate { type Super = NSObject; type Mutability = MainThreadOnly; const NAME: &'static str = "Delegate"; } - unsafe impl Delegate { - #[method(init)] - unsafe fn init(this: *mut Self) -> Option> { - let this: Option<&mut Self> = msg_send![super(this), init]; - this.map(|this| { - Ivar::write(&mut this.nav_url, IdCell::default()); - Ivar::write(&mut this.web_view, IdCell::default()); - Ivar::write(&mut this.window, IdCell::default()); - NonNull::from(this) - }) - } + impl DeclaredClass for Delegate { + type Ivars = Ivars; } unsafe impl NSObjectProtocol for Delegate {} @@ -298,7 +292,9 @@ declare_class!( impl Delegate { pub fn new(mtm: MainThreadMarker) -> Id { - unsafe { msg_send_id![mtm.alloc(), init] } + let this = mtm.alloc(); + let this = this.set_ivars(Ivars::default()); + unsafe { msg_send_id![super(this), init] } } } @@ -307,10 +303,8 @@ fn main() { let app = NSApplication::sharedApplication(mtm); app.setActivationPolicy(NSApplicationActivationPolicyRegular); - // initialize the delegate - let delegate = Delegate::new(mtm); - // configure the application delegate + let delegate = Delegate::new(mtm); let object = ProtocolObject::from_ref(&*delegate); app.setDelegate(Some(object)); diff --git a/crates/icrate/examples/delegate.rs b/crates/icrate/examples/delegate.rs index 5d4e44457..b2453b89c 100644 --- a/crates/icrate/examples/delegate.rs +++ b/crates/icrate/examples/delegate.rs @@ -1,53 +1,38 @@ #![deny(unsafe_op_in_unsafe_fn)] -use std::ptr::NonNull; - use icrate::AppKit::{NSApplication, NSApplicationActivationPolicyRegular, NSApplicationDelegate}; use icrate::Foundation::{ ns_string, MainThreadMarker, NSCopying, NSNotification, NSObject, NSObjectProtocol, NSString, }; -use objc2::declare::{Ivar, IvarBool, IvarDrop, IvarEncode}; use objc2::rc::Id; use objc2::runtime::ProtocolObject; -use objc2::{declare_class, msg_send, msg_send_id, mutability, ClassType}; +use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass}; + +#[derive(Debug)] +#[allow(unused)] +struct Ivars { + ivar: u8, + another_ivar: bool, + box_ivar: Box, + maybe_box_ivar: Option>, + id_ivar: Id, + maybe_id_ivar: Option>, +} declare_class!( - #[derive(Debug)] - struct AppDelegate { - ivar: IvarEncode, - another_ivar: IvarBool<"_another_ivar">, - box_ivar: IvarDrop, "_box_ivar">, - maybe_box_ivar: IvarDrop>, "_maybe_box_ivar">, - id_ivar: IvarDrop, "_id_ivar">, - maybe_id_ivar: IvarDrop>, "_maybe_id_ivar">, - } - - mod ivars; + struct AppDelegate; + // SAFETY: + // - The superclass NSObject does not have any subclassing requirements. + // - Main thread only mutability is correct, since this is an application delegate. + // - `AppDelegate` does not implement `Drop`. unsafe impl ClassType for AppDelegate { type Super = NSObject; type Mutability = mutability::MainThreadOnly; const NAME: &'static str = "MyAppDelegate"; } - unsafe impl AppDelegate { - #[method(initWith:another:)] - unsafe fn init_with( - this: *mut Self, - ivar: u8, - another_ivar: bool, - ) -> Option> { - let this: Option<&mut Self> = unsafe { msg_send![super(this), init] }; - - this.map(|this| { - Ivar::write(&mut this.ivar, ivar); - Ivar::write(&mut this.another_ivar, another_ivar); - Ivar::write(&mut this.maybe_box_ivar, None); - Ivar::write(&mut this.maybe_id_ivar, Some(ns_string!("def").copy())); - Ivar::write(&mut this.box_ivar, Box::new(2)); - Ivar::write(&mut this.id_ivar, NSString::from_str("abc")); - NonNull::from(this) - }) - } + impl DeclaredClass for AppDelegate { + type Ivars = Ivars; } unsafe impl NSObjectProtocol for AppDelegate {} @@ -69,7 +54,16 @@ declare_class!( impl AppDelegate { pub fn new(ivar: u8, another_ivar: bool, mtm: MainThreadMarker) -> Id { - unsafe { msg_send_id![mtm.alloc(), initWith: ivar, another: another_ivar] } + let this = mtm.alloc(); + let this = this.set_ivars(Ivars { + ivar, + another_ivar, + box_ivar: Box::new(2), + maybe_box_ivar: None, + id_ivar: NSString::from_str("abc"), + maybe_id_ivar: Some(ns_string!("def").copy()), + }); + unsafe { msg_send_id![super(this), init] } } } @@ -79,12 +73,8 @@ fn main() { let app = NSApplication::sharedApplication(mtm); app.setActivationPolicy(NSApplicationActivationPolicyRegular); - // initialize the delegate - let delegate = AppDelegate::new(42, true, mtm); - - println!("{delegate:?}"); - // configure the application delegate + let delegate = AppDelegate::new(42, true, mtm); let object = ProtocolObject::from_ref(&*delegate); app.setDelegate(Some(object)); diff --git a/crates/icrate/examples/metal.rs b/crates/icrate/examples/metal.rs index 7b31d439e..b2b2d19f9 100644 --- a/crates/icrate/examples/metal.rs +++ b/crates/icrate/examples/metal.rs @@ -20,12 +20,8 @@ use icrate::{ MetalKit::{MTKView, MTKViewDelegate}, }; use objc2::{ - declare::{Ivar, IvarDrop}, - declare_class, msg_send, msg_send_id, - mutability::MainThreadOnly, - rc::Id, - runtime::ProtocolObject, - ClassType, + declare_class, msg_send_id, mutability::MainThreadOnly, rc::Id, runtime::ProtocolObject, + ClassType, DeclaredClass, }; #[rustfmt::skip] @@ -101,18 +97,16 @@ pub struct Color { pub b: f32, } -type IdCell = Box>>; - macro_rules! idcell { ($name:ident => $this:expr) => { - $this.$name.set($name).expect(&format!( + $this.ivars().$name.set($name).expect(&format!( "ivar should not already be initialized: `{}`", stringify!($name) )); }; ($name:ident <= $this:expr) => { #[rustfmt::skip] - let Some($name) = $this.$name.get() else { + let Some($name) = $this.ivars().$name.get() else { unreachable!( "ivar should be initialized: `{}`", stringify!($name) @@ -121,38 +115,30 @@ macro_rules! idcell { }; } +// declare the desired instance variables +struct Ivars { + start_date: Id, + command_queue: OnceCell>>, + pipeline_state: OnceCell>>, + window: OnceCell>, +} + // declare the Objective-C class machinery declare_class!( - // declare the delegate class with our instance variables - #[rustfmt::skip] // FIXME: rustfmt breaks the macro parsing apparently - struct Delegate { - start_date: IvarDrop, "_start_date">, - command_queue: IvarDrop>, "_command_queue">, - pipeline_state: IvarDrop>, "_pipeline_state">, - window: IvarDrop, "_window">, - } - mod ivars; + struct Delegate; - // declare the class type + // SAFETY: + // - The superclass NSObject does not have any subclassing requirements. + // - Main thread only mutability is correct, since this is an application delegate. + // - `Delegate` does not implement `Drop`. unsafe impl ClassType for Delegate { type Super = NSObject; type Mutability = MainThreadOnly; const NAME: &'static str = "Delegate"; } - // define the Delegate methods (e.g., initializer) - unsafe impl Delegate { - #[method(init)] - unsafe fn init(this: *mut Self) -> Option> { - let this: Option<&mut Self> = msg_send![super(this), init]; - this.map(|this| { - Ivar::write(&mut this.start_date, unsafe { NSDate::now() }); - Ivar::write(&mut this.command_queue, IdCell::default()); - Ivar::write(&mut this.pipeline_state, IdCell::default()); - Ivar::write(&mut this.window, IdCell::default()); - NonNull::from(this) - }) - } + impl DeclaredClass for Delegate { + type Ivars = Ivars; } unsafe impl NSObjectProtocol for Delegate {} @@ -277,7 +263,7 @@ declare_class!( // compute the scene properties let scene_properties_data = &SceneProperties { - time: unsafe { self.start_date.timeIntervalSinceNow() } as f32, + time: unsafe { self.ivars().start_date.timeIntervalSinceNow() } as f32, }; // write the scene properties to the vertex shader argument buffer at index 0 let scene_properties_bytes = NonNull::from(scene_properties_data); @@ -360,7 +346,14 @@ declare_class!( impl Delegate { pub fn new(mtm: MainThreadMarker) -> Id { - unsafe { msg_send_id![mtm.alloc(), init] } + let this = mtm.alloc(); + let this = this.set_ivars(Ivars { + start_date: unsafe { NSDate::now() }, + command_queue: OnceCell::default(), + pipeline_state: OnceCell::default(), + window: OnceCell::default(), + }); + unsafe { msg_send_id![super(this), init] } } } @@ -370,10 +363,8 @@ fn main() { let app = NSApplication::sharedApplication(mtm); app.setActivationPolicy(NSApplicationActivationPolicyRegular); - // initialize the delegate - let delegate = Delegate::new(mtm); - // configure the application delegate + let delegate = Delegate::new(mtm); let object = ProtocolObject::from_ref(&*delegate); app.setDelegate(Some(object)); diff --git a/crates/icrate/tests/array.rs b/crates/icrate/tests/array.rs index 04fb17adf..398c894c6 100644 --- a/crates/icrate/tests/array.rs +++ b/crates/icrate/tests/array.rs @@ -122,7 +122,7 @@ fn test_retains_stored() { drop(obj); expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; expected.assert_current(); } @@ -187,7 +187,7 @@ fn test_iter_minimal_retains() { assert_eq!(iter.count(), 0); expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; expected.assert_current(); } diff --git a/crates/icrate/tests/auto_traits.rs b/crates/icrate/tests/auto_traits.rs index 807f1a492..5f0fa8169 100644 --- a/crates/icrate/tests/auto_traits.rs +++ b/crates/icrate/tests/auto_traits.rs @@ -7,7 +7,7 @@ use icrate::Foundation::*; use objc2::mutability::{Immutable, Mutable}; use objc2::rc::Id; use objc2::runtime::AnyObject; -use objc2::{declare_class, ClassType}; +use objc2::{declare_class, ClassType, DeclaredClass}; // We expect most Foundation types to be UnwindSafe and RefUnwindSafe, // since they follow Rust's usual mutability rules (&T = immutable). @@ -40,6 +40,8 @@ macro_rules! helper { type Mutability = $mutability; const NAME: &'static str = concat!(stringify!($name), "Test"); } + + impl DeclaredClass for $name {} ); }; } diff --git a/crates/icrate/tests/mutable_array.rs b/crates/icrate/tests/mutable_array.rs index a6597ed0b..b3501064e 100644 --- a/crates/icrate/tests/mutable_array.rs +++ b/crates/icrate/tests/mutable_array.rs @@ -151,7 +151,7 @@ fn test_remove() { array.removeAllObjects(); expected.release += 2; - expected.dealloc += 2; + expected.drop += 2; expected.assert_current(); assert_eq!(array.len(), 0); } diff --git a/crates/icrate/tests/mutable_dictionary.rs b/crates/icrate/tests/mutable_dictionary.rs index 61db87512..6d3933079 100644 --- a/crates/icrate/tests/mutable_dictionary.rs +++ b/crates/icrate/tests/mutable_dictionary.rs @@ -102,7 +102,7 @@ fn test_insert_key_copies() { dict.removeAllObjects(); // Release key expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; expected.assert_current(); } @@ -137,7 +137,7 @@ fn test_insert_value_retain_release() { drop(old); expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; expected.assert_current(); } @@ -183,7 +183,7 @@ fn test_remove_clear_release_dealloc() { dict.removeAllObjects(); expected.release += 2; - expected.dealloc += 2; + expected.drop += 2; expected.assert_current(); assert_eq!(dict.len(), 0); } diff --git a/crates/icrate/tests/mutable_set.rs b/crates/icrate/tests/mutable_set.rs index da18d0266..d997449b9 100644 --- a/crates/icrate/tests/mutable_set.rs +++ b/crates/icrate/tests/mutable_set.rs @@ -113,7 +113,7 @@ fn test_clear_release_dealloc() { set.removeAllObjects(); expected.release += 4; - expected.dealloc += 4; + expected.drop += 4; expected.assert_current(); assert_eq!(set.len(), 0); } diff --git a/crates/icrate/tests/set.rs b/crates/icrate/tests/set.rs index fe4a5fbd4..ec96addbd 100644 --- a/crates/icrate/tests/set.rs +++ b/crates/icrate/tests/set.rs @@ -227,7 +227,7 @@ fn test_retains_stored() { drop(obj); expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; expected.assert_current(); } @@ -291,7 +291,7 @@ fn test_iter_minimal_retains() { assert_eq!(iter.count(), 0); expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; expected.assert_current(); } diff --git a/crates/objc2/CHANGELOG.md b/crates/objc2/CHANGELOG.md index c3ab4498d..847c4f156 100644 --- a/crates/objc2/CHANGELOG.md +++ b/crates/objc2/CHANGELOG.md @@ -18,8 +18,104 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added methods `as_ptr` and `as_mut_ptr` to `Allocated`. * Added optimization for converting `msg_send_id![cls, alloc]` to a call to the faster runtime function `objc_alloc`. +* Added `DeclaredClass`, which represents classes that are declared in Rust. +* Added `Allocated::set_ivars`, which sets the instance variables of an + object, and returns the new `rc::PartialInit`. +* Added the ability for `msg_send_id!` to call `super` methods. ### Changed +* **BREAKING**: Changed how instance variables work in `declare_class!`. + + Previously, instance variables had to implement `Encode`, and you had to + initialize them properly, which was difficult to ensure. + + Now, you implement the new `DeclaredClass` trait instead, which helps to + ensure all of this for you. + + ```rust + // Before + declare_class!( + struct MyObject { + object: IvarDrop, "_object">, + data: IvarDrop>, "_data">, + } + + mod ivars; + + unsafe impl ClassType for MyObject { + type Super = NSObject; + type Mutability = InteriorMutable; + const NAME: &'static str = "MyObject"; + } + + unsafe impl MyObject { + #[method(init)] + unsafe fn init(this: *mut Self) -> Option> { + let this: Option<&mut Self> = msg_send![super(this), init]; + this.map(|this| { + Ivar::write(&mut this.object, NSObject::new()); + Ivar::write(&mut this.data, Box::new(MyData::new())); + NonNull::from(this) + }) + } + } + ); + + extern_methods!( + unsafe impl MyObject { + #[method_id(new)] + pub fn new() -> Id; + } + ); + + fn main() { + let obj = MyObject::new(); + println!("{:?}", obj.object); + } + + // After + struct MyIvars { + object: Id, + data: Option>, + } + + declare_class!( + struct MyObject; + + unsafe impl ClassType for MyObject { + type Super = NSObject; + type Mutability = InteriorMutable; + const NAME: &'static str = "MyObject"; + } + + impl DeclaredClass for MyObject { + type Ivars = MyIvars; + } + + unsafe impl MyObject { + #[method_id(init)] + pub fn init(this: Allocated) -> Option> { + let this = this.set_ivars(MyIvars { + object: NSObject::new(), + data: MyData::new(), + }); + unsafe { msg_send_id![super(this), init] } + } + } + ); + + extern_methods!( + unsafe impl MyObject { + #[method_id(new)] + pub fn new() -> Id; + } + ); + + fn main() { + let obj = MyObject::new(); + println!("{:?}", obj.ivars().object); + } + ``` * **BREAKING**: `AnyClass::verify_sel` now take more well-defined types `EncodeArguments` and `EncodeReturn`. * **BREAKING**: Changed how the `mutability` traits work; these no longer have diff --git a/crates/objc2/src/__macro_helpers/common_selectors.rs b/crates/objc2/src/__macro_helpers/common_selectors.rs index 508a2f3c1..6516aa492 100644 --- a/crates/objc2/src/__macro_helpers/common_selectors.rs +++ b/crates/objc2/src/__macro_helpers/common_selectors.rs @@ -28,3 +28,83 @@ pub fn new_sel() -> Sel { pub fn dealloc_sel() -> Sel { __sel_inner!(__sel_data!(dealloc), "dealloc") } + +/// An undocumented selector called by the Objective-C runtime when +/// initalizing instance variables. +#[inline] +#[allow(dead_code)] // May be useful in the future +fn cxx_construct_sel() -> Sel { + __sel_inner!(".cxx_construct\0", ".cxx_construct") +} + +/// Objective-C runtimes call `.cxx_destruct` as part of the final `dealloc` +/// call inside `NSObject` (and has done so since macOS 10.4). +/// +/// While [GCC does document it somewhat][gcc-docs], this is still severely +/// undocumented in clang - but since the selector is emitted into the final +/// binary, it is fine to rely on it being used. +/// +/// Unfortunately though, this only works if the class has been declared +/// statically, since in that case a flag is set to inform the runtime that it +/// needs to run destructors. So unfortunately we can't use this on +/// dynamically declared classes. +/// +/// +/// # ABI +/// +/// The ABI of `.cxx_destruct` in Apple's runtime is actually that it does NOT +/// take a selector, unlike every other Objective-C method, see: +/// +/// +/// So the signature is `extern "C" fn(*mut AnyObject)`. +/// +/// This is likely because it's not a real Objective-C method that can be +/// called from userspace / objc_msgSend, and it's more efficient to not pass +/// the selector. +/// +/// Note that even if Apple decides to suddenly add the selector one day, +/// ignoring it will still be sound, since the function uses the C calling +/// convention, where such an ignored parameter would be allowed on all +/// relevant architectures. +/// +/// TODO: Unsure whether "C-unwind" is allowed? +/// +/// [gcc-docs]: https://gcc.gnu.org/onlinedocs/gcc/Objective-C-and-Objective-C_002b_002b-Dialect-Options.html#index-fobjc-call-cxx-cdtors +#[inline] +#[allow(dead_code)] // May be useful in the future +fn cxx_destruct_sel() -> Sel { + __sel_inner!(".cxx_destruct\0", ".cxx_destruct") +} + +#[cfg(test)] +mod tests { + use core::sync::atomic::{AtomicBool, Ordering}; + + use crate::declare::ClassBuilder; + use crate::rc::Id; + use crate::runtime::NSObject; + use crate::{msg_send_id, ClassType}; + + use super::*; + + /// Test the unfortunate fact that we can't use .cxx_destruct on dynamic classes. + #[test] + fn test_destruct() { + static HAS_RUN: AtomicBool = AtomicBool::new(false); + + let mut builder = ClassBuilder::new("TestCxxDestruct", NSObject::class()).unwrap(); + + unsafe extern "C" fn destruct(_: *mut NSObject, _: Sel) { + HAS_RUN.store(true, Ordering::Relaxed); + } + + // Note: The ABI is not upheld here, but its fine for this test + unsafe { builder.add_method(cxx_destruct_sel(), destruct as unsafe extern "C" fn(_, _)) }; + + let cls = builder.register(); + + let obj: Id = unsafe { msg_send_id![cls, new] }; + drop(obj); + assert!(!HAS_RUN.load(Ordering::Relaxed)); + } +} diff --git a/crates/objc2/src/__macro_helpers/declare_class.rs b/crates/objc2/src/__macro_helpers/declare_class.rs index 38eaba628..7bfc4fc22 100644 --- a/crates/objc2/src/__macro_helpers/declare_class.rs +++ b/crates/objc2/src/__macro_helpers/declare_class.rs @@ -4,18 +4,15 @@ use core::marker::PhantomData; #[cfg(all(debug_assertions, feature = "verify"))] use std::collections::HashSet; +use crate::declare::ClassBuilder; +use crate::encode::{Encode, Encoding}; +use crate::rc::{Allocated, Id}; +use crate::runtime::{AnyClass, AnyObject, MessageReceiver, MethodImplementation, Sel}; #[cfg(all(debug_assertions, feature = "verify"))] use crate::runtime::{AnyProtocol, MethodDescription}; +use crate::{ClassType, DeclaredClass, Message, ProtocolType}; -use objc2_encode::Encoding; - -use crate::declare::{ClassBuilder, IvarType}; -use crate::encode::Encode; -use crate::rc::{Allocated, Id}; -use crate::runtime::{AnyClass, MethodImplementation, Sel}; -use crate::runtime::{AnyObject, MessageReceiver}; -use crate::{ClassType, Message, ProtocolType}; - +use super::declared_ivars::{register_with_ivars, setup_dealloc}; use super::{CopyOrMutCopy, Init, MaybeUnwrap, New, Other}; use crate::mutability; @@ -213,7 +210,7 @@ fn failed_declaring_class(name: &str) -> ! { panic!("could not create new class {name}. Perhaps a class with that name already exists?") } -impl ClassBuilderHelper { +impl ClassBuilderHelper { #[inline] #[track_caller] #[allow(clippy::new_without_default)] @@ -221,11 +218,13 @@ impl ClassBuilderHelper { where T::Super: ClassType, { - let builder = match ClassBuilder::new(T::NAME, ::class()) { + let mut builder = match ClassBuilder::new(T::NAME, ::class()) { Some(builder) => builder, None => failed_declaring_class(T::NAME), }; + setup_dealloc::(&mut builder); + Self { builder, p: PhantomData, @@ -291,13 +290,8 @@ impl ClassBuilderHelper { } #[inline] - pub fn add_static_ivar(&mut self) { - self.builder.add_static_ivar::() - } - - #[inline] - pub fn register(self) -> &'static AnyClass { - self.builder.register() + pub fn register(self) -> (&'static AnyClass, isize, isize) { + register_with_ivars::(self.builder) } } @@ -324,7 +318,7 @@ pub struct ClassProtocolMethodsBuilder<'a, T: ?Sized> { registered_class_methods: HashSet, } -impl ClassProtocolMethodsBuilder<'_, T> { +impl ClassProtocolMethodsBuilder<'_, T> { // Addition: This restricts to callee `T` #[inline] pub unsafe fn add_method(&mut self, sel: Sel, func: F) diff --git a/crates/objc2/src/__macro_helpers/declared_ivars.rs b/crates/objc2/src/__macro_helpers/declared_ivars.rs new file mode 100644 index 000000000..1761059fb --- /dev/null +++ b/crates/objc2/src/__macro_helpers/declared_ivars.rs @@ -0,0 +1,605 @@ +//! # Supporting code for instance variables on declared classes. +//! +//! Adding instance variables to Objective-C classes is fairly simple, it can +//! be done using `ClassBuilder::add_ivar`. +//! +//! However, things become more complicated once we have to handle `Drop`, +//! deallocation and unwind safety. +//! +//! Basically, instead of storing the ivars directly, we store it as the +//! following tagged enum: +//! ``` +//! #[repr(u8)] +//! enum ActualIvar { +//! Allocated = 0, +//! PartialInit(T::Ivars), +//! Finalized(T::Ivars), +//! } +//! ``` +//! +//! For performance reasons, we unfortunately can't write it that cleanly: we +//! want the data and the drop flag as two separate ivars instead of combining +//! them into one, since that will give the layout algorithm in the +//! Objective-C runtime more information to work with, and it allows us to +//! selectively omit the drop flag or the data storage when either is not +//! needed. +//! +//! Ideally, we'd be able to somehow statically detect when the ivars have a +//! zero niche, which would allow us to know if the type is safe to drop when +//! zero-initialized: +//! ``` +//! None::.is_all_zeroes_bitpattern() +//! ``` +//! +//! However, detecting if the `None` is all zeroes requires reading the bytes, +//! which is [unsound for types that may have padding][unsound-read-padding], +//! since that padding is uninitialized. +//! +//! So this is an optimization that we don't yet do, but that may be possible +//! in the future using something like `bytemuck::Zeroable`. +//! +//! [unsound-read-padding]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ea068e8d9e55801aa9520ea914eb2822 + +use alloc::borrow::Cow; +use alloc::format; +use core::mem; +use core::ptr::{self, NonNull}; + +use crate::declare::ClassBuilder; +use crate::encode::{Encode, Encoding}; +use crate::runtime::{AnyClass, AnyObject, MessageReceiver, Sel}; +use crate::{sel, ClassType, DeclaredClass}; + +/// A type representing the drop flags that may be set for a type. +#[repr(u8)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) enum DropFlag { + /// Set to zero to ensure that this is the default when created by the + /// Objective-C runtime. + /// + /// Ivars are [documented][obj-init-zeroed] to be zero-initialized after + /// allocation, and that has been true since at least [the Objective-C + /// version shipped with Mac OS X 10.0][objc4-208-init]. + /// + /// [obj-init-zeroed]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html#//apple_ref/doc/uid/TP40011210-CH4-SW7 + /// [objc4-208-init]: https://github.com/apple-oss-distributions/objc4/blob/objc4-208/runtime/objc-class.m#L367 + #[default] + Allocated = 0x00, + /// Used when `mem::needs_drop::()`, or with debug assertions enabled. + InitializedIvars = 0x0f, + /// Used when `mem::needs_drop::()`, or with debug assertions enabled. + Finalized = 0xff, +} + +// SAFETY: The DropFlag is #[repr(u8)] +unsafe impl Encode for DropFlag { + const ENCODING: Encoding = u8::ENCODING; +} + +pub trait DeclaredIvarsHelper { + const HAS_IVARS: bool; + const HAS_DROP_FLAG: bool; +} + +impl DeclaredIvarsHelper for T { + /// Only add ivar if we need the runtime to allocate memory for it. + /// + /// We can avoid doing so if the type is a zero-sized type (ZST), and the + /// required alignment is less than the alignment of a pointer (objects + /// are guaranteed to have at least that alignment themselves). + const HAS_IVARS: bool = { + mem::size_of::() > 0 + || mem::align_of::() > mem::align_of::<*mut AnyObject>() + }; + /// Only add drop flag if the type or the ivars need it. + /// + /// `needs_drop::` can reliably detect a direct implementation of + /// `Drop`, since the type only includes `ManuallyDrop` or `PhantomData` + /// fields. + const HAS_DROP_FLAG: bool = mem::needs_drop::() || mem::needs_drop::(); +} + +/// Helper function for getting a pointer to the instance variable. +/// +/// # Safety +/// +/// The pointer must be valid, and a the instance variable offset (if it has +/// any) must have been initialized. +#[inline] +unsafe fn ptr_to_ivar(ptr: NonNull) -> NonNull { + // This is called even when there is no ivars, but that's fine, since in + // that case the ivar is zero-sized, and we can still compute a pointer to + // the ivar. + // + // debug_assert!(T::HAS_IVARS); + + // SAFETY: That an instance variable with the given type exists at the + // specified offset is ensured by `DeclaredClass` trait implementor. + unsafe { AnyObject::ivar_at_offset::(ptr.cast(), T::__ivars_offset()) } +} + +/// Helper function for getting a pointer to the drop flag. +/// +/// # Safety +/// +/// The pointer must be valid and have an initialized drop flag. +#[inline] +unsafe fn ptr_to_drop_flag(ptr: NonNull) -> *mut DropFlag { + debug_assert!(T::HAS_DROP_FLAG); + // SAFETY: That a drop flag exists at the specified offset is ensured + // by caller. + unsafe { AnyObject::ivar_at_offset::(ptr.cast(), T::__drop_flag_offset()).as_ptr() } +} + +pub(crate) fn setup_dealloc(builder: &mut ClassBuilder) +where + T::Super: ClassType, +{ + // Add dealloc if the class or the ivars need dropping. + if mem::needs_drop::() || mem::needs_drop::() { + let func: unsafe extern "C" fn(_, _) = dealloc::; + // SAFETY: The function signature is correct. + unsafe { builder.add_method(sel!(dealloc), func) }; + } +} + +/// The `dealloc` Objective-C method. +/// +/// See the following links for more details about `dealloc`: +/// - +/// - +/// - +/// +/// TODO: Change this to `extern "C-unwind"`, unwinding in dealloc is allowed. +unsafe extern "C" fn dealloc(this: NonNull, cmd: Sel) +where + T::Super: ClassType, +{ + /// Helper function for marking the cold path when branching. + #[inline] + #[cold] + fn cold_path() {} + + // SAFETY: `dealloc` is only registered when there is a need for dropping, + // and hence a need for a drop flag. + let drop_flag = unsafe { *ptr_to_drop_flag(this) }; + + if mem::needs_drop::() { + match drop_flag { + // Don't deallocate the current instance if it has not been fully + // initialized. + // + // Note that we still run the superclass deinitializer below. + DropFlag::Allocated | DropFlag::InitializedIvars => cold_path(), + // SAFETY: This is the `dealloc` method, so we know that the type + // never needs to be deallocated again. + // + // Additionally, we know that the type was fully initialized, since + // that's what the drop flag says. + // + // TODO: This can unwind, is it correct to just let that + // propagate? + DropFlag::Finalized => unsafe { ptr::drop_in_place(this.as_ptr()) }, + } + } + + // TODO: Debug assertions that the retain count is still 1 here. + + // Note: This should be done inside `.cxx_destruct`, since if a superclass + // calls an overwritten method in its `dealloc`, it can access + // deinitialized instance variables; but we can't do that without + // generating statics, so we have to do it in `dealloc` for now. + // + // Another possibility would be to read the contents of the ivars onto the + // stack here, and only deinitialize after the superclass' `dealloc`, but + // that would break the pinning guarantee that ivars otherwise have. + if mem::needs_drop::() { + match drop_flag { + // Do nothing if the ivars have not been initialized. + DropFlag::Allocated => cold_path(), + DropFlag::InitializedIvars | DropFlag::Finalized => { + // SAFETY: The instance variable is initialized, so it is + // valid to drop here. + // + // TODO: This can unwind, is it correct to just let that + // propagate? + unsafe { ptr::drop_in_place(ptr_to_ivar(this).as_ptr()) }; + } + } + } + + // The superclass' "marker" that this stores is wrapped in `ManuallyDrop`, + // we drop it by calling the superclass' `dealloc` method instead. + // + // Note: ARC does this automatically, which means most Objective-C code in + // the wild don't contain this call; but we _are_ ARC, so we must do this. + // + // SAFETY: The argument and return types are correct, and we make sure to + // only call this once. + unsafe { + MessageReceiver::send_super_message( + this, + ::Super::class(), + cmd, // Reuse the selector + (), // No arguments + ) + } +} + +/// Register the class, and get the ivar offsets. +#[inline] +pub(crate) fn register_with_ivars( + mut builder: ClassBuilder, +) -> (&'static AnyClass, isize, isize) { + let (ivar_name, drop_flag_name): (Cow<'static, str>, Cow<'static, str>) = { + if cfg!(feature = "gnustep-1-7") { + // GNUStep does not support a subclass having an ivar with the + // same name as a superclass, so let's use the class name as the + // ivar name to ensure uniqueness. + ( + format!("{}_ivars", T::NAME).into(), + format!("{}_drop_flag", T::NAME).into(), + ) + } else { + ("ivars".into(), "drop_flag".into()) + } + }; + + if T::HAS_IVARS { + // TODO: Consider not adding a encoding - Swift doesn't do it. + let ivar_encoding = Encoding::Array( + mem::size_of::() as u64, + match mem::align_of::() { + 1 => &u8::ENCODING, + 2 => &u16::ENCODING, + 4 => &u32::ENCODING, + // The alignment of `u64` may not be 8 on all architectures + 8 if mem::align_of::() == 8 => &u64::ENCODING, + alignment => panic!("unsupported alignment {alignment} for `{}::Ivars`", T::NAME), + }, + ); + unsafe { builder.add_ivar_inner::(&ivar_name, &ivar_encoding) }; + } + + if T::HAS_DROP_FLAG { + // TODO: Maybe we can reuse the drop flag when subclassing an already + // declared class? + builder.add_ivar::(&drop_flag_name); + } + + let cls = builder.register(); + + let ivars_offset = if T::HAS_IVARS { + cls.instance_variable(&ivar_name) + .expect("failed retrieving instance variable on newly declared class") + .offset() + } else { + // Fallback to an offset of zero. + // + // This is fine, since any reads here will only be via. zero-sized + // ivars, where the actual pointer doesn't matter. + 0 + }; + + let drop_flag_offset = if T::HAS_DROP_FLAG { + cls.instance_variable(&drop_flag_name) + .expect("failed retrieving drop flag instance variable on newly declared class") + .offset() + } else { + // Fall back to an offset of zero. + // + // This is fine, since the drop flag is never actually used in the + // cases where it was not added. + 0 + }; + + (cls, ivars_offset, drop_flag_offset) +} + +/// # Safety +/// +/// The pointer must be a valid, newly allocated instance. +#[inline] +pub(crate) unsafe fn initialize_ivars(ptr: NonNull, val: T::Ivars) { + // Debug assert the state of the drop flag + if T::HAS_DROP_FLAG && cfg!(debug_assertions) { + // SAFETY: Just checked that the drop flag is available. + match unsafe { *ptr_to_drop_flag(ptr) } { + DropFlag::Allocated => { + // Allow initialization after allocation + } + DropFlag::InitializedIvars => { + panic!("tried to initialize ivars after they were already initialized") + } + DropFlag::Finalized => { + panic!("tried to initialize ivars on an already initialized object") + } + } + } + + // SAFETY: + // - Caller ensures the pointer is valid. + // - The location is properly aligned by `ClassBuilder::add_ivar`. + // - This write is done as part of initialization, so we know that the + // pointer is not shared elsewhere. + unsafe { ptr_to_ivar(ptr).as_ptr().write(val) }; + + // Write to drop flag that we've initialized the instance variables. + // + // Note: We intentionally only do this _after_ writing to the ivars, + // for better unwind safety. + if T::HAS_DROP_FLAG && (mem::needs_drop::() || cfg!(debug_assertions)) { + // SAFETY: Just checked that the drop flag is available. + unsafe { ptr_to_drop_flag(ptr).write(DropFlag::InitializedIvars) } + } +} + +/// # Safety +/// +/// The pointer must be valid and finalized (i.e. all super initializers must +/// have been run). +#[inline] +pub(crate) unsafe fn set_finalized(ptr: NonNull) { + // Debug assert the state of the drop flag + if T::HAS_DROP_FLAG && cfg!(debug_assertions) { + // SAFETY: Just checked that the drop flag is available. + match unsafe { *ptr_to_drop_flag(ptr) } { + DropFlag::Allocated => { + panic!("tried to finalize an object that was not yet fully initialized") + } + DropFlag::InitializedIvars => { + // Allow finalizing after initialization + } + DropFlag::Finalized => { + panic!("tried to finalize an already finalized object") + } + } + } + + // Write to drop flag that we've fully initialized the class. + if T::HAS_DROP_FLAG && (mem::needs_drop::() || cfg!(debug_assertions)) { + // SAFETY: Just checked that the drop flag is available. + unsafe { ptr_to_drop_flag(ptr).write(DropFlag::Finalized) } + } +} + +/// # Safety +/// +/// The pointer must be valid and the instance variables must be initialized. +#[inline] +pub(crate) unsafe fn get_initialized_ivar_ptr( + ptr: NonNull, +) -> NonNull { + // Debug assert the state of the drop flag + if T::HAS_DROP_FLAG && cfg!(debug_assertions) { + // SAFETY: Just checked that the drop flag is available. + match unsafe { *ptr_to_drop_flag(ptr) } { + DropFlag::Allocated => { + panic!("tried to access uninitialized instance variable") + } + DropFlag::InitializedIvars => { + // Allow accessing even if not finalized, since we only set + // that state _after_ it actually happens, while accesses may + // be done by the superclass initializer in e.g. an + // overwritten method. + } + DropFlag::Finalized => { + // Allow accessing if finalized + } + } + } + + // SAFETY: That the pointer is valid is ensured by caller. + unsafe { ptr_to_ivar(ptr) } +} + +#[cfg(test)] +mod tests { + use core::sync::atomic::{AtomicBool, Ordering}; + + use alloc::boxed::Box; + + use super::*; + use crate::mutability::Mutable; + use crate::rc::{Allocated, Id, __RcTestObject, __ThreadTestData}; + use crate::runtime::NSObject; + use crate::{declare_class, msg_send, msg_send_id, ClassType, DeclaredClass}; + + #[test] + fn assert_size() { + assert_eq!(mem::size_of::(), 1); + } + + #[test] + fn ensure_custom_drop_is_possible() { + static HAS_RUN_DEALLOC: AtomicBool = AtomicBool::new(false); + + #[derive(Default, Debug, PartialEq, Eq)] + struct Ivars { + ivar: u8, + ivar_bool: bool, + } + + declare_class!( + struct CustomDrop; + + unsafe impl ClassType for CustomDrop { + type Super = NSObject; + type Mutability = Mutable; + const NAME: &'static str = "CustomDrop"; + } + + impl DeclaredClass for CustomDrop { + type Ivars = Ivars; + } + + unsafe impl CustomDrop { + #[method_id(init)] + fn init(this: Allocated) -> Id { + let this = this.set_ivars(Ivars::default()); + unsafe { msg_send_id![super(this), init] } + } + } + ); + + impl Drop for CustomDrop { + fn drop(&mut self) { + HAS_RUN_DEALLOC.store(true, Ordering::Relaxed); + } + } + + let _: Id = unsafe { msg_send_id![CustomDrop::class(), new] }; + + assert!(HAS_RUN_DEALLOC.load(Ordering::Relaxed)); + } + + #[derive(Debug, PartialEq, Eq)] + struct IvarTesterIvars { + ivar1: Id<__RcTestObject>, + ivar2: Option>, + ivar3: Box>, + ivar4: Option>>, + } + + declare_class!( + #[derive(Debug, PartialEq, Eq)] + struct IvarTester; + + unsafe impl ClassType for IvarTester { + type Super = NSObject; + type Mutability = Mutable; + const NAME: &'static str = "IvarTester"; + } + + impl DeclaredClass for IvarTester { + type Ivars = IvarTesterIvars; + } + + unsafe impl IvarTester { + #[method_id(init)] + fn init(this: Allocated) -> Option> { + let this = this.set_ivars(IvarTesterIvars { + ivar1: __RcTestObject::new(), + ivar2: Some(__RcTestObject::new()), + ivar3: Box::new(__RcTestObject::new()), + ivar4: Some(Box::new(__RcTestObject::new())), + }); + unsafe { msg_send_id![super(this), init] } + } + + #[method(initInvalid)] + fn init_invalid(this: *mut Self) -> *mut Self { + // Don't actually initialize anything here; this creates an + // invalid instance, where accessing the two ivars `ivar1` + // and `ivar3` is UB + unsafe { msg_send![super(this), init] } + } + } + ); + + #[derive(Debug, PartialEq, Eq)] + struct IvarTesterSubclassIvars { + ivar5: Id<__RcTestObject>, + } + + declare_class!( + #[derive(Debug, PartialEq, Eq)] + struct IvarTesterSubclass; + + unsafe impl ClassType for IvarTesterSubclass { + type Super = IvarTester; + type Mutability = Mutable; + const NAME: &'static str = "IvarTesterSubclass"; + } + + impl DeclaredClass for IvarTesterSubclass { + type Ivars = IvarTesterSubclassIvars; + } + + unsafe impl IvarTesterSubclass { + #[method_id(init)] + fn init(this: Allocated) -> Option> { + let this = this.set_ivars(IvarTesterSubclassIvars { + ivar5: __RcTestObject::new(), + }); + unsafe { msg_send_id![super(this), init] } + } + } + ); + + #[test] + fn test_alloc_dealloc() { + let expected = __ThreadTestData::current(); + + let obj: Allocated = unsafe { msg_send_id![IvarTester::class(), alloc] }; + expected.assert_current(); + + drop(obj); + expected.assert_current(); + } + + #[test] + fn test_init_drop() { + let mut expected = __ThreadTestData::current(); + + let mut obj: Id = unsafe { msg_send_id![IvarTester::class(), new] }; + expected.alloc += 4; + expected.init += 4; + expected.assert_current(); + + obj.ivars_mut().ivar1 = obj.ivars().ivar1.clone(); + expected.retain += 1; + expected.release += 1; + expected.assert_current(); + + obj.ivars_mut().ivar2 = None; + expected.release += 1; + expected.drop += 1; + expected.assert_current(); + + drop(obj); + expected.release += 3; + expected.drop += 3; + expected.assert_current(); + } + + #[test] + fn test_subclass() { + let mut expected = __ThreadTestData::current(); + + let mut obj: Id = + unsafe { msg_send_id![IvarTesterSubclass::class(), new] }; + expected.alloc += 5; + expected.init += 5; + expected.assert_current(); + + obj.ivars_mut().ivar5 = (&**obj).ivars().ivar1.clone(); + expected.retain += 1; + expected.release += 1; + expected.drop += 1; + expected.assert_current(); + + drop(obj); + expected.release += 5; + expected.drop += 4; + expected.assert_current(); + } + + #[test] + #[cfg_attr(not(debug_assertions), ignore = "only panics with debug assertions")] + #[should_panic = "internal error: entered unreachable code: tried to access uninitialized instance variable"] + fn test_init_invalid_ref() { + let obj: Id = unsafe { msg_send_id![IvarTester::alloc(), initInvalid] }; + + std::println!("{:?}", obj.ivars().ivar1); + } + + #[test] + #[cfg_attr(not(debug_assertions), ignore = "only panics with debug assertions")] + #[should_panic = "internal error: entered unreachable code: tried to access uninitialized instance variable"] + fn test_init_invalid_mut() { + let mut obj: Id = unsafe { msg_send_id![IvarTester::alloc(), initInvalid] }; + + obj.ivars_mut().ivar1 = __RcTestObject::new(); + } +} diff --git a/crates/objc2/src/__macro_helpers/mod.rs b/crates/objc2/src/__macro_helpers/mod.rs index b7cffdbf7..3df738817 100644 --- a/crates/objc2/src/__macro_helpers/mod.rs +++ b/crates/objc2/src/__macro_helpers/mod.rs @@ -2,11 +2,10 @@ pub use core::borrow::{Borrow, BorrowMut}; pub use core::cell::UnsafeCell; pub use core::convert::{AsMut, AsRef}; pub use core::marker::{PhantomData, Sized}; -pub use core::mem::{needs_drop, size_of, ManuallyDrop}; +pub use core::mem::{size_of, ManuallyDrop, MaybeUninit}; pub use core::ops::{Deref, DerefMut}; pub use core::option::Option::{self, None, Some}; -pub use core::primitive::{bool, str, u8}; -pub use core::ptr::drop_in_place; +pub use core::primitive::{bool, isize, str, u8}; pub use core::{compile_error, concat, panic, stringify}; // TODO: Use `core::cell::LazyCell` pub use std::sync::Once; @@ -15,6 +14,7 @@ mod cache; mod common_selectors; mod convert; mod declare_class; +pub(crate) mod declared_ivars; mod method_family; mod msg_send; mod msg_send_id; @@ -28,11 +28,12 @@ pub use self::declare_class::{ ClassProtocolMethodsBuilder, IdReturnValue, MaybeOptionId, MessageRecieveId, ValidSubclassMutability, }; +pub use self::declared_ivars::DeclaredIvarsHelper; pub use self::method_family::{ retain_semantics, Alloc, CopyOrMutCopy, Init, New, Other, RetainSemantics, }; pub use self::msg_send::MsgSend; -pub use self::msg_send_id::{MaybeUnwrap, MsgSendId}; +pub use self::msg_send_id::{MaybeUnwrap, MsgSendId, MsgSendSuperId}; /// Helper struct for emitting the module info that macOS 32-bit requires. /// diff --git a/crates/objc2/src/__macro_helpers/msg_send_id.rs b/crates/objc2/src/__macro_helpers/msg_send_id.rs index 9cb02d96b..b63efe3f3 100644 --- a/crates/objc2/src/__macro_helpers/msg_send_id.rs +++ b/crates/objc2/src/__macro_helpers/msg_send_id.rs @@ -1,10 +1,11 @@ -use core::ptr; +use core::ptr::{self, NonNull}; -use crate::encode::Encode; -use crate::rc::{Allocated, Id}; +use crate::encode::{Encode, RefEncode}; +use crate::rc::{Allocated, Id, PartialInit}; use crate::runtime::{AnyClass, AnyObject, Sel}; -use crate::{sel, Message}; +use crate::{sel, ClassType, DeclaredClass, Message}; +use super::declared_ivars::set_finalized; use super::{Alloc, ConvertArguments, CopyOrMutCopy, Init, MsgSend, New, Other, TupleExtender}; pub trait MsgSendId { @@ -57,6 +58,94 @@ pub trait MsgSendId { } } +/// new: T -> Option> +/// alloc: &AnyClass -> Allocated +/// init: PartialInit -> Option> // Changed +/// copy/mutableCopy: T -> Option> +/// others: T -> Option> +#[doc(hidden)] +pub trait MsgSendSuperId { + type Inner: ?Sized + RefEncode; + + unsafe fn send_super_message_id>( + obj: T, + superclass: &AnyClass, + sel: Sel, + args: A, + ) -> R; + + #[inline] + #[track_caller] + unsafe fn send_super_message_id_static>( + obj: T, + sel: Sel, + args: A, + ) -> R + where + Self::Inner: ClassType, + ::Super: ClassType, + { + unsafe { + Self::send_super_message_id(obj, ::Super::class(), sel, args) + } + } + + #[inline] + #[track_caller] + unsafe fn send_super_message_id_error( + obj: T, + superclass: &AnyClass, + sel: Sel, + args: A, + ) -> Result> + where + *mut *mut E: Encode, + A: TupleExtender<*mut *mut E>, + >::PlusOneArgument: ConvertArguments, + E: Message, + Option: MaybeUnwrap, + { + let mut err: *mut E = ptr::null_mut(); + let args = args.add_argument(&mut err); + // SAFETY: See `send_message_id_error` + let res: Option = unsafe { Self::send_super_message_id(obj, superclass, sel, args) }; + if let Some(res) = res { + Ok(res) + } else { + // SAFETY: See `send_message_id_error` + Err(unsafe { encountered_error(err) }) + } + } + + #[inline] + #[track_caller] + unsafe fn send_super_message_id_static_error( + obj: T, + sel: Sel, + args: A, + ) -> Result> + where + Self::Inner: ClassType, + ::Super: ClassType, + *mut *mut E: Encode, + A: TupleExtender<*mut *mut E>, + >::PlusOneArgument: ConvertArguments, + E: Message, + Option: MaybeUnwrap, + { + let mut err: *mut E = ptr::null_mut(); + let args = args.add_argument(&mut err); + // SAFETY: See `send_message_id_error` + let res: Option = unsafe { Self::send_super_message_id_static(obj, sel, args) }; + if let Some(res) = res { + Ok(res) + } else { + // SAFETY: See `send_message_id_error` + Err(unsafe { encountered_error(err) }) + } + } +} + // Marked `cold` to tell the optimizer that errors are comparatively rare. // And intentionally not inlined, for much the same reason. #[cold] @@ -85,6 +174,26 @@ impl MsgSendId>> for New { } } +impl MsgSendSuperId>> for New { + type Inner = T::Inner; + + #[inline] + unsafe fn send_super_message_id>>>( + obj: T, + superclass: &AnyClass, + sel: Sel, + args: A, + ) -> R { + let ptr = obj.into_raw_receiver(); + // SAFETY: Same as in `send_message_id` + let obj = unsafe { MsgSend::send_super_message(ptr, superclass, sel, args) }; + // SAFETY: Same as in `send_message_id` + let obj = unsafe { Id::new(obj) }; + // SAFETY: Same as in `send_message_id` + R::maybe_unwrap::(obj, (unsafe { ptr.as_ref() }, sel)) + } +} + impl MsgSendId<&'_ AnyClass, Allocated> for Alloc { #[inline] unsafe fn send_message_id>>( @@ -100,6 +209,24 @@ impl MsgSendId<&'_ AnyClass, Allocated> for Alloc { } } +impl MsgSendSuperId<&'_ AnyClass, Allocated> for Alloc { + type Inner = AnyClass; + + #[inline] + unsafe fn send_super_message_id>>( + cls: &AnyClass, + superclass: &AnyClass, + sel: Sel, + args: A, + ) -> R { + // SAFETY: Same as in `send_message_id` + let obj = unsafe { MsgSend::send_super_message(cls, superclass, sel, args) }; + // SAFETY: Same as in `send_message_id` + let obj = unsafe { Allocated::new(obj) }; + R::maybe_unwrap::(obj, ()) + } +} + impl Alloc { /// Fast path optimization for `msg_send_id![cls, alloc]`. #[inline] @@ -144,6 +271,32 @@ impl MsgSendId, Option>> for Init { } } +impl MsgSendSuperId, Option>> for Init { + type Inner = T; + + #[inline] + unsafe fn send_super_message_id>>>( + obj: PartialInit, + superclass: &AnyClass, + sel: Sel, + args: A, + ) -> R { + let ptr = PartialInit::into_ptr(obj); + // SAFETY: Same as `send_message_id`. + let ptr = unsafe { MsgSend::send_super_message(ptr, superclass, sel, args) }; + // SAFETY: The returned pointer is the same as the one we passed in. + // + // TODO: If this is not the case, a lot will have gone wrong anyhow, + // so unsure if we can do anything better than just ignore the issue? + if let Some(ptr) = NonNull::new(ptr) { + unsafe { set_finalized(ptr) } + } + // SAFETY: Same as `send_message_id` + let obj = unsafe { Id::new(ptr) }; + R::maybe_unwrap::(obj, (ptr.cast(), sel)) + } +} + impl MsgSendId>> for CopyOrMutCopy { #[inline] unsafe fn send_message_id>>>( @@ -160,6 +313,24 @@ impl MsgSendId>> for CopyOrMutC } } +impl MsgSendSuperId>> for CopyOrMutCopy { + type Inner = T::Inner; + + #[inline] + unsafe fn send_super_message_id>>>( + obj: T, + superclass: &AnyClass, + sel: Sel, + args: A, + ) -> R { + // SAFETY: Same as in `send_message_id` + let obj = unsafe { MsgSend::send_super_message(obj, superclass, sel, args) }; + // SAFETY: Same as in `send_message_id` + let obj = unsafe { Id::new(obj) }; + R::maybe_unwrap::(obj, ()) + } +} + impl MsgSendId>> for Other { #[inline] unsafe fn send_message_id>>>( @@ -183,6 +354,26 @@ impl MsgSendId>> for Other { } } +impl MsgSendSuperId>> for Other { + type Inner = T::Inner; + + #[inline] + unsafe fn send_super_message_id>>>( + obj: T, + superclass: &AnyClass, + sel: Sel, + args: A, + ) -> R { + let ptr = obj.into_raw_receiver(); + // SAFETY: Same as `send_message_id` + let obj = unsafe { MsgSend::send_super_message(ptr, superclass, sel, args) }; + // SAFETY: Same as `send_message_id` + let obj = unsafe { Id::retain_autoreleased(obj) }; + // SAFETY: Same as `send_message_id` + R::maybe_unwrap::(obj, (unsafe { ptr.as_ref() }, sel)) + } +} + pub trait MaybeUnwrap { type Input; #[track_caller] @@ -372,7 +563,8 @@ mod tests { drop(obj); expected.release += 1; - expected.dealloc += 1; + // Drop flag ensures uninitialized do not Drop + // expected.drop += 1; expected.assert_current(); } @@ -450,7 +642,7 @@ mod tests { expected.assert_current(); }); expected.release += 5; - expected.dealloc += 4; + expected.drop += 4; expected.assert_current(); } diff --git a/crates/objc2/src/__macro_helpers/writeback.rs b/crates/objc2/src/__macro_helpers/writeback.rs index 3b4e78f77..aaf2a3fd7 100644 --- a/crates/objc2/src/__macro_helpers/writeback.rs +++ b/crates/objc2/src/__macro_helpers/writeback.rs @@ -258,7 +258,7 @@ mod tests { if error.is_some() { expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; } drop(error); expected.assert_current(); @@ -279,7 +279,7 @@ mod tests { expected.init += 1; expected.retain += 1; expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; helper(&mut expected, true, Some(__RcTestObject::new())); } @@ -328,19 +328,19 @@ mod tests { drop(obj); expected.release += 1; if AUTORELEASE_SKIPPED { - expected.dealloc += 1; + expected.drop += 1; } expected.assert_current(); }); if !AUTORELEASE_SKIPPED { expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; } expected.assert_current(); drop(err); expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; expected.assert_current(); } } diff --git a/crates/objc2/src/declare/ivar.rs b/crates/objc2/src/declare/ivar.rs deleted file mode 100644 index d89b32a6e..000000000 --- a/crates/objc2/src/declare/ivar.rs +++ /dev/null @@ -1,388 +0,0 @@ -use core::fmt; -use core::marker::PhantomData; -use core::mem::{self, MaybeUninit}; -use core::ops::{Deref, DerefMut}; -use core::ptr::{self, NonNull}; - -use crate::encode::Encode; -use crate::runtime::AnyObject; - -pub(crate) mod private { - pub trait Sealed {} -} - -/// Types that may be used in ivars. -/// -/// This may be either: -/// - [`IvarBool`][super::IvarBool]. -/// - [`IvarDrop`][super::IvarDrop]. -/// - [`IvarEncode`][super::IvarEncode]. -/// -/// This is a sealed trait, and should not need to be implemented. Open an -/// issue if you know a use-case where this restrition should be lifted! -/// -/// -/// # Safety -/// -/// You cannot rely on any safety guarantees from this. -// -// The type must have the same memory layout as the output type. -// -// Additionally, the type must be safe to drop even if zero-initialized. -// -// Ivars are documented to be zero-initialized in [this section of the -// Objective-C manual][obj-dynamically-created], and that has been true since -// at least [the Objective-C version shipped with Mac OS X 10.0][objc4-208]. -// -// [obj-dynamically-created]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html#//apple_ref/doc/uid/TP40011210-CH4-SW7 -// [objc4-208]: https://github.com/apple-oss-distributions/objc4/blob/objc4-208/runtime/objc-class.m#L367 -pub unsafe trait InnerIvarType: private::Sealed + Encode { - /// The type that an `Ivar` containing this will dereference to. - /// - /// E.g. `Ivar>>` will deref to `Box`. - type Output; - - /// # Safety - /// - /// The instance variable must have been initialized. - #[doc(hidden)] - unsafe fn __deref(&self) -> &Self::Output; - - /// # Safety - /// - /// The instance variable must have been initialized. - #[doc(hidden)] - unsafe fn __deref_mut(&mut self) -> &mut Self::Output; -} - -/// Helper trait for defining instance variables. -/// -/// This should be implemented for an empty marker type, which can then be -/// used within [`Ivar`] to refer to the instance variable. -/// -/// -/// # Safety -/// -/// Really, [`Ivar`] should be marked as `unsafe`, but since we can't do that -/// we'll mark this trait as `unsafe` instead. See [`Ivar`] for safety -/// requirements. -/// -/// -/// # Examples -/// -/// Create an instance variable `myCustomIvar` with type `i32`. -/// -/// ``` -/// use objc2::declare::{IvarEncode, IvarType}; -/// -/// // Helper type -/// struct MyCustomIvar; -/// -/// unsafe impl IvarType for MyCustomIvar { -/// type Type = IvarEncode; -/// const NAME: &'static str = "myCustomIvar"; -/// } -/// -/// // `Ivar` can now be used -/// ``` -pub unsafe trait IvarType { - /// The type of the instance variable. - type Type: InnerIvarType; - /// The name of the instance variable. - const NAME: &'static str; - - #[doc(hidden)] - unsafe fn __ivar_ptr(ptr: NonNull) -> NonNull { - // FIXME: This is currently unsound! Looking up the instance variable - // dynamically will return the wrong variable if two variables with - // the same name exist. - let ivar = unsafe { ptr.as_ref() }.lookup_instance_variable_dynamically(Self::NAME); - ivar.debug_assert_encoding(&Self::Type::ENCODING); - unsafe { AnyObject::ivar_at_offset(ptr, ivar.offset()) } - } -} - -/// A wrapper type over a custom instance variable. -/// -/// This type is not meant to be constructed by itself, it must reside within -/// another struct meant to represent an Objective-C object. -/// -/// On [`Deref`] it then uses the [`IvarType::NAME`] string to access the ivar -/// of the containing object. -/// -/// Note that this is not ([currently][zst-hack]) allowed by [stacked -/// borrows][sb], but due to objects being zero-sized types, we don't have -/// provenance over the ivars anyhow, this should be just as sound as normal -/// instance variable access. -/// -/// [sb]: https://github.com/rust-lang/unsafe-code-guidelines/blob/e21202c60c7be03dd2ab016ada92fb5305d40438/wip/stacked-borrows.md -/// [zst-hack]: https://github.com/rust-lang/unsafe-code-guidelines/issues/305 -/// -/// -/// # `bool` handling -/// -/// This does _not_ perform a conversion step between [`bool`] and the -/// Objective-C `BOOL`; use [`runtime::Bool`][crate::runtime::Bool] when you -/// want your instance variable to be accessible from other Objective-C code. -/// -/// -/// # Safety -/// -/// This must be used within a type that act as an Objective-C object. In -/// particular, this is never safe to have on the stack by itself. -/// -/// Additionally, the instance variable described by `T` must be available on -/// the specific instance, and be of the exact same type. When declaring the -/// object yourself, you can ensure this using -/// [`ClassBuilder::add_static_ivar`]. -/// -/// Finally, two ivars with the same name must not be used on the same object. -/// -/// [`ClassBuilder::add_static_ivar`]: crate::declare::ClassBuilder::add_static_ivar -/// -/// -/// # Examples -/// -/// ``` -/// use objc2::declare::{Ivar, IvarEncode, IvarType}; -/// use objc2::runtime::NSObject; -/// -/// // Declare ivar with given type and name -/// struct MyCustomIvar; -/// unsafe impl IvarType for MyCustomIvar { -/// type Type = IvarEncode; -/// const NAME: &'static str = "myCustomIvar"; -/// } -/// -/// // Custom object -/// #[repr(C)] -/// pub struct MyObject { -/// inner: NSObject, -/// // SAFETY: The instance variable is used within an object, and it is -/// // properly declared below. -/// my_ivar: Ivar, -/// } -/// -/// # use objc2::ClassType; -/// # use objc2::declare::ClassBuilder; -/// # let mut builder = ClassBuilder::new("MyObject", NSObject::class()).unwrap(); -/// // Declare the class and add the instance variable to it -/// builder.add_static_ivar::(); -/// # let _cls = builder.register(); -/// -/// let obj: MyObject; -/// // You can now access `obj.my_ivar` -/// ``` -/// -/// See also the `declare_ivar.rs` example. -#[repr(C)] -// Must not be `Copy` nor `Clone`! -pub struct Ivar { - /// Make this type allowed in `repr(C)` - inner: [u8; 0], - /// For proper variance and auto traits - item: PhantomData, -} - -impl Drop for Ivar { - #[inline] - fn drop(&mut self) { - if mem::needs_drop::() { - unsafe { ptr::drop_in_place(self.as_inner_mut_ptr().as_ptr()) } - } - } -} - -impl Ivar { - /// Get a pointer to the instance variable. - /// - /// Note that if the ivar has already been initialized, you can use the - /// `Deref` implementation to get a reference. - /// - /// This is similar to [`MaybeUninit::as_ptr`], see that for usage - /// instructions. - pub fn as_ptr(this: &Self) -> *const ::Target { - this.as_inner_ptr().as_ptr().cast() - } - - fn as_inner_ptr(&self) -> NonNull { - let ptr: NonNull = NonNull::from(self).cast(); - - // SAFETY: The user ensures that this is placed in a struct that can - // be reinterpreted as an `AnyObject`. Since `Ivar` can never be - // constructed by itself (and is neither Copy nor Clone), we know that - // it is guaranteed to _stay_ in said struct. - // - // Even if the user were to do `mem::swap`, the `Ivar` has a unique - // type (and does not hold any data), so that wouldn't break anything. - // - // Note: We technically don't have provenance over the object, nor the - // ivar, but the object doesn't have provenance over the ivar either, - // so that is fine. - unsafe { T::__ivar_ptr(ptr) } - } - - /// Get a mutable pointer to the instance variable. - /// - /// This is useful when you want to initialize the ivar inside an `init` - /// method (where it may otherwise not have been safely initialized yet). - /// - /// Note that if the ivar has already been initialized, you can use the - /// `DerefMut` implementation to get a mutable reference. - /// - /// This is similar to [`MaybeUninit::as_mut_ptr`], see that for usage - /// instructions. - pub fn as_mut_ptr(this: &mut Self) -> *mut ::Target { - this.as_inner_mut_ptr().as_ptr().cast() - } - - fn as_inner_mut_ptr(&mut self) -> NonNull { - let ptr: NonNull = NonNull::from(self).cast(); - - // SAFETY: Same as `as_inner_ptr` - unsafe { T::__ivar_ptr(ptr) } - } - - /// Sets the value of the instance variable. - /// - /// This is useful when you want to initialize the ivar inside an `init` - /// method (where it may otherwise not have been safely initialized yet). - /// - /// This is similar to [`MaybeUninit::write`], see that for usage - /// instructions. - pub fn write(this: &mut Self, val: ::Target) -> &mut ::Target { - let ptr: *mut ::Output = Self::as_mut_ptr(this); - let ptr: *mut MaybeUninit<::Output> = ptr.cast(); - let ivar = unsafe { ptr.as_mut().unwrap_unchecked() }; - ivar.write(val) - } -} - -impl Deref for Ivar { - type Target = ::Output; - - #[inline] - fn deref(&self) -> &Self::Target { - // SAFETY: User ensures that the `Ivar` is only used when the ivar - // exists, has the correct type, and has been properly initialized. - // - // Since all accesses to a particular ivar only goes through one - // `Ivar`, if we have `&Ivar` we know that `&T` is safe. - unsafe { self.as_inner_ptr().as_ref().__deref() } - } -} - -impl DerefMut for Ivar { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - // SAFETY: User ensures that the `Ivar` is only used when the ivar - // exists, has the correct type, and has been properly initialized. - // - // Safe as mutable because there is only one access to a - // particular ivar at a time (since we have `&mut self`). - - // Note: We're careful not to create `&mut AnyObject` because the user - // might have two mutable references to different ivars, as such: - // - // ``` - // #[repr(C)] - // struct X { - // inner: AnyObject, - // ivar1: Ivar, - // ivar2: Ivar, - // } - // - // let mut x: X; - // let ivar1: &mut Ivar = &mut x.ivar1; - // let ivar2: &mut Ivar = &mut x.ivar2; - // ``` - // - // And using `mut` would create aliasing mutable reference to the - // object. - unsafe { self.as_inner_mut_ptr().as_mut().__deref_mut() } - } -} - -/// Format as a pointer to the instance variable. -impl fmt::Pointer for Ivar { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&Self::as_ptr(self), f) - } -} - -#[cfg(test)] -mod tests { - use core::mem; - use core::panic::{RefUnwindSafe, UnwindSafe}; - use std::sync::atomic::{AtomicBool, Ordering}; - - use super::*; - use crate::declare::{IvarBool, IvarEncode}; - use crate::mutability::Mutable; - use crate::rc::Id; - use crate::runtime::NSObject; - use crate::{declare_class, msg_send, msg_send_id, test_utils, ClassType}; - - struct TestIvar; - - unsafe impl IvarType for TestIvar { - type Type = IvarEncode; - const NAME: &'static str = "_foo"; - } - - #[repr(C)] - struct IvarTestObject { - inner: NSObject, - foo: Ivar, - } - - #[test] - fn auto_traits() { - fn assert_auto_traits() {} - assert_auto_traits::>(); - - // Ensure that `Ivar` is zero-sized - assert_eq!(mem::size_of::>(), 0); - assert_eq!(mem::align_of::>(), 1); - } - - #[test] - fn access_ivar() { - let mut obj = test_utils::custom_object(); - let _: () = unsafe { msg_send![&mut obj, setFoo: 42u32] }; - - let obj = unsafe { Id::as_ptr(&obj).cast::().as_ref().unwrap() }; - assert_eq!(*obj.foo, 42); - } - - #[test] - fn ensure_custom_drop_is_possible() { - static HAS_RUN_DEALLOC: AtomicBool = AtomicBool::new(false); - - declare_class!( - #[derive(Debug, PartialEq, Eq)] - struct CustomDrop { - ivar: IvarEncode, - ivar_bool: IvarBool<"_ivar_bool">, - } - - mod customdrop; - - unsafe impl ClassType for CustomDrop { - type Super = NSObject; - type Mutability = Mutable; - const NAME: &'static str = "CustomDrop"; - } - ); - - impl Drop for CustomDrop { - fn drop(&mut self) { - HAS_RUN_DEALLOC.store(true, Ordering::Relaxed); - } - } - - let _: Id = unsafe { msg_send_id![CustomDrop::class(), new] }; - - assert!(HAS_RUN_DEALLOC.load(Ordering::Relaxed)); - } -} diff --git a/crates/objc2/src/declare/ivar_bool.rs b/crates/objc2/src/declare/ivar_bool.rs deleted file mode 100644 index f7e1930a5..000000000 --- a/crates/objc2/src/declare/ivar_bool.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::encode::{Encode, Encoding}; - -use super::InnerIvarType; - -/// Ivar of [`bool`]. -/// -/// This is used to work around the fact that `bool` is not [`Encode`]. -/// -/// If you want to access this instance variable to Objective-C, you must do -/// so using C99 `_Bool`; if you want to use `BOOL` in Objective-C, you should -/// use `IvarEncode`. -#[repr(transparent)] -#[allow(missing_copy_implementations)] -#[allow(missing_debug_implementations)] -pub struct IvarBool(bool); - -unsafe impl Encode for IvarBool { - const ENCODING: Encoding = Encoding::Bool; -} - -impl super::ivar::private::Sealed for IvarBool {} - -// SAFETY: IvarBool is `#[repr(transparent)]`, and `bool` is safe to -// zero-initialize -unsafe impl InnerIvarType for IvarBool { - type Output = bool; - - #[inline] - unsafe fn __deref(&self) -> &Self::Output { - &self.0 - } - - #[inline] - unsafe fn __deref_mut(&mut self) -> &mut Self::Output { - &mut self.0 - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use core::mem; - - #[test] - fn needs_drop() { - assert!(!mem::needs_drop::()); - assert_eq!(mem::size_of::(), mem::size_of::()); - } -} diff --git a/crates/objc2/src/declare/ivar_drop.rs b/crates/objc2/src/declare/ivar_drop.rs deleted file mode 100644 index 0fa488608..000000000 --- a/crates/objc2/src/declare/ivar_drop.rs +++ /dev/null @@ -1,378 +0,0 @@ -use alloc::boxed::Box; -use core::ffi::c_void; - -use crate::encode::{Encode, Encoding}; -use crate::rc::Id; -use crate::Message; - -use super::InnerIvarType; - -mod private { - /// # Safety - /// - /// The inner type must be safe to zero-initialize. - pub unsafe trait IvarDropHelper { - type Inner; - } -} - -/// Ivar types that may drop. -/// -/// This currently works with the following types: -/// - `Box` -/// - `Option>` -/// - `Id` -/// - `Option>` -/// -/// Further may be added when the standard library guarantee their layout. -#[repr(transparent)] -#[allow(missing_debug_implementations)] -pub struct IvarDrop(::Inner); - -impl super::ivar::private::Sealed for IvarDrop {} - -// Note that we use `*const c_void` and not `*const T` to allow _any_ type, -// not just types that can be encoded by Objective-C -unsafe impl Encode for IvarDrop> { - const ENCODING: Encoding = <*const c_void>::ENCODING; -} - -// SAFETY: `Option>` is safe to zero-initialize -unsafe impl private::IvarDropHelper for Box { - type Inner = Option>; -} - -// SAFETY: The memory layout of `Box` is guaranteed to be a pointer: -// -// -// The user ensures that the Box has been initialized in an `init` method -// before being used. -unsafe impl InnerIvarType for IvarDrop> { - type Output = Box; - - #[inline] - unsafe fn __deref(&self) -> &Self::Output { - match &self.0 { - Some(inner) => inner, - None => unsafe { box_unreachable() }, - } - } - - #[inline] - unsafe fn __deref_mut(&mut self) -> &mut Self::Output { - match &mut self.0 { - Some(inner) => inner, - None => unsafe { box_unreachable() }, - } - } -} - -unsafe impl Encode for IvarDrop>> { - const ENCODING: Encoding = <*const c_void>::ENCODING; -} - -// SAFETY: `Option>` is safe to zero-initialize -unsafe impl private::IvarDropHelper for Option> { - type Inner = Option>; -} - -// SAFETY: `Option>` guarantees the null-pointer optimization, so for -// `T: Sized` the layout is just a pointer: -// -// -// This is valid to initialize as all-zeroes, so the user doesn't have to do -// anything to initialize it. -unsafe impl InnerIvarType for IvarDrop>> { - type Output = Option>; - - #[inline] - unsafe fn __deref(&self) -> &Self::Output { - &self.0 - } - - #[inline] - unsafe fn __deref_mut(&mut self) -> &mut Self::Output { - &mut self.0 - } -} - -unsafe impl Encode for IvarDrop> { - const ENCODING: Encoding = <*const T>::ENCODING; -} - -// SAFETY: `Option>` is safe to zero-initialize -unsafe impl private::IvarDropHelper for Id { - type Inner = Option>; -} - -// SAFETY: `Id` is `NonNull`, and hence safe to store as a pointer. -// -// The user ensures that the Id has been initialized in an `init` method -// before being used. -// -// Note: We could technically do `impl InnerIvarType for Ivar>` -// directly today, but since we can't do so for `Box` (because that is -// `#[fundamental]`), I think it makes sense to handle them similarly. -unsafe impl InnerIvarType for IvarDrop> { - type Output = Id; - - #[inline] - unsafe fn __deref(&self) -> &Self::Output { - match &self.0 { - Some(inner) => inner, - None => unsafe { id_unreachable() }, - } - } - - #[inline] - unsafe fn __deref_mut(&mut self) -> &mut Self::Output { - match &mut self.0 { - Some(inner) => inner, - None => unsafe { id_unreachable() }, - } - } -} - -unsafe impl Encode for IvarDrop>> { - const ENCODING: Encoding = <*const T>::ENCODING; -} - -// SAFETY: `Option>` is safe to zero-initialize -unsafe impl private::IvarDropHelper for Option> { - type Inner = Option>; -} - -// SAFETY: `Id` guarantees the null-pointer optimization. -// -// This is valid to initialize as all-zeroes, so the user doesn't have to do -// anything to initialize it. -unsafe impl InnerIvarType for IvarDrop>> { - type Output = Option>; - - #[inline] - unsafe fn __deref(&self) -> &Self::Output { - &self.0 - } - - #[inline] - unsafe fn __deref_mut(&mut self) -> &mut Self::Output { - &mut self.0 - } -} - -// TODO: Allow the following once their layout is guaranteed by `std`: -// - Arc -// - Option> -// - sync::Weak -// - Rc -// - Option> -// - rc::Weak -// - Vec -// - String - -// TODO: Allow `WeakId` once we figure out how to allow it being initialized -// by default. - -#[inline] -#[track_caller] -unsafe fn id_unreachable() -> ! { - #[cfg(debug_assertions)] - { - unreachable!("an Id in instance variables must always be initialized before use!") - } - // SAFETY: Checked by caller - #[cfg(not(debug_assertions))] - unsafe { - core::hint::unreachable_unchecked() - } -} - -#[inline] -#[track_caller] -unsafe fn box_unreachable() -> ! { - #[cfg(debug_assertions)] - { - unreachable!("a Box in instance variables must always be initialized before use!") - } - // SAFETY: Checked by caller - #[cfg(not(debug_assertions))] - unsafe { - core::hint::unreachable_unchecked() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::declare::{Ivar, IvarType}; - use crate::mutability::Mutable; - use crate::rc::{Allocated, __RcTestObject, __ThreadTestData}; - use crate::runtime::NSObject; - use crate::{declare_class, msg_send, msg_send_id, ClassType}; - - struct TestIvar1; - unsafe impl IvarType for TestIvar1 { - type Type = IvarDrop>; - const NAME: &'static str = "_abc"; - } - - struct TestIvar2; - unsafe impl IvarType for TestIvar2 { - type Type = IvarDrop>>; - const NAME: &'static str = "_abc"; - } - - struct TestIvar3; - unsafe impl IvarType for TestIvar3 { - type Type = IvarDrop>; - const NAME: &'static str = "_abc"; - } - - struct TestIvar4; - unsafe impl IvarType for TestIvar4 { - type Type = IvarDrop>>; - const NAME: &'static str = "_abc"; - } - - declare_class!( - #[derive(Debug, PartialEq, Eq)] - struct IvarTester { - ivar1: IvarDrop, "_ivar1">, - ivar2: IvarDrop>, "_ivar2">, - ivar3: IvarDrop>, "_ivar3">, - ivar4: IvarDrop>>, "_ivar4">, - } - - mod ivartester; - - unsafe impl ClassType for IvarTester { - type Super = NSObject; - type Mutability = Mutable; - const NAME: &'static str = "IvarTester"; - } - - unsafe impl IvarTester { - #[method(init)] - fn init(&mut self) -> Option<&mut Self> { - let this: Option<&mut Self> = unsafe { msg_send![super(self), init] }; - this.map(|this| { - Ivar::write(&mut this.ivar1, __RcTestObject::new()); - *this.ivar2 = Some(__RcTestObject::new()); - Ivar::write(&mut this.ivar3, Box::new(__RcTestObject::new())); - *this.ivar4 = Some(Box::new(__RcTestObject::new())); - this - }) - } - - #[method(initInvalid)] - fn init_invalid(&mut self) -> Option<&mut Self> { - // Don't actually initialize anything here; this creates an - // invalid instance, where accessing the two ivars `ivar1` - // and `ivar3` is UB - unsafe { msg_send![super(self), init] } - } - } - ); - - declare_class!( - #[derive(Debug, PartialEq, Eq)] - struct IvarTesterSubclass { - ivar5: IvarDrop, "_ivar5">, - } - - mod ivartestersubclass; - - unsafe impl ClassType for IvarTesterSubclass { - type Super = IvarTester; - type Mutability = Mutable; - const NAME: &'static str = "IvarTesterSubclass"; - } - - unsafe impl IvarTesterSubclass { - #[method(init)] - fn init(&mut self) -> Option<&mut Self> { - let this: Option<&mut Self> = unsafe { msg_send![super(self), init] }; - this.map(|this| { - Ivar::write(&mut this.ivar5, __RcTestObject::new()); - this - }) - } - } - ); - - #[test] - fn test_alloc_dealloc() { - let expected = __ThreadTestData::current(); - - let obj: Allocated = unsafe { msg_send_id![IvarTester::class(), alloc] }; - expected.assert_current(); - - drop(obj); - expected.assert_current(); - } - - #[test] - fn test_init_drop() { - let mut expected = __ThreadTestData::current(); - - let mut obj: Id = unsafe { msg_send_id![IvarTester::class(), new] }; - expected.alloc += 4; - expected.init += 4; - expected.assert_current(); - - *obj.ivar1 = (*obj.ivar1).clone(); - expected.retain += 1; - expected.release += 1; - expected.assert_current(); - - *obj.ivar2 = None; - expected.release += 1; - expected.dealloc += 1; - expected.assert_current(); - - drop(obj); - expected.release += 3; - expected.dealloc += 3; - expected.assert_current(); - } - - #[test] - fn test_subclass() { - let mut expected = __ThreadTestData::current(); - - let mut obj: Id = - unsafe { msg_send_id![IvarTesterSubclass::class(), new] }; - expected.alloc += 5; - expected.init += 5; - expected.assert_current(); - - *obj.ivar5 = (*obj.ivar1).clone(); - expected.retain += 1; - expected.release += 1; - expected.dealloc += 1; - expected.assert_current(); - - drop(obj); - expected.release += 5; - expected.dealloc += 4; - expected.assert_current(); - } - - #[test] - #[cfg_attr(not(debug_assertions), ignore = "only panics in debug mode")] - #[should_panic = "an Id in instance variables must always be initialized before use"] - fn test_init_invalid_ref() { - let obj: Id = unsafe { msg_send_id![IvarTester::alloc(), initInvalid] }; - - std::println!("{:?}", obj.ivar1); - } - - #[test] - #[cfg_attr(not(debug_assertions), ignore = "only panics in debug mode")] - #[should_panic = "an Id in instance variables must always be initialized before use"] - fn test_init_invalid_mut() { - let mut obj: Id = unsafe { msg_send_id![IvarTester::alloc(), initInvalid] }; - - *obj.ivar1 = __RcTestObject::new(); - } -} diff --git a/crates/objc2/src/declare/ivar_encode.rs b/crates/objc2/src/declare/ivar_encode.rs deleted file mode 100644 index 3a4300059..000000000 --- a/crates/objc2/src/declare/ivar_encode.rs +++ /dev/null @@ -1,75 +0,0 @@ -use core::mem::MaybeUninit; - -use crate::encode::{Encode, Encoding}; - -use super::InnerIvarType; - -/// Ivar types that are [`Encode`]. -// -// Note: We put the inner type in a `MaybeUninit`, since we may need to access -// this type before the inner type has been properly initialized. -#[repr(transparent)] -#[allow(missing_debug_implementations)] -pub struct IvarEncode(MaybeUninit); - -// We intentionally don't implement `Drop`, since that may happen before the -// ivar has been initialized. -// -// For example in the case of `NonNull`, it would be zero-initialized, -// which is an invalid state for that to have. - -// SAFETY: `IvarEncode` is `#[repr(transparent)]`, and the layout of -// `MaybeUninit` is the same as `T`. -unsafe impl Encode for IvarEncode { - const ENCODING: Encoding = T::ENCODING; -} - -impl super::ivar::private::Sealed for IvarEncode {} - -// SAFETY: `IvarEncode` has the same memory layout as T, and -// `MaybeUninit` is safe to zero-initialize. -unsafe impl InnerIvarType for IvarEncode { - type Output = T; - - #[inline] - unsafe fn __deref(&self) -> &Self::Output { - // SAFETY: Checked by caller - unsafe { self.0.assume_init_ref() } - } - - #[inline] - unsafe fn __deref_mut(&mut self) -> &mut Self::Output { - // SAFETY: Checked by caller - unsafe { self.0.assume_init_mut() } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use core::mem; - - #[test] - fn needs_drop() { - assert!(!mem::needs_drop::>()); - assert!(!mem::needs_drop::>()); - - // You wouldn't do this, but let's make sure it works as expected - #[repr(transparent)] - struct DropAndEncode(i32); - - unsafe impl Encode for DropAndEncode { - const ENCODING: Encoding = i32::ENCODING; - } - - impl Drop for DropAndEncode { - fn drop(&mut self) {} - } - - assert!(mem::needs_drop::()); - assert!(!mem::needs_drop::>()); - - assert_eq!(mem::size_of::>(), mem::size_of::()); - } -} diff --git a/crates/objc2/src/declare/ivar_forwarding_impls.rs b/crates/objc2/src/declare/ivar_forwarding_impls.rs deleted file mode 100644 index 3b80f25c7..000000000 --- a/crates/objc2/src/declare/ivar_forwarding_impls.rs +++ /dev/null @@ -1,340 +0,0 @@ -//! Trivial forwarding impls on `Ivar`. -//! -//! Kept here to keep `ivar.rs` free from this boilerplate. -//! -//! `#[inline]` is used where the standard library `Box` uses it. - -#![forbid(unsafe_code)] - -// use alloc::borrow; -use alloc::string::String; -use alloc::vec::Vec; -use core::cmp::Ordering; -use core::fmt; -use core::future::Future; -use core::hash; -use core::iter::FusedIterator; -use core::ops::Deref; -use core::pin::Pin; -use core::task::{Context, Poll}; -use std::error::Error; -use std::io; - -use super::{Ivar, IvarType}; - -impl PartialEq for Ivar -where - ::Target: PartialEq, -{ - #[inline] - fn eq(&self, other: &Self) -> bool { - (**self).eq(&**other) - } - - #[inline] - #[allow(clippy::partialeq_ne_impl)] - fn ne(&self, other: &Self) -> bool { - (**self).ne(&**other) - } -} - -impl Eq for Ivar where ::Target: Eq {} - -impl PartialOrd for Ivar -where - ::Target: PartialOrd, -{ - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - (**self).partial_cmp(&**other) - } - #[inline] - fn lt(&self, other: &Self) -> bool { - (**self).lt(&**other) - } - #[inline] - fn le(&self, other: &Self) -> bool { - (**self).le(&**other) - } - #[inline] - fn ge(&self, other: &Self) -> bool { - (**self).ge(&**other) - } - #[inline] - fn gt(&self, other: &Self) -> bool { - (**self).gt(&**other) - } -} - -impl Ord for Ivar -where - ::Target: Ord, -{ - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - (**self).cmp(&**other) - } -} - -impl hash::Hash for Ivar -where - ::Target: hash::Hash, -{ - fn hash(&self, state: &mut H) { - (**self).hash(state) - } -} - -impl hash::Hasher for Ivar -where - ::Target: hash::Hasher, -{ - fn finish(&self) -> u64 { - (**self).finish() - } - fn write(&mut self, bytes: &[u8]) { - (**self).write(bytes) - } - fn write_u8(&mut self, i: u8) { - (**self).write_u8(i) - } - fn write_u16(&mut self, i: u16) { - (**self).write_u16(i) - } - fn write_u32(&mut self, i: u32) { - (**self).write_u32(i) - } - fn write_u64(&mut self, i: u64) { - (**self).write_u64(i) - } - fn write_u128(&mut self, i: u128) { - (**self).write_u128(i) - } - fn write_usize(&mut self, i: usize) { - (**self).write_usize(i) - } - fn write_i8(&mut self, i: i8) { - (**self).write_i8(i) - } - fn write_i16(&mut self, i: i16) { - (**self).write_i16(i) - } - fn write_i32(&mut self, i: i32) { - (**self).write_i32(i) - } - fn write_i64(&mut self, i: i64) { - (**self).write_i64(i) - } - fn write_i128(&mut self, i: i128) { - (**self).write_i128(i) - } - fn write_isize(&mut self, i: isize) { - (**self).write_isize(i) - } -} - -impl fmt::Display for Ivar -where - ::Target: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl fmt::Debug for Ivar -where - ::Target: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl Iterator for Ivar -where - ::Target: Iterator, -{ - type Item = <::Target as Iterator>::Item; - fn next(&mut self) -> Option<<::Target as Iterator>::Item> { - (**self).next() - } - fn size_hint(&self) -> (usize, Option) { - (**self).size_hint() - } - fn nth(&mut self, n: usize) -> Option<<::Target as Iterator>::Item> { - (**self).nth(n) - } -} - -impl DoubleEndedIterator for Ivar -where - ::Target: DoubleEndedIterator, -{ - fn next_back(&mut self) -> Option<<::Target as Iterator>::Item> { - (**self).next_back() - } - fn nth_back(&mut self, n: usize) -> Option<<::Target as Iterator>::Item> { - (**self).nth_back(n) - } -} - -impl ExactSizeIterator for Ivar -where - ::Target: ExactSizeIterator, -{ - fn len(&self) -> usize { - (**self).len() - } -} - -impl FusedIterator for Ivar where ::Target: FusedIterator {} - -// impl borrow::Borrow<::Target> for Ivar { -// fn borrow(&self) -> &::Target { -// self -// } -// } -// -// impl borrow::BorrowMut<::Target> for Ivar { -// fn borrow_mut(&mut self) -> &mut ::Target { -// self -// } -// } - -impl AsRef<::Target> for Ivar { - fn as_ref(&self) -> &::Target { - // Auto-derefs - self - } -} - -impl AsMut<::Target> for Ivar { - fn as_mut(&mut self) -> &mut ::Target { - // Auto-derefs - self - } -} - -impl Error for Ivar -where - ::Target: Error, -{ - fn source(&self) -> Option<&(dyn Error + 'static)> { - (**self).source() - } -} - -impl io::Read for Ivar -where - ::Target: io::Read, -{ - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } - - #[inline] - fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { - (**self).read_vectored(bufs) - } - - #[inline] - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - (**self).read_to_end(buf) - } - - #[inline] - fn read_to_string(&mut self, buf: &mut String) -> io::Result { - (**self).read_to_string(buf) - } - - #[inline] - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - (**self).read_exact(buf) - } -} - -impl io::Write for Ivar -where - ::Target: io::Write, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (**self).write(buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { - (**self).write_vectored(bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (**self).flush() - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (**self).write_all(buf) - } - - #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - (**self).write_fmt(fmt) - } -} - -impl io::Seek for Ivar -where - ::Target: io::Seek, -{ - #[inline] - fn seek(&mut self, pos: io::SeekFrom) -> io::Result { - (**self).seek(pos) - } - - #[inline] - fn stream_position(&mut self) -> io::Result { - (**self).stream_position() - } -} - -impl io::BufRead for Ivar -where - ::Target: io::BufRead, -{ - #[inline] - fn fill_buf(&mut self) -> io::Result<&[u8]> { - (**self).fill_buf() - } - - #[inline] - fn consume(&mut self, amt: usize) { - (**self).consume(amt) - } - - #[inline] - fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { - (**self).read_until(byte, buf) - } - - #[inline] - fn read_line(&mut self, buf: &mut String) -> io::Result { - (**self).read_line(buf) - } -} - -impl Future for Ivar -where - Self: Unpin, - ::Target: Future + Unpin, -{ - type Output = <::Target as Future>::Output; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - <::Target as Future>::poll(Pin::new(&mut *self), cx) - } -} - -// TODO: impl Fn traits, CoerceUnsized, Stream and so on when stabilized diff --git a/crates/objc2/src/declare/mod.rs b/crates/objc2/src/declare/mod.rs index 6f61fc867..0daa8155c 100644 --- a/crates/objc2/src/declare/mod.rs +++ b/crates/objc2/src/declare/mod.rs @@ -4,12 +4,6 @@ //! variables and methods can then be added before the class is ultimately //! registered. -mod ivar; -mod ivar_bool; -mod ivar_drop; -mod ivar_encode; -mod ivar_forwarding_impls; - use alloc::format; use alloc::string::ToString; use core::mem; @@ -24,11 +18,6 @@ use crate::runtime::{AnyClass, AnyObject, AnyProtocol, Bool, Imp, MethodImplemen use crate::sel; use crate::Message; -pub use ivar::{InnerIvarType, Ivar, IvarType}; -pub use ivar_bool::IvarBool; -pub use ivar_drop::IvarDrop; -pub use ivar_encode::IvarEncode; - fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> CString { // First two arguments are always self and the selector let mut types = format!("{ret}{}{}", <*mut AnyObject>::ENCODING, Sel::ENCODING); @@ -399,6 +388,10 @@ impl ClassBuilder { unsafe { self.add_ivar_inner::(name, &T::ENCODING) } } + pub(crate) unsafe fn add_ivar_inner(&mut self, name: &str, encoding: &Encoding) { + unsafe { self.add_ivar_inner_mono(name, mem::size_of::(), T::LOG2_ALIGNMENT, encoding) } + } + // Monomorphized version unsafe fn add_ivar_inner_mono( &mut self, @@ -430,21 +423,6 @@ impl ClassBuilder { assert!(success.as_bool(), "failed to add ivar {name}"); } - unsafe fn add_ivar_inner(&mut self, name: &str, encoding: &Encoding) { - unsafe { self.add_ivar_inner_mono(name, mem::size_of::(), T::LOG2_ALIGNMENT, encoding) } - } - - /// Adds an instance variable from an [`IvarType`]. - /// - /// - /// # Panics - /// - /// Same as [`ClassBuilder::add_ivar`]. - pub fn add_static_ivar(&mut self) { - // SAFETY: The encoding is correct - unsafe { self.add_ivar_inner::(T::NAME, &T::Type::ENCODING) } - } - /// Adds the given protocol to self. /// /// # Panics @@ -617,7 +595,8 @@ mod tests { use crate::rc::Id; use crate::runtime::{NSObject, NSObjectProtocol}; use crate::{ - declare_class, extern_methods, msg_send, msg_send_id, test_utils, ClassType, ProtocolType, + declare_class, extern_methods, msg_send, msg_send_id, test_utils, ClassType, DeclaredClass, + ProtocolType, }; #[test] @@ -905,6 +884,8 @@ mod tests { type Mutability = Immutable; const NAME: &'static str = "TestInheritedNSObjectMethodsWork"; } + + impl DeclaredClass for Custom {} ); extern_methods!( @@ -923,7 +904,7 @@ mod tests { // description let expected = - format!("Custom {{ __superclass: ManuallyDrop {{ value: }} }}"); + format!("Custom {{ __superclass: ManuallyDrop {{ value: }}, __ivars: PhantomData<()> }}"); assert_eq!(format!("{obj1:?}"), expected); // hash diff --git a/crates/objc2/src/lib.rs b/crates/objc2/src/lib.rs index 1dbaeb38e..c6cec5447 100644 --- a/crates/objc2/src/lib.rs +++ b/crates/objc2/src/lib.rs @@ -190,7 +190,7 @@ pub use objc_sys as ffi; #[doc(no_inline)] pub use self::encode::{Encode, Encoding, RefEncode}; -pub use self::top_level_traits::{ClassType, Message, ProtocolType}; +pub use self::top_level_traits::{ClassType, DeclaredClass, Message, ProtocolType}; #[cfg(feature = "objc2-proc-macros")] #[doc(hidden)] diff --git a/crates/objc2/src/macros/__field_helpers.rs b/crates/objc2/src/macros/__field_helpers.rs deleted file mode 100644 index b545f17c6..000000000 --- a/crates/objc2/src/macros/__field_helpers.rs +++ /dev/null @@ -1,355 +0,0 @@ -#[doc(hidden)] -#[macro_export] -macro_rules! __emit_struct_and_ivars { - ( - ($(#[$m:meta])*) - ($v:vis) - ($($struct:tt)*) - ($($ivar_helper_module_v:vis mod $ivar_helper_module:ident)?) - ($($fields:tt)*) - ($($parsed_fields:tt)*) - ) => { - $crate::__parse_fields! { - ($($fields)*) - ($($ivar_helper_module_v mod $ivar_helper_module)?) - () () // No parsed ivars - ($($parsed_fields)*) - - ($crate::__emit_struct) - ($(#[$m])*) - ($v) - ($($struct)*) - } - } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __emit_struct { - ( - ($(#[$m:meta])*) - ($v:vis) - ($($struct:tt)*) - - ($($fields:tt)*) - ) => { - $(#[$m])* - #[repr(C)] - $v struct $($struct)* { - // These are at this point all zero-sized. - $($fields)* - } - } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __parse_fields { - // Base-case, no ivars, no module - ( - () // No more fields left - () // No module - () () // No ivars - ($($parsed_fields:tt)*) - - ($out_macro:path) - $($macro_args:tt)* - ) => { - $out_macro! { - $($macro_args)* - - ($($parsed_fields)*) - } - }; - - // Base-case, has ivars, no module - ( - () // No more fields left - () // No module - ($($ivar_output:tt)+) ($($ivar_type_name:tt)+) - ($($parsed_fields:tt)*) - - ($out_macro:path) - $($macro_args:tt)* - ) => { - $crate::__macro_helpers::compile_error!( - "must specify an ivar module when the type has ivars" - ); - - $($ivar_output)+ - - $out_macro! { - $($macro_args)* - - ($($parsed_fields)*) - } - }; - - // Base-case, no ivars, has module - ( - () // No more fields left - ($ivar_helper_module_v:vis mod $ivar_helper_module:ident) - () () // No ivars - ($($parsed_fields:tt)*) - - ($out_macro:path) - $($macro_args:tt)* - ) => { - $ivar_helper_module_v mod $ivar_helper_module { - $crate::__macro_helpers::compile_error!( - "no need to specify an ivar module when the type has no ivars" - ); - - pub(super) fn __objc2_declare_ivars( - __objc2_builder: &mut $crate::__macro_helpers::ClassBuilderHelper, - ) {} - } - - $out_macro! { - $($macro_args)* - - ($($parsed_fields)*) - } - }; - - // Base-case, has ivars, has module - ( - () // No more fields left - ($ivar_helper_module_v:vis mod $ivar_helper_module:ident) - ($($ivar_output:tt)+) ($($ivar_type_name:ident)+) - ($($parsed_fields:tt)*) - - ($out_macro:path) - $($macro_args:tt)* - ) => { - $ivar_helper_module_v mod $ivar_helper_module { - use super::*; - - $($ivar_output)+ - - pub(super) fn __objc2_declare_ivars( - __objc2_builder: &mut $crate::__macro_helpers::ClassBuilderHelper, - ) { - // Ivars - $( - __objc2_builder.add_static_ivar::<$ivar_type_name>(); - )+ - } - } - - $out_macro! { - $($macro_args)* - - ($($parsed_fields)*) - } - }; - - // PhantomData - ( - ( - $(#[$m:meta])* - $vis:vis $field_name:ident: PhantomData<$ty:ty> - $(, $($rest_fields:tt)*)? - ) - ($($ivar_helper_module_v:vis mod $ivar_helper_module:ident)?) - ($($ivar_output:tt)*) ($($ivar_type_name:ident)*) - ($($parsed_fields:tt)*) - - ($out_macro:path) - $($macro_args:tt)* - ) => { - $crate::__parse_fields! { - ($($($rest_fields)*)?) - ($($ivar_helper_module_v mod $ivar_helper_module)?) - ($($ivar_output)*) ($($ivar_type_name)*) - ( - $($parsed_fields)* - - // A user could have defined their own PhantomData-like, type, - // and then tried to use it here, which we would accept, but - // which wouldn't necessarily be zero-sized! - // - // Hence we wrap it in an extra PhantomData, to ensure it is - // (while still not generating "unused imports" for the user). - $(#[$m])* - $vis $field_name: $crate::__macro_helpers::PhantomData>, - ) - - ($out_macro) - $($macro_args)* - } - }; - - // IvarDrop - ( - ( - $(#[$m:meta])* - $vis:vis $field_name:ident: IvarDrop<$ty:ty, $ivar_name:literal> - $(, $($rest_fields:tt)*)? - ) - ($($ivar_helper_module_v:vis mod $ivar_helper_module:ident)?) - ($($ivar_output:tt)*) ($($ivar_type_name:ident)*) - ($($parsed_fields:tt)*) - - ($out_macro:path) - $($macro_args:tt)* - ) => { - $crate::__parse_fields! { - ($($($rest_fields)*)?) - ($($ivar_helper_module_v mod $ivar_helper_module)?) - ( - $($ivar_output)* - - #[allow(non_camel_case_types)] - #[allow(unreachable_pub)] - pub struct $field_name { - __priv: (), - } - - // SAFETY: - // - The ivars are in a type used as an Objective-C object. - // - The ivar is added to the class in `__objc2_declare_ivars`. - // - Caller upholds that the ivars are properly initialized. - unsafe impl $crate::declare::IvarType for $field_name { - type Type = IvarDrop<$ty>; - const NAME: &'static $crate::__macro_helpers::str = $ivar_name; - } - ) ($($ivar_type_name)* $field_name) - ( - $($parsed_fields)* - - $(#[$m])* - $vis $field_name: $crate::declare::Ivar<$($ivar_helper_module ::)? $field_name>, - ) - - ($out_macro) - $($macro_args)* - } - }; - - // IvarEncode - ( - ( - $(#[$m:meta])* - $vis:vis $field_name:ident: IvarEncode<$ty:ty, $ivar_name:literal> - $(, $($rest_fields:tt)*)? - ) - ($($ivar_helper_module_v:vis mod $ivar_helper_module:ident)?) - ($($ivar_output:tt)*) ($($ivar_type_name:ident)*) - ($($parsed_fields:tt)*) - - ($out_macro:path) - $($macro_args:tt)* - ) => { - $crate::__parse_fields! { - ($($($rest_fields)*)?) - ($($ivar_helper_module_v mod $ivar_helper_module)?) - ( - $($ivar_output)* - - #[allow(non_camel_case_types)] - #[allow(unreachable_pub)] - pub struct $field_name { - __priv: (), - } - - // SAFETY: See above - unsafe impl $crate::declare::IvarType for $field_name { - type Type = IvarEncode<$ty>; - const NAME: &'static $crate::__macro_helpers::str = $ivar_name; - } - ) ($($ivar_type_name)* $field_name) - ( - $($parsed_fields)* - - $(#[$m])* - $vis $field_name: $crate::declare::Ivar<$($ivar_helper_module ::)? $field_name>, - ) - - ($out_macro) - $($macro_args)* - } - }; - - // IvarBool - ( - ( - $(#[$m:meta])* - $vis:vis $field_name:ident: IvarBool<$ivar_name:literal> - $(, $($rest_fields:tt)*)? - ) - ($($ivar_helper_module_v:vis mod $ivar_helper_module:ident)?) - ($($ivar_output:tt)*) ($($ivar_type_name:ident)*) - ($($parsed_fields:tt)*) - - ($out_macro:path) - $($macro_args:tt)* - ) => { - $crate::__parse_fields! { - ($($($rest_fields)*)?) - ($($ivar_helper_module_v mod $ivar_helper_module)?) - ( - $($ivar_output)* - - #[allow(non_camel_case_types)] - #[allow(unreachable_pub)] - pub struct $field_name { - __priv: (), - } - - // SAFETY: See above - unsafe impl $crate::declare::IvarType for $field_name { - type Type = IvarBool; - const NAME: &'static $crate::__macro_helpers::str = $ivar_name; - } - ) ($($ivar_type_name)* $field_name) - ( - $($parsed_fields)* - - $(#[$m])* - $vis $field_name: $crate::declare::Ivar<$($ivar_helper_module ::)? $field_name>, - ) - - ($out_macro) - $($macro_args)* - } - }; - - // Invalid type - ( - ( - $(#[$m:meta])* - $vis:vis $field_name:ident: $ty:ty - $(, $($rest_fields:tt)*)? - ) - ($($ivar_helper_module_v:vis mod $ivar_helper_module:ident)?) - ($($ivar_output:tt)*) ($($ivar_type_name:ident)*) - ($($parsed_fields:tt)*) - - ($out_macro:path) - $($macro_args:tt)* - ) => { - $crate::__macro_helpers::compile_error!($crate::__macro_helpers::concat!( - "invalid type ", - $crate::__macro_helpers::stringify!($ty), - " in field ", - $crate::__macro_helpers::stringify!($field_name), - ". Type must be either `PhantomData`, `IvarDrop`, `IvarBool` or `IvarEncode`." - )); - - $crate::__parse_fields! { - ($($($rest_fields)*)?) - ($($ivar_helper_module_v mod $ivar_helper_module)?) - ($($ivar_output)*) ($($ivar_type_name)*) - ( - $($parsed_fields)* - - $(#[$m])* - $vis $field_name: $ty, - ) - - ($out_macro) - $($macro_args)* - } - } -} diff --git a/crates/objc2/src/macros/__method_msg_send.rs b/crates/objc2/src/macros/__method_msg_send.rs index fbcccf9b8..3dfe8a238 100644 --- a/crates/objc2/src/macros/__method_msg_send.rs +++ b/crates/objc2/src/macros/__method_msg_send.rs @@ -167,6 +167,7 @@ macro_rules! __method_msg_send_id { $crate::__msg_send_id_helper! { ($receiver) ($($retain_semantics)?) + (MsgSendId) (send_message_id) ($sel) () @@ -255,6 +256,7 @@ macro_rules! __method_msg_send_id { $crate::__msg_send_id_helper! { ($receiver) ($($retain_semantics)?) + (MsgSendId) (send_message_id) ($($sel_parsed)*) ($($arg_parsed)*) @@ -275,6 +277,7 @@ macro_rules! __method_msg_send_id { $crate::__msg_send_id_helper! { ($receiver) ($($retain_semantics)?) + (MsgSendId) // Use error method (send_message_id_error) ($($sel_parsed)* $sel :) diff --git a/crates/objc2/src/macros/__msg_send_parse.rs b/crates/objc2/src/macros/__msg_send_parse.rs index e6ee0c5fa..31f25fce9 100644 --- a/crates/objc2/src/macros/__msg_send_parse.rs +++ b/crates/objc2/src/macros/__msg_send_parse.rs @@ -139,6 +139,7 @@ macro_rules! __comma_between_args { #[macro_export] #[cfg(feature = "unstable-msg-send-always-comma")] macro_rules! __comma_between_args { + // msg_send! ( (send_super_message_static) ($($args:tt)*) @@ -169,12 +170,37 @@ macro_rules! __comma_between_args { ($crate::__macro_helpers::stringify!($obj), $($args)*) } }; - // Catch-all for msg_send_id! + // msg_send_id! ( - ($fn:ident) + (send_super_message_id_static) + ($($args:tt)*) + ($obj:expr) + () + (MsgSendSuperId) + ) => { + $crate::__comma_between_args_inner! { + ("msg_send_id") + ($crate::__macro_helpers::stringify!(super($obj)), $($args)*) + } + }; + ( + (send_super_message_id) + ($($args:tt)*) + ($obj:expr, $superclass:expr) + () + (MsgSendSuperId) + ) => { + $crate::__comma_between_args_inner! { + ("msg_send_id") + ($crate::__macro_helpers::stringify!(super($obj, $superclass)), $($args)*) + } + }; + ( + (send_message_id) ($($args:tt)*) ($obj:expr) () + (MsgSendId) ) => { $crate::__comma_between_args_inner! { ("msg_send_id") diff --git a/crates/objc2/src/macros/declare_class.rs b/crates/objc2/src/macros/declare_class.rs index 2b710477c..3702ae5db 100644 --- a/crates/objc2/src/macros/declare_class.rs +++ b/crates/objc2/src/macros/declare_class.rs @@ -16,62 +16,72 @@ /// /// # Specification /// -/// This macro consists of roughly four parts: -/// - The type and ivar definition. +/// This macro consists of the following parts (the first three are required): +/// - The type declaration. /// - The [`ClassType`] implementation. -/// - Any number of method definitions. +/// - The [`DeclaredClass`] implementation. +/// - Any number of inherent implementations. /// - Any number of protocol implementations. /// /// With the syntax generally resembling a combination of that in /// [`extern_class!`] and [`extern_methods!`]. /// /// [`ClassType`]: crate::ClassType +/// [`DeclaredClass`]: crate::DeclaredClass /// [`extern_class!`]: crate::extern_class /// [`extern_methods!`]: crate::extern_methods /// /// -/// ## Ivar definition +/// ## Type declaration /// -/// The type definition works a lot like [`extern_class!`] (including the -/// allowed attributes), with the added capability that struct fields are -/// automatically defined as custom instance variables, which are then -/// accessible on instances of the class. (E.g. you can use `self.my_ivar` as -/// if the class was a normal Rust struct). +/// The type declaration works a lot like in [`extern_class!`], an opaque +/// struct is created and a lot of traits is implemented for that struct. /// -/// The instance variables are specified as such: -/// - [`IvarEncode`](crate::declare::IvarEncode) -/// - [`IvarBool<"my_crate_ivar">`](crate::declare::IvarBool) -/// - [`IvarDrop`](crate::declare::IvarDrop) +/// You are allowed to add most common attributes to the declaration, +/// including `#[cfg(...)]` and doc comments. ABI-modifying attributes like +/// `#[repr(...)]` are not allowed. /// -/// This is special syntax that will be used to generate helper types that -/// implement [`declare::IvarType`], which is then used inside the new struct. +/// `#[derive(...)]` attributes are allowed, but heavily discouraged, as they +/// are likely to not work as you'd expect them to. This is being worked on in +/// [#267]. /// -/// Instance variable names must be unique, and must not conflict with any -/// superclass' instance variables - this means is is good practice to name -/// them with a prefix of your crate name, or similar. +/// If the type implements [`Drop`], the macro will generate a `dealloc` +/// method for you, and will call that automatically. /// -/// [`declare::IvarType`]: crate::declare::IvarType +/// [#267]: https://github.com/madsmtm/objc2/issues/267 /// /// /// ## `ClassType` implementation /// -/// This also resembles that in [`extern_class!`], except that +/// This also resembles the syntax in [`extern_class!`], except that /// [`ClassType::NAME`] must be specified, and it must be unique across the -/// entire application. Good practice here is to include your crate name in -/// the prefix. +/// entire application. +/// +/// If you're developing a library, good practice here would be to include +/// your crate name in the prefix (something like `"MyLibrary_MyClass"`). /// /// The class is guaranteed to have been created and registered with the /// Objective-C runtime after the [`ClassType::class`] function has been /// called. /// -/// The macro will generate a `dealloc` method for you, which will call any -/// [`Drop`] impl you may have defined on the type. -/// /// [`ClassType::NAME`]: crate::ClassType::NAME /// [`ClassType::class`]: crate::ClassType::class /// /// -/// ## Method definitions +/// ## `DeclaredClass` implementation +/// +/// The syntax here is as if you were implementing the trait yourself. +/// +/// You may optionally specify the associated type [`Ivars`]; this is the +/// intended way to specify the data your class stores. If you don't specify +/// any ivars, the macro will default to [`()`][unit]. +/// +/// Beware that the ivars must be initialized in an `init` method. +/// +/// [`Ivars`]: crate::DeclaredClass::Ivars +/// +/// +/// ## Inherent method definitions /// /// Within the `impl` block you can define two types of functions; /// ["associated functions"] and ["methods"]. These are then mapped to the @@ -142,12 +152,12 @@ /// The implemented `ClassType::class` method may panic in a few cases, such /// as if: /// - A class with the specified name already exists. -/// - One of the class' instance variables already exist on a superclass. /// - Debug assertions are enabled, and an overriden method's signature is not /// equal to the one on the superclass. /// - The `verify` feature and debug assertions are enabled, and the required /// protocol methods are not implemented. -/// - And possibly more similar cases. +/// +/// And possibly more similar cases. /// /// /// # Safety @@ -155,12 +165,16 @@ /// Using this macro requires writing a few `unsafe` markers: /// /// `unsafe impl ClassType for T` has the following safety requirements: -/// - Any invariants that the overridden class [`ClassType::Super`] may have -/// must be upheld. +/// - Any invariants that the superclass [`ClassType::Super`] may have must be +/// upheld. /// - [`ClassType::Mutability`] must be correct. -/// - Any instance variables you specify under the struct definition must -/// either be able to be created using [`MaybeUninit::zeroed`], or be -/// properly initialized in an `init` method. +/// - If your type implements `Drop`, the implementation must abide by the +/// following rules: +/// - It must not call any overridden methods. +/// - It must not `retain` the object past the lifetime of the drop. +/// - It must not `retain` in the same scope that `&mut self` is active. +/// - TODO: And probably a few more. [Open an issue] if you would like +/// guidance on whether your implementation is correct. /// /// `unsafe impl T { ... }` asserts that the types match those that are /// expected when the method is invoked from Objective-C. Note that unlike @@ -175,7 +189,7 @@ /// /// [`ClassType::Super`]: crate::ClassType::Super /// [`ClassType::Mutability`]: crate::ClassType::Mutability -/// [`MaybeUninit::zeroed`]: core::mem::MaybeUninit::zeroed +/// [Open an issue]: https://github.com/madsmtm/objc2/issues/new /// /// /// # Examples @@ -189,61 +203,55 @@ /// # use objc2::runtime::{NSObject, NSObjectProtocol, NSZone}; /// # #[cfg(available_in_icrate)] /// use icrate::Foundation::{NSCopying, NSObject, NSObjectProtocol, NSZone}; -/// use objc2::declare::{Ivar, IvarDrop, IvarEncode}; -/// use objc2::rc::Id; +/// use objc2::rc::{Allocated, Id}; /// use objc2::{ -/// declare_class, extern_protocol, msg_send, msg_send_id, mutability, ClassType, ProtocolType, +/// declare_class, extern_protocol, msg_send, msg_send_id, mutability, ClassType, +/// DeclaredClass, ProtocolType, /// }; /// -/// declare_class!( -/// struct MyCustomObject { -/// foo: IvarEncode, -/// pub bar: IvarEncode, -/// object: IvarDrop, "_object">, -/// } +/// #[derive(Clone)] +/// struct Ivars { +/// foo: u8, +/// bar: c_int, +/// object: Id, +/// } /// -/// mod ivars; +/// declare_class!( +/// struct MyCustomObject; /// +/// // SAFETY: +/// // - The superclass NSObject does not have any subclassing requirements. +/// // - Interior mutability is a safe default. +/// // - `MyCustomObject` does not implement `Drop`. /// unsafe impl ClassType for MyCustomObject { /// type Super = NSObject; -/// type Mutability = mutability::Mutable; +/// type Mutability = mutability::InteriorMutable; /// const NAME: &'static str = "MyCustomObject"; /// } /// +/// impl DeclaredClass for MyCustomObject { +/// type Ivars = Ivars; +/// } +/// /// unsafe impl MyCustomObject { -/// #[method(initWithFoo:)] -/// fn init_with(this: &mut Self, foo: u8) -> Option<&mut Self> { -/// let this: Option<&mut Self> = unsafe { -/// msg_send![super(this), init] -/// }; -/// -/// this.map(|this| { -/// // Initialize instance variables -/// -/// // Some types like `u8`, `bool`, `Option>` and -/// // `Option>` are safe to zero-initialize, and we can -/// // write to the variable as normal: -/// *this.foo = foo; -/// *this.bar = 42; -/// -/// // For others like `&u8`, `Box` or `Id`, we have to -/// // initialize them with `Ivar::write`: -/// Ivar::write(&mut this.object, NSObject::new()); -/// -/// // All the instance variables have been initialized; our -/// // initializer is sound -/// this -/// }) +/// #[method_id(initWithFoo:)] +/// fn init_with(this: Allocated, foo: u8) -> Option> { +/// let this = this.set_ivars(Ivars { +/// foo, +/// bar: 42, +/// object: NSObject::new(), +/// }); +/// unsafe { msg_send_id![super(this), init] } /// } /// /// #[method(foo)] /// fn __get_foo(&self) -> u8 { -/// *self.foo +/// self.ivars().foo /// } /// /// #[method_id(object)] /// fn __get_object(&self) -> Id { -/// self.object.clone() +/// self.ivars().object.clone() /// } /// /// #[method(myClassMethod)] @@ -253,9 +261,8 @@ /// # /// # #[method_id(copyWithZone:)] /// # fn copyWithZone(&self, _zone: *const NSZone) -> Id { -/// # let mut obj = Self::new(*self.foo); -/// # *obj.bar = *self.bar; -/// # obj +/// # let new = Self::alloc().set_ivars(self.ivars().clone()); +/// # unsafe { msg_send_id![super(new), init] } /// # } /// } /// @@ -265,9 +272,8 @@ /// unsafe impl NSCopying for MyCustomObject { /// #[method_id(copyWithZone:)] /// fn copyWithZone(&self, _zone: *const NSZone) -> Id { -/// let mut obj = Self::new(*self.foo); -/// *obj.bar = *self.bar; -/// obj +/// let new = Self::alloc().set_ivars(self.ivars().clone()); +/// unsafe { msg_send_id![super(new), init] } /// } /// /// // If we have tried to add other methods here, or had forgotten @@ -296,9 +302,9 @@ /// /// fn main() { /// let obj = MyCustomObject::new(3); -/// assert_eq!(*obj.foo, 3); -/// assert_eq!(*obj.bar, 42); -/// assert!(obj.object.is_kind_of::()); +/// assert_eq!(obj.ivars().foo, 3); +/// assert_eq!(obj.ivars().bar, 42); +/// assert!(obj.ivars().object.is_kind_of::()); /// /// # let obj: Id = unsafe { msg_send_id![&obj, copy] }; /// # #[cfg(available_in_icrate)] @@ -316,22 +322,18 @@ /// ```text /// #import /// -/// @interface MyCustomObject: NSObject { -/// // Public ivar -/// int bar; -/// } -/// +/// @interface MyCustomObject: NSObject /// - (instancetype)initWithFoo:(uint8_t)foo; /// - (uint8_t)foo; /// - (NSObject*)object; /// + (BOOL)myClassMethod; -/// /// @end /// /// /// @implementation MyCustomObject { -/// // Private ivar +/// // Instance variables /// uint8_t foo; +/// int bar; /// NSObject* _Nonnull object; /// } /// @@ -360,9 +362,10 @@ /// // NSCopying /// /// - (id)copyWithZone:(NSZone *)_zone { -/// MyCustomObject* obj = [[MyCustomObject alloc] initWithFoo: self->foo]; -/// obj->bar = self->bar; -/// return obj; +/// MyCustomObject* new = [[MyCustomObject alloc] initWithFoo: self->foo]; +/// new->bar = self->bar; +/// new->obj = self->obj; +/// return new; /// } /// /// @end @@ -371,16 +374,11 @@ #[doc(alias = "@implementation")] #[macro_export] macro_rules! declare_class { - // With ivar helper { $(#[$m:meta])* - $v:vis struct $name:ident { - $($fields:tt)* - } - - $ivar_helper_module_v:vis mod $ivar_helper_module:ident; + $v:vis struct $name:ident; - unsafe impl ClassType for $for:ty { + unsafe impl ClassType for $for_class:ty { $(#[inherits($($inheritance_rest:ty),+)])? type Super = $superclass:ty; @@ -389,238 +387,109 @@ macro_rules! declare_class { const NAME: &'static str = $name_const:expr; } - $($impls:tt)* - } => { - $crate::__emit_struct_and_ivars! { - ($(#[$m])*) - ($v) - ($name) - ($ivar_helper_module_v mod $ivar_helper_module) - ($($fields)*) - ( - // Superclasses are deallocated by calling `[super dealloc]`. - __superclass: $crate::__macro_helpers::ManuallyDrop<$superclass>, - ) - } - - $crate::__declare_class_inner! { - ($ivar_helper_module) - - unsafe impl ClassType for $for { - $(#[inherits($($inheritance_rest),+)])? - type Super = $superclass; - - type Mutability = $mutability; - - const NAME: &'static str = $name_const; - } - - $($impls)* - } - }; - - // No ivar helper - { - $(#[$m:meta])* - $v:vis struct $name:ident { - $($fields:tt)* - } - - unsafe impl ClassType for $for:ty { - $(#[inherits($($inheritance_rest:ty),+)])? - type Super = $superclass:ty; - - type Mutability = $mutability:ty; - - const NAME: &'static str = $name_const:expr; + impl DeclaredClass for $for_declared:ty { + $(type Ivars = $ivars:ty;)? } $($impls:tt)* } => { - $crate::__emit_struct_and_ivars! { - ($(#[$m])*) - ($v) - ($name) - () - ($($fields)*) - ( - // Superclasses are deallocated by calling `[super dealloc]`. - __superclass: $crate::__macro_helpers::ManuallyDrop<$superclass>, - ) + $(#[$m])* + #[repr(C)] + $v struct $name { + // Superclasses are deallocated by calling `[super dealloc]`. + __superclass: $crate::__macro_helpers::ManuallyDrop<$superclass>, + // Include ivars for proper auto traits. + __ivars: $crate::__macro_helpers::PhantomData<::Ivars>, } - $crate::__declare_class_inner! { - () - - unsafe impl ClassType for $for { - $(#[inherits($($inheritance_rest),+)])? - type Super = $superclass; + $crate::__extern_class_impl_traits! { + // SAFETY: Upheld by caller + unsafe impl () for $for_class { + INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::AnyObject]; - type Mutability = $mutability; + fn as_super(&self) { + &*self.__superclass + } - const NAME: &'static str = $name_const; + fn as_super_mut(&mut self) { + &mut *self.__superclass + } } - - $($impls)* } - }; - // Allow declaring class with no instance variables - { - $(#[$m:meta])* - $v:vis struct $name:ident; - - unsafe impl ClassType for $for:ty { - $(#[inherits($($inheritance_rest:ty),+)])? - type Super = $superclass:ty; - - type Mutability = $mutability:ty; - - const NAME: &'static str = $name_const:expr; - } - - $($impls:tt)* - } => { - $crate::__emit_struct_and_ivars! { - ($(#[$m])*) - ($v) - ($name) - () - () - ( - // Superclasses are deallocated by calling `[super dealloc]`. - __superclass: $crate::__macro_helpers::ManuallyDrop<$superclass>, - ) - } + // Anonymous block to hide the shared statics + const _: () = { + static mut __OBJC2_CLASS: $crate::__macro_helpers::MaybeUninit<&'static $crate::runtime::AnyClass> = $crate::__macro_helpers::MaybeUninit::uninit(); + static mut __OBJC2_IVAR_OFFSET: $crate::__macro_helpers::MaybeUninit<$crate::__macro_helpers::isize> = $crate::__macro_helpers::MaybeUninit::uninit(); + static mut __OBJC2_DROP_FLAG_OFFSET: $crate::__macro_helpers::MaybeUninit<$crate::__macro_helpers::isize> = $crate::__macro_helpers::MaybeUninit::uninit(); - $crate::__declare_class_inner! { - () - - unsafe impl ClassType for $for { - $(#[inherits($($inheritance_rest),+)])? + // Creation + unsafe impl ClassType for $for_class { type Super = $superclass; - type Mutability = $mutability; + const NAME: &'static $crate::__macro_helpers::str = $name_const; - const NAME: &'static str = $name_const; - } + fn class() -> &'static $crate::runtime::AnyClass { + $crate::__macro_helpers::assert_mutability_matches_superclass_mutability::(); - $($impls)* - } - }; -} + // TODO: Use `std::sync::OnceLock` + static REGISTER_CLASS: $crate::__macro_helpers::Once = $crate::__macro_helpers::Once::new(); -#[doc(hidden)] -#[macro_export] -macro_rules! __declare_class_inner { - { - ($($ivar_helper_module:ident)?) + REGISTER_CLASS.call_once(|| { + let mut __objc2_builder = $crate::__macro_helpers::ClassBuilderHelper::::new(); - unsafe impl ClassType for $for:ty { - $(#[inherits($($inheritance_rest:ty),+)])? - type Super = $superclass:ty; + // Implement protocols and methods + $crate::__declare_class_register_impls! { + (__objc2_builder) + $($impls)* + } - type Mutability = $mutability:ty; + let (__objc2_cls, __objc2_ivar_offset, __objc2_drop_flag_offset) = __objc2_builder.register(); - const NAME: &'static str = $name_const:expr; - } + // SAFETY: Modification is ensured by `Once` to happen + // before any access to the variables. + unsafe { + __OBJC2_CLASS.write(__objc2_cls); + __OBJC2_IVAR_OFFSET.write(__objc2_ivar_offset); + __OBJC2_DROP_FLAG_OFFSET.write(__objc2_drop_flag_offset); + } + }); - $($impls:tt)* - } => { - $crate::__extern_class_impl_traits! { - // SAFETY: Upheld by caller - unsafe impl () for $for { - INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::AnyObject]; + // SAFETY: We just registered the class, so is now available + unsafe { __OBJC2_CLASS.assume_init() } + } - fn as_super(&self) { + #[inline] + fn as_super(&self) -> &Self::Super { &*self.__superclass } - fn as_super_mut(&mut self) { + #[inline] + fn as_super_mut(&mut self) -> &mut Self::Super { &mut *self.__superclass } } - } - // Creation - unsafe impl ClassType for $for { - type Super = $superclass; - type Mutability = $mutability; - const NAME: &'static $crate::__macro_helpers::str = $name_const; - - fn class() -> &'static $crate::runtime::AnyClass { - $crate::__macro_helpers::assert_mutability_matches_superclass_mutability::(); - - // TODO: Use `core::cell::LazyCell` - static REGISTER_CLASS: $crate::__macro_helpers::Once = $crate::__macro_helpers::Once::new(); - - REGISTER_CLASS.call_once(|| { - let mut __objc2_builder = $crate::__macro_helpers::ClassBuilderHelper::::new(); - - $($ivar_helper_module::__objc2_declare_ivars(&mut __objc2_builder);)? - - // See the following links for more details: - // - - // - - // - - unsafe extern "C" fn __objc2_dealloc(__objc2_self: *mut $for, __objc2_cmd: $crate::runtime::Sel) { - // SAFETY: Ivars are explicitly designed to always - // be valid to drop, and since this is the - // `dealloc` method, we know the ivars are never - // going to be touched again. - // - // This also runs any `Drop` impl that the type may - // have. - unsafe { $crate::__macro_helpers::drop_in_place(__objc2_self) }; - - // The superclass' "marker" that this stores is - // wrapped in `ManuallyDrop`, instead we drop it by - // calling the superclass' `dealloc` method. - // - // Note: ARC does this automatically, which means - // most Objective-C code in the wild don't contain - // this; but we _are_ ARC, so we must do this. - unsafe { - $crate::__macro_helpers::MsgSend::send_super_message_static( - __objc2_self, - __objc2_cmd, // Reuse the selector - (), // No arguments - ) - } - } + impl DeclaredClass for $for_declared { + type Ivars = $crate::__select_ivars!($($ivars)?); - if $crate::__macro_helpers::needs_drop::() { - unsafe { - __objc2_builder.add_method( - $crate::sel!(dealloc), - __objc2_dealloc as unsafe extern "C" fn(_, _), - ); - } - } - - // Implement protocols and methods - $crate::__declare_class_register_impls! { - (__objc2_builder) - $($impls)* - } - - let _cls = __objc2_builder.register(); - }); - - // We just registered the class, so it should be available - $crate::runtime::AnyClass::get(::NAME).unwrap() - } + #[inline] + fn __ivars_offset() -> $crate::__macro_helpers::isize { + // SAFETY: Accessing the offset is guaranteed to only be + // done after the class has been initialized. + unsafe { __OBJC2_IVAR_OFFSET.assume_init() } + } - #[inline] - fn as_super(&self) -> &Self::Super { - &*self.__superclass - } + #[inline] + fn __drop_flag_offset() -> $crate::__macro_helpers::isize { + // SAFETY: Same as above. + unsafe { __OBJC2_DROP_FLAG_OFFSET.assume_init() } + } - #[inline] - fn as_super_mut(&mut self) -> &mut Self::Super { - &mut *self.__superclass + // SAFETY: The offsets are implemented correctly + const __UNSAFE_OFFSETS_CORRECT: () = (); } - } + }; // Methods $crate::__declare_class_output_impls! { @@ -640,6 +509,18 @@ macro_rules! __select_name { }; } +#[doc(hidden)] +#[macro_export] +macro_rules! __select_ivars { + ($ivars:ty) => { + $ivars + }; + () => { + // Default ivars to unit + () + }; +} + #[doc(hidden)] #[macro_export] macro_rules! __declare_class_output_impls { @@ -1106,6 +987,8 @@ macro_rules! __declare_class_method_out_inner { $($params_prefix)* $($params_converted)* ) -> $crate::__macro_helpers::IdReturnValue { + // TODO: Somehow tell the compiler that `this: Allocated` is non-null. + $($body_prefix)* let __objc2_result = $body; diff --git a/crates/objc2/src/macros/extern_methods.rs b/crates/objc2/src/macros/extern_methods.rs index 3fe32b354..ae4f2c5c9 100644 --- a/crates/objc2/src/macros/extern_methods.rs +++ b/crates/objc2/src/macros/extern_methods.rs @@ -69,7 +69,7 @@ /// use objc2::ffi::NSUInteger; /// use objc2::rc::{Allocated, Id}; /// use objc2::runtime::NSObject; -/// use objc2::{declare_class, extern_methods, mutability, ClassType}; +/// use objc2::{declare_class, extern_methods, mutability, ClassType, DeclaredClass}; /// /// // Shim /// type NSError = NSObject; @@ -77,12 +77,18 @@ /// declare_class!( /// pub struct MyObject; /// +/// // SAFETY: +/// // - The superclass NSObject does not have any subclassing requirements. +/// // - Interior mutability is a safe default. +/// // - `MyObject` does not implement `Drop`. /// unsafe impl ClassType for MyObject { /// type Super = NSObject; -/// type Mutability = mutability::Immutable; +/// type Mutability = mutability::InteriorMutable; /// const NAME: &'static str = "MyObject"; /// } /// +/// impl DeclaredClass for MyObject {} +/// /// unsafe impl MyObject { /// // ... Assume we've implemented all the methods used below /// } @@ -123,7 +129,7 @@ /// # use objc2::ffi::NSUInteger; /// # use objc2::rc::{Allocated, Id}; /// # use objc2::runtime::NSObject; -/// # use objc2::{declare_class, extern_methods, mutability, ClassType}; +/// # use objc2::{declare_class, extern_methods, mutability, ClassType, DeclaredClass}; /// # /// # // Shim /// # type NSError = NSObject; @@ -137,6 +143,8 @@ /// # const NAME: &'static str = "MyObject2"; /// # } /// # +/// # impl DeclaredClass for MyObject {} +/// # /// # unsafe impl MyObject { /// # // ... Assume we've implemented all the methods used below /// # } diff --git a/crates/objc2/src/macros/mod.rs b/crates/objc2/src/macros/mod.rs index da02ac8e2..b82a19052 100644 --- a/crates/objc2/src/macros/mod.rs +++ b/crates/objc2/src/macros/mod.rs @@ -1,5 +1,4 @@ mod __attribute_helpers; -mod __field_helpers; mod __method_msg_send; mod __msg_send_parse; mod __rewrite_self_param; @@ -872,7 +871,7 @@ macro_rules! __class_inner { /// use objc2::msg_send; /// # /// # use objc2::runtime::NSObject; -/// # use objc2::{declare_class, mutability, ClassType}; +/// # use objc2::{declare_class, mutability, ClassType, DeclaredClass}; /// # /// # declare_class!( /// # struct MyObject; @@ -882,6 +881,8 @@ macro_rules! __class_inner { /// # type Mutability = mutability::InteriorMutable; /// # const NAME: &'static str = "MyObject"; /// # } +/// # +/// # impl DeclaredClass for MyObject {} /// # ); /// /// let obj: &MyObject; // Some object that implements ClassType @@ -1084,7 +1085,10 @@ macro_rules! msg_send_bool { /// type is a generic `Allocated`. /// /// - The `init` family: The receiver must be `Allocated` as returned from -/// `alloc`. The receiver is consumed, and a the now-initialized `Id` or +/// `alloc`, or if sending messages to the superclass, it must be +/// `PartialInit`. +/// +/// The receiver is consumed, and a the now-initialized `Id` or /// `Option>` (with the same `T`) is returned. /// /// - The `copy` family: The receiver may be anything that implements @@ -1102,17 +1106,16 @@ macro_rules! msg_send_bool { /// See [the clang documentation][arc-retainable] for the precise /// specification of Objective-C's ownership rules. /// -/// As you may have noticed, the return type is always either `Id / Allocated` -/// or `Option`. Internally, the return type is always -/// `Option` (for example: almost all `new` methods can fail -/// if the allocation failed), but for convenience, if the return type is -/// `Id / Allocated` this macro will automatically unwrap the object, or panic -/// with an error message if it couldn't be retrieved. +/// As you may have noticed, the return type is usually either `Id` or +/// `Option`. Internally, the return type is always `Option` (for +/// example: almost all `new` methods can fail if the allocation failed), but +/// for convenience, if the return type is `Id`, this macro will +/// automatically unwrap the object, or panic with an error message if it +/// couldn't be retrieved. /// -/// Though as a special case, if the last argument is the marker `_`, the -/// macro will return a `Result, Id>`, see below. +/// As a special case, if the last argument is the marker `_`, the macro will +/// return a `Result, Id>`, see below. /// -/// This macro doesn't support super methods yet, see [#173]. /// The `retain`, `release` and `autorelease` selectors are not supported, use /// [`Id::retain`], [`Id::drop`] and [`Id::autorelease`] for that. /// @@ -1120,7 +1123,6 @@ macro_rules! msg_send_bool { /// [`MessageReceiver`]: crate::runtime::MessageReceiver /// [`Id::retain_autoreleased`]: crate::rc::Id::retain_autoreleased /// [arc-retainable]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#retainable-object-pointers-as-operands-and-arguments -/// [#173]: https://github.com/madsmtm/objc2/pull/173 /// [`Id::retain`]: crate::rc::Id::retain /// [`Id::drop`]: crate::rc::Id::drop /// [`Id::autorelease`]: crate::rc::Id::autorelease @@ -1187,6 +1189,34 @@ macro_rules! msg_send_bool { /// ``` #[macro_export] macro_rules! msg_send_id { + [super($obj:expr), $($selector_and_arguments:tt)+] => { + $crate::__msg_send_parse! { + (send_super_message_id_static_error) + () + () + ($($selector_and_arguments)+) + (send_super_message_id_static) + + ($crate::__msg_send_id_helper) + ($obj) + () // No retain semantics + (MsgSendSuperId) + } + }; + [super($obj:expr, $superclass:expr), $($selector_and_arguments:tt)+] => { + $crate::__msg_send_parse! { + (send_super_message_id_error) + () + () + ($($selector_and_arguments)+) + (send_super_message_id) + + ($crate::__msg_send_id_helper) + ($obj, $superclass) + () // No retain semantics + (MsgSendSuperId) + } + }; [$obj:expr, new $(,)?] => ({ let result; result = <$crate::__macro_helpers::New as $crate::__macro_helpers::MsgSendId<_, _>>::send_message_id( @@ -1221,6 +1251,7 @@ macro_rules! msg_send_id { ($crate::__msg_send_id_helper) ($obj) () // No retain semantics + (MsgSendId) } }; } @@ -1230,8 +1261,9 @@ macro_rules! msg_send_id { #[macro_export] macro_rules! __msg_send_id_helper { { - ($obj:expr) + ($($fn_args:tt)+) ($($retain_semantics:ident)?) + ($trait:ident) ($fn:ident) (retain) () @@ -1241,8 +1273,9 @@ macro_rules! __msg_send_id_helper { ) }}; { - ($obj:expr) + ($($fn_args:tt)+) ($($retain_semantics:ident)?) + ($trait:ident) ($fn:ident) (release) () @@ -1252,8 +1285,9 @@ macro_rules! __msg_send_id_helper { ) }}; { - ($obj:expr) + ($($fn_args:tt)+) ($($retain_semantics:ident)?) + ($trait:ident) ($fn:ident) (autorelease) () @@ -1263,8 +1297,9 @@ macro_rules! __msg_send_id_helper { ) }}; { - ($obj:expr) + ($($fn_args:tt)+) ($($retain_semantics:ident)?) + ($trait:ident) ($fn:ident) (dealloc) () @@ -1274,21 +1309,23 @@ macro_rules! __msg_send_id_helper { ) }}; { - ($obj:expr) + ($($fn_args:tt)+) ($retain_semantics:ident) + ($trait:ident) ($fn:ident) ($($selector:tt)*) ($($argument:expr,)*) } => ({ - <$crate::__macro_helpers::$retain_semantics as $crate::__macro_helpers::MsgSendId<_, _>>::$fn( - $obj, + <$crate::__macro_helpers::$retain_semantics as $crate::__macro_helpers::$trait<_, _>>::$fn( + $($fn_args)+, $crate::sel!($($selector)*), ($($argument,)*), ) }); { - ($obj:expr) + ($($fn_args:tt)+) () + ($trait:ident) ($fn:ident) ($($selector:tt)*) ($($argument:expr,)*) @@ -1300,8 +1337,8 @@ macro_rules! __msg_send_id_helper { let result; result = <$crate::__macro_helpers::RetainSemantics<{ $crate::__macro_helpers::retain_semantics(__SELECTOR_DATA) - }> as $crate::__macro_helpers::MsgSendId<_, _>>::$fn( - $obj, + }> as $crate::__macro_helpers::$trait<_, _>>::$fn( + $($fn_args)+, $crate::__sel_inner!( __SELECTOR_DATA, $crate::__hash_idents!($($selector)*) diff --git a/crates/objc2/src/rc/allocated.rs b/crates/objc2/src/rc/allocated.rs index fee507913..c77cdff22 100644 --- a/crates/objc2/src/rc/allocated.rs +++ b/crates/objc2/src/rc/allocated.rs @@ -1,10 +1,12 @@ -use core::fmt; use core::marker::PhantomData; use core::mem::ManuallyDrop; +use core::ptr::NonNull; +use core::{fmt, ptr}; +use crate::__macro_helpers::declared_ivars::initialize_ivars; use crate::mutability::IsMutable; use crate::runtime::{objc_release_fast, AnyObject}; -use crate::Message; +use crate::{DeclaredClass, Message}; /// An Objective-C object that has been allocated, but not initialized. /// @@ -38,7 +40,7 @@ pub struct Allocated { /// The yet-to-be initialized object. /// /// We don't use `Id` here, since that has different auto-trait impls, and - /// requires in it's safety contract that the object is initialized (which + /// requires in its safety contract that the object is initialized (which /// makes it difficult to ensure correctness if such things are split /// across different files). Additionally, we want to have fine control /// over NULL-ness. @@ -48,20 +50,20 @@ pub struct Allocated { /// Necessary for dropck, as with `Id`. p: PhantomData, /// Necessary for restricting auto traits. + /// + /// We _could_ probably implement auto traits `Send` and `Sync` here, but to be + /// safe, we won't for now. p_auto_traits: PhantomData, } -// We _could_ probably implement auto traits `Send` and `Sync` here, but to be -// safe, we won't for now. -// -// Explicitly don't implement `Deref`, `Message` nor `RefEncode`, though! +// Explicitly don't implement `Deref`, `Message` nor `RefEncode`. impl Allocated { /// # Safety /// - /// The caller must ensure the given object has +1 retain count, and that - /// the object behind the pointer has been allocated (but not yet - /// initialized), or that the pointer is NULL. + /// The caller must ensure the pointer is NULL, or that the given object + /// has +1 retain count, and that the object behind the pointer has been + /// allocated (but not yet initialized). #[inline] pub(crate) unsafe fn new(ptr: *mut T) -> Self { Self { @@ -123,6 +125,57 @@ impl Allocated { let this = ManuallyDrop::new(this); this.ptr as *mut T } + + /// Initialize the instance variables for this object. + /// + /// This consumes the allocated instance, and returns the now partially + /// initialized instance instead, which can be further used in + /// [`msg_send_id!`] `super` calls. + /// + /// [`msg_send_id!`]: crate::msg_send_id + /// + /// + /// # Panics + /// + /// If debug assertions are enabled, this function will panic if the + /// allocated instance is `NULL`, which usually only happens in Out of + /// Memory situations. + /// + /// If debug assertions are disabled, this will return a `NULL` instance + /// and the ivars will be dropped. The NULL instance cannot cause + /// unsoundness and will likely lead to an initialization failure later on + /// instead, but not panicking here is done as a code-size optimization. + // + // Note: This is intentionally _not_ an associated method, even though + // `Allocated` will become `MethodReceiver` in the future. + #[inline] + #[track_caller] + pub fn set_ivars(self, ivars: T::Ivars) -> PartialInit + where + T: DeclaredClass + Sized, + { + if let Some(ptr) = NonNull::new(ManuallyDrop::new(self).ptr as *mut T) { + // SAFETY: The pointer came from `self`, so it is valid. + unsafe { initialize_ivars::(ptr, ivars) }; + + // SAFETY: + // - The pointer came from a `ManuallyDrop>`, which means + // that we've now transfered ownership over +1 retain count. + // - The instance variables for this class have been intialized above. + unsafe { PartialInit::new(ptr.as_ptr()) } + } else if cfg!(debug_assertions) { + panic!("tried to initialize instance variables on a NULL allocated object") + } else { + // Explicitly drop the ivars in this branch + drop(ivars); + + // Create a new NULL PartialInit, which will likely be checked for + // NULL-ness later on, after initialization of it has failed. + // + // SAFETY: The pointer is NULL. + unsafe { PartialInit::new(ptr::null_mut()) } + } + } } impl Drop for Allocated { @@ -146,6 +199,117 @@ impl fmt::Pointer for Allocated { } } +/// An Objective-C object that has been allocated and initialized in the +/// current class, but not yet initialized in the superclass. +/// +/// This is returned by [`Allocated::set_ivars`], and is intended to be used +/// further in [`msg_send_id!`] `super` calls. +/// +/// [`msg_send_id!`]: crate::msg_send_id +/// +/// +/// # Memory layout +/// +/// The memory layout of this struct is NOT currently guaranteed, as we may +/// want to be able to move a drop flag to the stack in the future. +// +// Internally, this is very similar to `Allocated`, except that we have +// different guarantees on the validity of the object. +#[repr(transparent)] +#[derive(Debug)] +pub struct PartialInit { + /// The partially initialized object. + /// + /// Variance is same as `Id`. + ptr: *const T, // Intentionally not NonNull + /// Necessary for dropck, as with `Id`. + p: PhantomData, + /// Restrict auto traits, same as `Allocated`. + p_auto_traits: PhantomData, +} + +impl PartialInit { + /// # Safety + /// + /// The caller must ensure the pointer is NULL, or that the given object + /// is allocated, has +1 retain count, and that the class' instance + /// variables have been initialized. + #[inline] + pub(crate) unsafe fn new(ptr: *mut T) -> Self { + Self { + ptr, + p: PhantomData, + p_auto_traits: PhantomData, + } + } + + /// Returns a raw pointer to the object. + /// + /// The pointer is valid for at least as long as the `PartialInit` is + /// held. + /// + /// See [`PartialInit::as_mut_ptr`] for the mutable equivalent. + /// + /// This is an associated method, and must be called as + /// `PartialInit::as_ptr(obj)`. + #[inline] + pub fn as_ptr(this: &Self) -> *const T { + this.ptr + } + + /// Returns a raw mutable pointer to the object. + /// + /// The pointer is valid for at least as long as the `PartialInit` is + /// held. + /// + /// See [`PartialInit::as_ptr`] for the immutable equivalent. + /// + /// This is an associated method, and must be called as + /// `PartialInit::as_mut_ptr(obj)`. + /// + /// + /// # Note about mutable references + /// + /// See [`Allocated::as_mut_ptr`] for details on when mutable references + /// are safe. + #[inline] + #[allow(unknown_lints)] // New lint below + #[allow(clippy::needless_pass_by_ref_mut)] + pub fn as_mut_ptr(this: &mut Self) -> *mut T + where + T: IsMutable, + { + this.ptr as *mut T + } + + #[inline] + pub(crate) fn into_ptr(this: Self) -> *mut T { + let this = ManuallyDrop::new(this); + this.ptr as *mut T + } +} + +impl Drop for PartialInit { + #[inline] + fn drop(&mut self) { + // SAFETY: Partially initialized objects can always safely be + // released, since destructors are written to take into account that + // the object may not have been fully initialized. + // + // This is also safe in the case where the object is NULL, + // since `objc_release` allows NULL pointers. + // + // Rest is same as `Id`. + unsafe { objc_release_fast(self.ptr as *mut _) }; + } +} + +impl fmt::Pointer for PartialInit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.ptr, f) + } +} + #[cfg(test)] mod tests { use core::panic::{RefUnwindSafe, UnwindSafe}; @@ -158,6 +322,7 @@ mod tests { #[test] fn auto_traits() { assert_not_impl_any!(Allocated<()>: Send, Sync, UnwindSafe, RefUnwindSafe, Unpin); + assert_not_impl_any!(PartialInit<()>: Send, Sync, UnwindSafe, RefUnwindSafe, Unpin); } #[repr(C)] @@ -168,7 +333,15 @@ mod tests { /// Test that `Allocated` is covariant over `T`. #[allow(unused)] - fn assert_variance<'b>(obj: Allocated>) -> Allocated> { + fn assert_allocated_variance<'b>(obj: Allocated>) -> Allocated> { + obj + } + + /// Test that `PartialInit` is covariant over `T`. + #[allow(unused)] + fn assert_partialinit_variance<'b>( + obj: PartialInit>, + ) -> PartialInit> { obj } } diff --git a/crates/objc2/src/rc/id.rs b/crates/objc2/src/rc/id.rs index 7398c9c2d..3714462de 100644 --- a/crates/objc2/src/rc/id.rs +++ b/crates/objc2/src/rc/id.rs @@ -837,7 +837,7 @@ mod tests { use crate::mutability::{Immutable, Mutable}; use crate::rc::{__RcTestObject, __ThreadTestData, autoreleasepool}; use crate::runtime::{AnyObject, NSObject}; - use crate::{declare_class, msg_send}; + use crate::{declare_class, msg_send, DeclaredClass}; #[test] fn auto_traits() { @@ -851,6 +851,8 @@ mod tests { type Mutability = $mutability; const NAME: &'static str = concat!(stringify!($name), "Test"); } + + impl DeclaredClass for $name {} ); }; } @@ -906,7 +908,7 @@ mod tests { drop(obj); expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; expected.assert_current(); } @@ -932,7 +934,7 @@ mod tests { expected.assert_current(); }); expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; expected.assert_current(); } @@ -970,7 +972,7 @@ mod tests { drop(cloned); expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; expected.assert_current(); } diff --git a/crates/objc2/src/rc/id_traits.rs b/crates/objc2/src/rc/id_traits.rs index 4ab267273..2bef09a79 100644 --- a/crates/objc2/src/rc/id_traits.rs +++ b/crates/objc2/src/rc/id_traits.rs @@ -130,7 +130,7 @@ mod tests { use super::*; use crate::mutability::Mutable; use crate::runtime::NSObject; - use crate::{declare_class, msg_send_id, ClassType}; + use crate::{declare_class, msg_send_id, ClassType, DeclaredClass}; declare_class!( #[derive(PartialEq, Eq, Hash, Debug)] @@ -141,6 +141,8 @@ mod tests { type Mutability = Mutable; const NAME: &'static str = "MyCustomCollection"; } + + impl DeclaredClass for Collection {} ); impl DefaultId for Collection { diff --git a/crates/objc2/src/rc/mod.rs b/crates/objc2/src/rc/mod.rs index 4c6dc0953..968707987 100644 --- a/crates/objc2/src/rc/mod.rs +++ b/crates/objc2/src/rc/mod.rs @@ -56,7 +56,7 @@ mod id_traits; mod test_object; mod weak_id; -pub use self::allocated::Allocated; +pub use self::allocated::{Allocated, PartialInit}; pub use self::autorelease::{ autoreleasepool, autoreleasepool_leaking, AutoreleasePool, AutoreleaseSafe, }; diff --git a/crates/objc2/src/rc/test_object.rs b/crates/objc2/src/rc/test_object.rs index 202de9997..98bc46f41 100644 --- a/crates/objc2/src/rc/test_object.rs +++ b/crates/objc2/src/rc/test_object.rs @@ -4,7 +4,7 @@ use core::ptr; use super::{Allocated, Id}; use crate::mutability::Immutable; use crate::runtime::{NSObject, NSZone}; -use crate::{declare_class, msg_send, msg_send_id, ClassType}; +use crate::{declare_class, msg_send, msg_send_id, ClassType, DeclaredClass}; // TODO: Put tests that use this in another crate #[derive(Debug, Clone, Default, PartialEq, Eq)] @@ -12,7 +12,7 @@ use crate::{declare_class, msg_send, msg_send_id, ClassType}; #[doc(hidden)] pub struct __ThreadTestData { pub alloc: usize, - pub dealloc: usize, + pub drop: usize, pub init: usize, pub retain: usize, pub copy: usize, @@ -70,6 +70,8 @@ declare_class!( const NAME: &'static str = "__RcTestObject"; } + impl DeclaredClass for __RcTestObject {} + unsafe impl __RcTestObject { #[method_id(newReturningNull)] fn new_returning_null() -> Option> { @@ -106,10 +108,11 @@ declare_class!( ptr::null_mut() } - #[method(init)] - unsafe fn init(this: *mut Self) -> *mut Self { + #[method_id(init)] + unsafe fn init(this: Allocated) -> Id { TEST_DATA.with(|data| data.borrow_mut().init += 1); - unsafe { msg_send![super(this), init] } + let this = this.set_ivars(()); + unsafe { msg_send_id![super(this), init] } } #[method_id(initReturningNull)] @@ -286,7 +289,7 @@ declare_class!( impl Drop for __RcTestObject { fn drop(&mut self) { - TEST_DATA.with(|data| data.borrow_mut().dealloc += 1); + TEST_DATA.with(|data| data.borrow_mut().drop += 1); } } @@ -311,6 +314,8 @@ declare_class!( type Mutability = Immutable; const NAME: &'static str = "RcTestObjectSubclass"; } + + impl DeclaredClass for RcTestObjectSubclass {} ); #[cfg_attr(not(test), allow(unused))] @@ -357,7 +362,7 @@ mod tests { drop(res); $expected.release += 1; - $expected.dealloc += 1; + $expected.drop += 1; $expected.assert_current(); } } @@ -429,7 +434,7 @@ mod tests { drop(res); $expected.release += 1; - $expected.dealloc += 1; + $expected.drop += 1; $expected.assert_current(); // Errors @@ -449,7 +454,7 @@ mod tests { drop(res); $expected.release += 1; - $expected.dealloc += 1; + $expected.drop += 1; $expected.assert_current(); } } @@ -474,11 +479,11 @@ mod tests { expected.alloc -= 1; expected.release -= 1; - expected.dealloc -= 1; test_error_id!(expected, 0, initAndShouldError, { expected.alloc += 1; expected.release += 1; - expected.dealloc += 1; + // Drop flag ensures newly allocated objects do not drop + // expected.drop += 1; __RcTestObject::alloc() }); } @@ -499,7 +504,8 @@ mod tests { drop(res); expected.release += 1; - expected.dealloc += 1; + // Drop flag ensures uninitialized do not drop + // expected.drop += 1; expected.assert_current(); // Errors @@ -521,7 +527,7 @@ mod tests { drop(res); expected.release += 1; - expected.dealloc += 1; + expected.drop += 1; expected.assert_current(); } diff --git a/crates/objc2/src/rc/weak_id.rs b/crates/objc2/src/rc/weak_id.rs index 9f13e1211..48bd5f42f 100644 --- a/crates/objc2/src/rc/weak_id.rs +++ b/crates/objc2/src/rc/weak_id.rs @@ -210,7 +210,7 @@ mod tests { drop(obj); drop(strong); expected.release += 2; - expected.dealloc += 1; + expected.drop += 1; expected.assert_current(); if cfg!(not(feature = "gnustep-1-7")) { diff --git a/crates/objc2/src/runtime/bool.rs b/crates/objc2/src/runtime/bool.rs index 74b4bf9c0..e97a3b355 100644 --- a/crates/objc2/src/runtime/bool.rs +++ b/crates/objc2/src/runtime/bool.rs @@ -10,7 +10,9 @@ use crate::ffi; /// soon as possible. /// /// This is FFI-safe and can be used directly with `msg_send!` and `extern` -/// functions. +/// functions as a substitute for `BOOL` in Objective-C. If your Objective-C +/// code uses C99 `_Bool`, you should use a `#[repr(transparent)]` wrapper +/// around `bool` instead. /// /// Note that this is able to contain more states than `bool` on some /// platforms, but these cases should not be relied on! diff --git a/crates/objc2/src/runtime/message_receiver.rs b/crates/objc2/src/runtime/message_receiver.rs index bc1dc6b4e..d2cce6a72 100644 --- a/crates/objc2/src/runtime/message_receiver.rs +++ b/crates/objc2/src/runtime/message_receiver.rs @@ -510,7 +510,7 @@ mod tests { use crate::rc::{Allocated, Id}; use crate::runtime::NSObject; use crate::test_utils; - use crate::{declare_class, msg_send, msg_send_id, ClassType}; + use crate::{declare_class, msg_send, msg_send_id, ClassType, DeclaredClass}; declare_class!( struct MutableObject; @@ -520,6 +520,8 @@ mod tests { type Mutability = mutability::Mutable; const NAME: &'static str = "TestMutableObject"; } + + impl DeclaredClass for MutableObject {} ); #[allow(unused)] diff --git a/crates/objc2/src/runtime/protocol_object.rs b/crates/objc2/src/runtime/protocol_object.rs index ae8c8d085..46ff46c29 100644 --- a/crates/objc2/src/runtime/protocol_object.rs +++ b/crates/objc2/src/runtime/protocol_object.rs @@ -194,7 +194,7 @@ mod tests { use super::*; use crate::mutability::Mutable; use crate::runtime::{NSObject, NSObjectProtocol}; - use crate::{declare_class, extern_methods, extern_protocol, ClassType}; + use crate::{declare_class, extern_methods, extern_protocol, ClassType, DeclaredClass}; extern_protocol!( unsafe trait Foo { @@ -254,6 +254,8 @@ mod tests { const NAME: &'static str = "ProtocolTestsDummyClass"; } + impl DeclaredClass for DummyClass {} + unsafe impl NSObjectProtocol for DummyClass {} ); @@ -350,7 +352,7 @@ mod tests { assert_eq!( format!("{obj:?}"), format!( - "DummyClass {{ __superclass: {:?} }}", + "DummyClass {{ __superclass: {:?}, __ivars: PhantomData<()> }}", ManuallyDrop::new(foobar) ), ); diff --git a/crates/objc2/src/top_level_traits.rs b/crates/objc2/src/top_level_traits.rs index b852fb182..8af61e070 100644 --- a/crates/objc2/src/top_level_traits.rs +++ b/crates/objc2/src/top_level_traits.rs @@ -1,3 +1,6 @@ +use core::ptr::NonNull; + +use crate::__macro_helpers::declared_ivars::get_initialized_ivar_ptr; use crate::encode::RefEncode; use crate::msg_send_id; use crate::mutability::{IsAllocableAnyThread, IsRetainable, Mutability}; @@ -307,6 +310,62 @@ pub unsafe trait ClassType: Message { // TODO: `fn mtm(&self) -> MainThreadMarker where T::Mutability: MainThreadOnly` } +/// Marks types whose implementation is defined in Rust. +/// +/// This is used in [`declare_class!`], and allows access to the instance +/// variables that a given type declares, see that macro for details. +/// +/// [`declare_class!`]: crate::declare_class +// +// Note: We mark this trait as not `unsafe` for better documentation, since +// implementing it inside `declare_class!` is not `unsafe`. +// +// Safety is ensured by `__UNSAFE_OFFSETS_CORRECT`. +pub trait DeclaredClass: ClassType { + /// A type representing the instance variables that this class carries. + type Ivars: Sized; + + // TODO: Add `ivars_ptr(this: NonNull) -> NonNull`? + + /// Get a reference to the instance variable data that this object + /// carries. + #[inline] + fn ivars(&self) -> &Self::Ivars { + let ptr: NonNull = NonNull::from(self); + // SAFETY: The pointer is valid and initialized. + let ivars = unsafe { get_initialized_ivar_ptr(ptr) }; + // SAFETY: The lifetime of the instance variable is tied to the object. + unsafe { ivars.as_ref() } + } + + /// Get a mutable reference to the instance variable data that this object + /// carries. + #[inline] + fn ivars_mut(&mut self) -> &mut Self::Ivars { + let ptr: NonNull = NonNull::from(self); + // SAFETY: The pointer is valid and initialized. + let mut ivars = unsafe { get_initialized_ivar_ptr(ptr) }; + // SAFETY: The lifetime of the instance variable is tied to the object. + // + // Mutability is safe since the object itself is mutable. See + // `ClassType::as_super_mut` for why this is safe without + // `Self: IsMutable`. + unsafe { ivars.as_mut() } + } + + #[doc(hidden)] + fn __ivars_offset() -> isize; + + #[doc(hidden)] + fn __drop_flag_offset() -> isize; + + /// # Safety + /// + /// The ivar offset and drop flag offsets must be implemented correctly. + #[doc(hidden)] + const __UNSAFE_OFFSETS_CORRECT: (); +} + /// Marks types that represent specific protocols. /// /// This is the protocol equivalent of [`ClassType`]. diff --git a/crates/objc2/tests/declare_class.rs b/crates/objc2/tests/declare_class.rs index 08a9d0622..c5933d8b2 100644 --- a/crates/objc2/tests/declare_class.rs +++ b/crates/objc2/tests/declare_class.rs @@ -1,17 +1,15 @@ #![deny(deprecated, unreachable_code)] use core::ptr::{self, NonNull}; -use objc2::declare::IvarEncode; use objc2::mutability::Immutable; use objc2::rc::Id; use objc2::runtime::NSObject; -use objc2::{declare_class, extern_methods, sel, ClassType}; +use objc2::{declare_class, extern_methods, sel, ClassType, DeclaredClass}; // Test that adding the `deprecated` attribute does not mean that warnings // when using the method internally are output. declare_class!( - // Also ensure that empty fields still work - struct DeclareClassDepreactedMethod {} + struct DeclareClassDepreactedMethod; unsafe impl ClassType for DeclareClassDepreactedMethod { type Super = NSObject; @@ -19,6 +17,8 @@ declare_class!( const NAME: &'static str = "DeclareClassDepreactedMethod"; } + impl DeclaredClass for DeclareClassDepreactedMethod {} + #[deprecated] unsafe impl DeclareClassDepreactedMethod { #[method(deprecatedOnImpl)] @@ -50,6 +50,8 @@ declare_class!( const NAME: &'static str = "DeclareClassCfg"; } + impl DeclaredClass for DeclareClassCfg {} + unsafe impl DeclareClassCfg { #[cfg(debug_assertions)] #[method(changesOnCfg1)] @@ -192,6 +194,8 @@ declare_class!( const NAME: &'static str = "TestMultipleColonSelector"; } + impl DeclaredClass for TestMultipleColonSelector {} + unsafe impl TestMultipleColonSelector { #[method(test::arg3:)] fn _test_class(arg1: i32, arg2: i32, arg3: i32) -> i32 { @@ -265,6 +269,8 @@ declare_class!( const NAME: &'static str = "DeclareClassAllTheBool"; } + impl DeclaredClass for DeclareClassAllTheBool {} + unsafe impl DeclareClassAllTheBool { #[method(returnsBool)] fn returns_bool() -> bool { @@ -328,6 +334,8 @@ declare_class!( const NAME: &'static str = "DeclareClassUnreachable"; } + impl DeclaredClass for DeclareClassUnreachable {} + // Ensure none of these warn unsafe impl DeclareClassUnreachable { #[method(unreachable)] @@ -367,27 +375,6 @@ fn test_unreachable() { let _ = DeclareClassUnreachable::class(); } -#[test] -#[should_panic = "failed to add ivar _ivar"] -fn test_duplicate_ivar() { - declare_class!( - struct DeclareClassDuplicateIvar { - ivar1: IvarEncode, - ivar2: IvarEncode, - } - - mod ivars; - - unsafe impl ClassType for DeclareClassDuplicateIvar { - type Super = NSObject; - type Mutability = Immutable; - const NAME: &'static str = "DeclareClassDuplicateIvar"; - } - ); - - let _ = DeclareClassDuplicateIvar::class(); -} - declare_class!( #[derive(Debug)] struct OutParam; @@ -398,6 +385,8 @@ declare_class!( const NAME: &'static str = "OutParam"; } + impl DeclaredClass for OutParam {} + unsafe impl OutParam { #[method(unsupported1:)] fn _unsupported1(_param: &mut Id) {} @@ -486,6 +475,8 @@ fn test_pointer_receiver_allowed() { const NAME: &'static str = "PointerReceiver"; } + impl DeclaredClass for PointerReceiver {} + unsafe impl PointerReceiver { #[method(constPtr)] fn const_ptr(_this: *const Self) {} diff --git a/crates/objc2/tests/declare_class_self.rs b/crates/objc2/tests/declare_class_self.rs index 6915e5f0f..6355e8429 100644 --- a/crates/objc2/tests/declare_class_self.rs +++ b/crates/objc2/tests/declare_class_self.rs @@ -3,7 +3,7 @@ //! do it in a context where `Self` works. use objc2::rc::{Allocated, Id}; use objc2::runtime::NSObject; -use objc2::{declare_class, mutability, ClassType}; +use objc2::{declare_class, mutability, ClassType, DeclaredClass}; trait GetSameType { type SameType: ?Sized; @@ -37,6 +37,8 @@ declare_class!( const NAME: &'static str = "MyTestObject"; } + impl DeclaredClass for MyTestObject {} + unsafe impl MyTestObject { #[method_id(initWith:)] fn init( diff --git a/crates/objc2/tests/macros_mainthreadmarker.rs b/crates/objc2/tests/macros_mainthreadmarker.rs index dff31e4c4..bf67b8614 100644 --- a/crates/objc2/tests/macros_mainthreadmarker.rs +++ b/crates/objc2/tests/macros_mainthreadmarker.rs @@ -1,6 +1,9 @@ use objc2::rc::Id; use objc2::runtime::{NSObject, NSObjectProtocol}; -use objc2::{declare_class, extern_methods, extern_protocol, mutability, ClassType, ProtocolType}; +use objc2::{ + declare_class, extern_methods, extern_protocol, mutability, ClassType, DeclaredClass, + ProtocolType, +}; extern_protocol!( #[allow(clippy::missing_safety_doc)] @@ -27,6 +30,8 @@ declare_class!( const NAME: &'static str = "MainThreadMarkerTest"; } + impl DeclaredClass for Cls {} + unsafe impl Proto for Cls { #[method(myMethod:)] fn _my_mainthreadonly_method(arg: i32) -> i32 { diff --git a/crates/objc2/tests/no_prelude.rs b/crates/objc2/tests/no_prelude.rs index cf79c10f5..c09ab224d 100644 --- a/crates/objc2/tests/no_prelude.rs +++ b/crates/objc2/tests/no_prelude.rs @@ -9,7 +9,7 @@ extern crate objc2 as new_objc2; -use new_objc2::{ClassType, ProtocolType}; +use new_objc2::{ClassType, DeclaredClass, ProtocolType}; mod core {} mod std {} @@ -82,14 +82,16 @@ type ExactSizeIterator = BogusType; type SliceConcatExt = BogusType; type ToString = BogusType; +type PhantomData = BogusType; + // Test begin below this line -type PhantomData = T; +pub struct MyCustomIvars { + ivars: i32, +} new_objc2::declare_class!( - pub struct CustomObject { - field1: PhantomData, - } + pub struct CustomObject; unsafe impl ClassType for CustomObject { type Super = new_objc2::runtime::NSObject; @@ -97,6 +99,10 @@ new_objc2::declare_class!( const NAME: &'static str = "CustomObject"; } + impl DeclaredClass for CustomObject { + type Ivars = MyCustomIvars; + } + unsafe impl CustomObject { #[method(a)] fn _a() {} diff --git a/crates/objc2/tests/track_caller.rs b/crates/objc2/tests/track_caller.rs index f50b32984..29bc8a47c 100644 --- a/crates/objc2/tests/track_caller.rs +++ b/crates/objc2/tests/track_caller.rs @@ -9,7 +9,7 @@ use std::sync::Mutex; use objc2::encode::Encode; use objc2::rc::{Allocated, Id, __RcTestObject}; use objc2::runtime::NSObject; -use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType}; +use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass}; static EXPECTED_MESSAGE: Mutex = Mutex::new(String::new()); static EXPECTED_LINE: Mutex = Mutex::new(0); @@ -202,6 +202,8 @@ declare_class!( const NAME: &'static str = "PanickingClass"; } + impl DeclaredClass for PanickingClass {} + unsafe impl PanickingClass { #[method(panic)] fn _panic() -> *mut Self { diff --git a/crates/objc2/tests/use_macros.rs b/crates/objc2/tests/use_macros.rs index 081b1e9f7..ce2ffe197 100644 --- a/crates/objc2/tests/use_macros.rs +++ b/crates/objc2/tests/use_macros.rs @@ -1,6 +1,6 @@ use objc2::mutability::Immutable; use objc2::runtime::{AnyClass, NSObject}; -use objc2::{class, declare_class, msg_send, sel, ClassType}; +use objc2::{class, declare_class, msg_send, sel, ClassType, DeclaredClass}; declare_class!( pub struct MyObject; @@ -10,6 +10,8 @@ declare_class!( type Mutability = Immutable; const NAME: &'static str = "MyObject"; } + + impl DeclaredClass for MyObject {} ); #[test] diff --git a/crates/test-assembly/crates/test_declare_class/expected/apple-aarch64.s b/crates/test-assembly/crates/test_declare_class/expected/apple-aarch64.s index 8e8074c7c..0609a5b5d 100644 --- a/crates/test-assembly/crates/test_declare_class/expected/apple-aarch64.s +++ b/crates/test-assembly/crates/test_declare_class/expected/apple-aarch64.s @@ -11,6 +11,55 @@ SYM(core[CRATE_ID]::ptr::drop_in_place::<::call SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0): ret + .p2align 2 +SYM(objc2[CRATE_ID]::__macro_helpers::declared_ivars::dealloc::, 0): + sub sp, sp, #64 + stp x22, x21, [sp, #16] + stp x20, x19, [sp, #32] + stp x29, x30, [sp, #48] + add x29, sp, #48 + mov x19, x1 + mov x20, x0 +Lloh0: + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0)@PAGE +Lloh1: + ldr x8, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0)@PAGEOFF] + ldrb w8, [x0, x8] + cbz w8, LBB3_5 + cmp w8, #255 + b.ne LBB3_3 + bl SYM(::drop, 0) +LBB3_3: +Lloh2: + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)@PAGE +Lloh3: + ldr x8, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)@PAGEOFF] + add x8, x20, x8 + ldp x0, x21, [x8] + bl _objc_release + cbz x21, LBB3_5 + mov x0, x21 + bl _objc_release +LBB3_5: +Lloh4: + adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE +Lloh5: + ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] +Lloh6: + ldr x8, [x8] + stp x20, x8, [sp] + mov x0, sp + mov x1, x19 + bl _objc_msgSendSuper + ldp x29, x30, [sp, #48] + ldp x20, x19, [sp, #32] + ldp x22, x21, [sp, #16] + add sp, sp, #64 + ret + .loh AdrpLdr Lloh0, Lloh1 + .loh AdrpLdr Lloh2, Lloh3 + .loh AdrpLdrGotLdr Lloh4, Lloh5, Lloh6 + .p2align 2 SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0): sub sp, sp, #64 @@ -21,847 +70,828 @@ SYM(::call_once::<::call_once::<::class::{closure#0}>::{closure#0}, 0): - sub sp, sp, #48 - stp x20, x19, [sp, #16] - stp x29, x30, [sp, #32] - add x29, sp, #32 + sub sp, sp, #96 + stp x20, x19, [sp, #64] + stp x29, x30, [sp, #80] + add x29, sp, #80 ldr x8, [x0] ldrb w9, [x8] strb wzr, [x8] - cbz w9, LBB4_3 -Lloh55: - adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE -Lloh56: - ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] -Lloh57: - ldr x2, [x8] -Lloh58: - adrp x0, l_anon.[ID].16@PAGE -Lloh59: - add x0, x0, l_anon.[ID].16@PAGEOFF - mov w1, #9 - bl SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) - cbz x0, LBB4_4 - str x0, [sp, #8] -Lloh60: - adrp x1, l_anon.[ID].18@PAGE -Lloh61: - add x1, x1, l_anon.[ID].18@PAGEOFF -Lloh62: - adrp x19, l_anon.[ID].4@PAGE -Lloh63: - add x19, x19, l_anon.[ID].4@PAGEOFF - add x0, sp, #8 - mov w2, #4 - mov w3, #8 - mov w4, #3 - mov x5, x19 - bl SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) -Lloh64: - adrp x1, l_anon.[ID].17@PAGE + cbz w9, LBB5_5 Lloh65: - add x1, x1, l_anon.[ID].17@PAGEOFF - add x0, sp, #8 - mov w2, #11 - mov w3, #8 - mov w4, #3 - mov x5, x19 - bl SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) + adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE Lloh66: - adrp x8, L_OBJC_SELECTOR_REFERENCES_dealloc@GOTPAGE + ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] Lloh67: - ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_dealloc@GOTPAGEOFF] + ldr x2, [x8] Lloh68: - ldr x1, [x8] + adrp x0, l_anon.[ID].23@PAGE Lloh69: - adrp x20, l_anon.[ID].3@PAGE + add x0, x0, l_anon.[ID].23@PAGEOFF + mov w1, #9 + bl SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) + cbz x0, LBB5_6 + str x0, [sp, #24] Lloh70: - add x20, x20, l_anon.[ID].3@PAGEOFF + adrp x8, L_OBJC_SELECTOR_REFERENCES_dealloc@GOTPAGE Lloh71: - adrp x4, l_anon.[ID].5@PAGE + ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_dealloc@GOTPAGEOFF] Lloh72: - add x4, x4, l_anon.[ID].5@PAGEOFF + ldr x1, [x8] Lloh73: - adrp x5, SYM(::class::{closure#0}::__objc2_dealloc, 0)@PAGE + adrp x19, l_anon.[ID].3@PAGE Lloh74: - add x5, x5, SYM(::class::{closure#0}::__objc2_dealloc, 0)@PAGEOFF - add x0, sp, #8 - mov x2, x20 - mov x3, #0 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) + add x19, x19, l_anon.[ID].3@PAGEOFF Lloh75: - adrp x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGE + adrp x4, l_anon.[ID].5@PAGE Lloh76: - ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGEOFF] + add x4, x4, l_anon.[ID].5@PAGEOFF Lloh77: - ldr x1, [x8] + adrp x5, SYM(objc2[CRATE_ID]::__macro_helpers::declared_ivars::dealloc::, 0)@PAGE Lloh78: - adrp x5, _init_drop_ivars@PAGE -Lloh79: - add x5, x5, _init_drop_ivars@PAGEOFF - add x0, sp, #8 - mov x2, x20 + add x5, x5, SYM(objc2[CRATE_ID]::__macro_helpers::declared_ivars::dealloc::, 0)@PAGEOFF + add x0, sp, #24 + mov x2, x19 mov x3, #0 - mov x4, x19 bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - ldr x0, [sp, #8] - bl SYM(objc2::declare::ClassBuilder::register::GENERATED_ID, 0) - ldp x29, x30, [sp, #32] - ldp x20, x19, [sp, #16] - add sp, sp, #48 - ret -LBB4_3: + ldr x8, [sp, #24] + str x8, [sp, #8] +Lloh79: + adrp x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGE Lloh80: - adrp x0, l_anon.[ID].11@PAGE + ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGEOFF] Lloh81: - add x0, x0, l_anon.[ID].11@PAGEOFF + ldr x1, [x8] Lloh82: - adrp x2, l_anon.[ID].13@PAGE + adrp x4, l_anon.[ID].4@PAGE Lloh83: - add x2, x2, l_anon.[ID].13@PAGEOFF - mov w1, #43 - bl SYM(core::panicking::panic::GENERATED_ID, 0) -LBB4_4: + add x4, x4, l_anon.[ID].4@PAGEOFF Lloh84: - adrp x0, l_anon.[ID].16@PAGE + adrp x5, _init_drop_ivars@PAGE Lloh85: - add x0, x0, l_anon.[ID].16@PAGEOFF + add x5, x5, _init_drop_ivars@PAGEOFF + add x0, sp, #8 + mov x2, x19 + mov x3, #0 + bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) + ldr x8, [sp, #8] + str x8, [sp, #16] + mov w8, #16 Lloh86: - adrp x2, l_anon.[ID].28@PAGE + adrp x9, l_anon.[ID].15@PAGE Lloh87: - add x2, x2, l_anon.[ID].28@PAGEOFF - mov w1, #9 - bl SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) - .loh AdrpAdd Lloh58, Lloh59 - .loh AdrpLdrGotLdr Lloh55, Lloh56, Lloh57 - .loh AdrpAdd Lloh78, Lloh79 - .loh AdrpLdrGotLdr Lloh75, Lloh76, Lloh77 - .loh AdrpAdd Lloh73, Lloh74 - .loh AdrpAdd Lloh71, Lloh72 - .loh AdrpAdd Lloh69, Lloh70 - .loh AdrpLdrGotLdr Lloh66, Lloh67, Lloh68 - .loh AdrpAdd Lloh64, Lloh65 - .loh AdrpAdd Lloh62, Lloh63 - .loh AdrpAdd Lloh60, Lloh61 - .loh AdrpAdd Lloh82, Lloh83 - .loh AdrpAdd Lloh80, Lloh81 - .loh AdrpAdd Lloh86, Lloh87 - .loh AdrpAdd Lloh84, Lloh85 - - .p2align 2 -SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0): - sub sp, sp, #48 - stp x20, x19, [sp, #16] - stp x29, x30, [sp, #32] - add x29, sp, #32 - ldr x8, [x0] - ldrb w9, [x8] - strb wzr, [x8] - cbz w9, LBB5_3 + add x9, x9, l_anon.[ID].15@PAGEOFF + stp x8, x9, [sp, #32] + mov w8, #27 + strb w8, [sp, #24] Lloh88: - adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE + adrp x20, l_anon.[ID].11@PAGE Lloh89: - ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] + add x20, x20, l_anon.[ID].11@PAGEOFF + add x0, sp, #16 + add x5, sp, #24 + mov x1, x20 + mov w2, #5 + mov w3, #16 + mov w4, #3 + bl SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) Lloh90: - ldr x2, [x8] + adrp x1, l_anon.[ID].12@PAGE Lloh91: - adrp x0, l_anon.[ID].14@PAGE + add x1, x1, l_anon.[ID].12@PAGEOFF Lloh92: - add x0, x0, l_anon.[ID].14@PAGEOFF - mov w1, #15 - bl SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) - cbz x0, LBB5_4 - str x0, [sp, #8] + adrp x5, l_anon.[ID].13@PAGE Lloh93: - adrp x1, l_anon.[ID].20@PAGE -Lloh94: - add x1, x1, l_anon.[ID].20@PAGEOFF -Lloh95: - adrp x5, l_anon.[ID].25@PAGE -Lloh96: - add x5, x5, l_anon.[ID].25@PAGEOFF - add x0, sp, #8 - mov w2, #4 + add x5, x5, l_anon.[ID].13@PAGEOFF + add x0, sp, #16 + mov w2, #9 mov w3, #1 mov w4, #0 bl SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) + ldr x0, [sp, #16] + bl SYM(objc2::declare::ClassBuilder::register::GENERATED_ID, 0) + mov x19, x0 + mov x1, x20 + mov w2, #5 + bl SYM(objc2::runtime::AnyClass::instance_variable::GENERATED_ID, 0) + cbz x0, LBB5_7 + bl _ivar_getOffset + mov x20, x0 +Lloh94: + adrp x1, l_anon.[ID].12@PAGE +Lloh95: + add x1, x1, l_anon.[ID].12@PAGEOFF + mov x0, x19 + mov w2, #9 + bl SYM(objc2::runtime::AnyClass::instance_variable::GENERATED_ID, 0) + cbz x0, LBB5_8 + bl _ivar_getOffset +Lloh96: + adrp x8, __MergedGlobals@PAGE+32 + str x19, [x8, __MergedGlobals@PAGEOFF+32] Lloh97: - adrp x1, l_anon.[ID].19@PAGE + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)@PAGE + str x20, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)@PAGEOFF] Lloh98: - add x1, x1, l_anon.[ID].19@PAGEOFF + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0)@PAGE + str x0, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0)@PAGEOFF] + ldp x29, x30, [sp, #80] + ldp x20, x19, [sp, #64] + add sp, sp, #96 + ret +LBB5_5: Lloh99: - adrp x5, l_anon.[ID].26@PAGE + adrp x0, l_anon.[ID].24@PAGE Lloh100: - add x5, x5, l_anon.[ID].26@PAGEOFF - add x0, sp, #8 - mov w2, #4 - mov w3, #4 - mov w4, #2 - bl SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) + add x0, x0, l_anon.[ID].24@PAGEOFF Lloh101: - adrp x8, L_OBJC_SELECTOR_REFERENCES_dealloc@GOTPAGE + adrp x2, l_anon.[ID].26@PAGE Lloh102: - ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_dealloc@GOTPAGEOFF] + add x2, x2, l_anon.[ID].26@PAGEOFF + mov w1, #43 + bl SYM(core::panicking::panic::GENERATED_ID, 0) +LBB5_6: Lloh103: - ldr x1, [x8] + adrp x0, l_anon.[ID].23@PAGE Lloh104: - adrp x19, l_anon.[ID].3@PAGE + add x0, x0, l_anon.[ID].23@PAGEOFF Lloh105: - add x19, x19, l_anon.[ID].3@PAGEOFF + adrp x2, l_anon.[ID].32@PAGE Lloh106: - adrp x4, l_anon.[ID].5@PAGE + add x2, x2, l_anon.[ID].32@PAGEOFF + mov w1, #9 + bl SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) +LBB5_7: Lloh107: - add x4, x4, l_anon.[ID].5@PAGEOFF + adrp x0, l_anon.[ID].16@PAGE Lloh108: - adrp x5, SYM(::class::{closure#0}::__objc2_dealloc, 0)@PAGE + add x0, x0, l_anon.[ID].16@PAGEOFF Lloh109: - add x5, x5, SYM(::class::{closure#0}::__objc2_dealloc, 0)@PAGEOFF - add x0, sp, #8 - mov x2, x19 - mov x3, #0 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) + adrp x2, l_anon.[ID].18@PAGE Lloh110: - adrp x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGE + add x2, x2, l_anon.[ID].18@PAGEOFF + mov w1, #59 + bl SYM(core::option::expect_failed::GENERATED_ID, 0) +LBB5_8: Lloh111: - ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGEOFF] + adrp x0, l_anon.[ID].19@PAGE Lloh112: - ldr x1, [x8] + add x0, x0, l_anon.[ID].19@PAGEOFF Lloh113: - adrp x4, l_anon.[ID].4@PAGE + adrp x2, l_anon.[ID].20@PAGE Lloh114: - add x4, x4, l_anon.[ID].4@PAGEOFF + add x2, x2, l_anon.[ID].20@PAGEOFF + mov w1, #69 + bl SYM(core::option::expect_failed::GENERATED_ID, 0) + .loh AdrpAdd Lloh68, Lloh69 + .loh AdrpLdrGotLdr Lloh65, Lloh66, Lloh67 + .loh AdrpAdd Lloh92, Lloh93 + .loh AdrpAdd Lloh90, Lloh91 + .loh AdrpAdd Lloh88, Lloh89 + .loh AdrpAdd Lloh86, Lloh87 + .loh AdrpAdd Lloh84, Lloh85 + .loh AdrpAdd Lloh82, Lloh83 + .loh AdrpLdrGotLdr Lloh79, Lloh80, Lloh81 + .loh AdrpAdd Lloh77, Lloh78 + .loh AdrpAdd Lloh75, Lloh76 + .loh AdrpAdd Lloh73, Lloh74 + .loh AdrpLdrGotLdr Lloh70, Lloh71, Lloh72 + .loh AdrpAdd Lloh94, Lloh95 + .loh AdrpAdrp Lloh97, Lloh98 + .loh AdrpAdrp Lloh96, Lloh97 + .loh AdrpAdd Lloh101, Lloh102 + .loh AdrpAdd Lloh99, Lloh100 + .loh AdrpAdd Lloh105, Lloh106 + .loh AdrpAdd Lloh103, Lloh104 + .loh AdrpAdd Lloh109, Lloh110 + .loh AdrpAdd Lloh107, Lloh108 + .loh AdrpAdd Lloh113, Lloh114 + .loh AdrpAdd Lloh111, Lloh112 + + .p2align 2 +SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0): + sub sp, sp, #96 + stp x20, x19, [sp, #64] + stp x29, x30, [sp, #80] + add x29, sp, #80 + ldr x8, [x0] + ldrb w9, [x8] + strb wzr, [x8] + cbz w9, LBB6_4 Lloh115: - adrp x5, _init_forgetable_ivars@PAGE + adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE Lloh116: - add x5, x5, _init_forgetable_ivars@PAGEOFF - add x0, sp, #8 - mov x2, x19 - mov x3, #0 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - ldr x0, [sp, #8] - bl SYM(objc2::declare::ClassBuilder::register::GENERATED_ID, 0) - ldp x29, x30, [sp, #32] - ldp x20, x19, [sp, #16] - add sp, sp, #48 - ret -LBB5_3: + ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] Lloh117: - adrp x0, l_anon.[ID].11@PAGE + ldr x2, [x8] Lloh118: - add x0, x0, l_anon.[ID].11@PAGEOFF + adrp x0, l_anon.[ID].21@PAGE Lloh119: - adrp x2, l_anon.[ID].13@PAGE + add x0, x0, l_anon.[ID].21@PAGEOFF + mov w1, #15 + bl SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) + cbz x0, LBB6_5 + str x0, [sp, #8] Lloh120: - add x2, x2, l_anon.[ID].13@PAGEOFF - mov w1, #43 - bl SYM(core::panicking::panic::GENERATED_ID, 0) -LBB5_4: + adrp x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGE Lloh121: - adrp x0, l_anon.[ID].14@PAGE + ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGEOFF] Lloh122: - add x0, x0, l_anon.[ID].14@PAGEOFF + ldr x1, [x8] Lloh123: - adrp x2, l_anon.[ID].27@PAGE + adrp x2, l_anon.[ID].3@PAGE Lloh124: - add x2, x2, l_anon.[ID].27@PAGEOFF - mov w1, #15 - bl SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) - .loh AdrpAdd Lloh91, Lloh92 - .loh AdrpLdrGotLdr Lloh88, Lloh89, Lloh90 - .loh AdrpAdd Lloh115, Lloh116 - .loh AdrpAdd Lloh113, Lloh114 - .loh AdrpLdrGotLdr Lloh110, Lloh111, Lloh112 - .loh AdrpAdd Lloh108, Lloh109 - .loh AdrpAdd Lloh106, Lloh107 - .loh AdrpAdd Lloh104, Lloh105 - .loh AdrpLdrGotLdr Lloh101, Lloh102, Lloh103 - .loh AdrpAdd Lloh99, Lloh100 - .loh AdrpAdd Lloh97, Lloh98 - .loh AdrpAdd Lloh95, Lloh96 - .loh AdrpAdd Lloh93, Lloh94 - .loh AdrpAdd Lloh119, Lloh120 - .loh AdrpAdd Lloh117, Lloh118 - .loh AdrpAdd Lloh123, Lloh124 - .loh AdrpAdd Lloh121, Lloh122 - - .p2align 2 -SYM(<::call_once<::class::{closure#0}>::{closure#0} as core[CRATE_ID]::ops::function::FnOnce<(&std[CRATE_ID]::sync::once::OnceState,)>>::call_once::{shim:vtable#0}, 0): - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - ldr x8, [x0] - str x8, [sp, #8] - add x0, sp, #8 - bl SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) - ldp x29, x30, [sp, #16] - add sp, sp, #32 - ret - - .p2align 2 -SYM(<::call_once<::class::{closure#0}>::{closure#0} as core[CRATE_ID]::ops::function::FnOnce<(&std[CRATE_ID]::sync::once::OnceState,)>>::call_once::{shim:vtable#0}, 0): - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - ldr x8, [x0] - str x8, [sp, #8] - add x0, sp, #8 - bl SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) - ldp x29, x30, [sp, #16] - add sp, sp, #32 - ret - - .p2align 2 -SYM(<::call_once<::class::{closure#0}>::{closure#0} as core[CRATE_ID]::ops::function::FnOnce<(&std[CRATE_ID]::sync::once::OnceState,)>>::call_once::{shim:vtable#0}, 0): - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 - ldr x8, [x0] - str x8, [sp, #8] - add x0, sp, #8 - bl SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) - ldp x29, x30, [sp, #16] - add sp, sp, #32 - ret - - .globl _access_forgetable_ivars_class - .p2align 2 -_access_forgetable_ivars_class: - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 + add x2, x2, l_anon.[ID].3@PAGEOFF Lloh125: - adrp x8, SYM(::class::REGISTER_CLASS, 0)@PAGE + adrp x4, l_anon.[ID].4@PAGE Lloh126: - add x8, x8, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF - ldapr x8, [x8] - cmp x8, #3 - b.ne LBB9_3 + add x4, x4, l_anon.[ID].4@PAGEOFF Lloh127: - adrp x0, l_anon.[ID].14@PAGE + adrp x5, _init_forgetable_ivars@PAGE Lloh128: - add x0, x0, l_anon.[ID].14@PAGEOFF - mov w1, #15 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbz x0, LBB9_4 -LBB9_2: - ldp x29, x30, [sp, #16] - add sp, sp, #32 - ret -LBB9_3: - mov w8, #1 - strb w8, [sp, #7] - add x8, sp, #7 - str x8, [sp, #8] + add x5, x5, _init_forgetable_ivars@PAGEOFF + add x0, sp, #8 + mov x3, #0 + bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) + ldr x8, [sp, #8] + str x8, [sp, #16] + mov w8, #8 Lloh129: - adrp x0, SYM(::class::REGISTER_CLASS, 0)@PAGE + adrp x9, l_anon.[ID].14@PAGE Lloh130: - add x0, x0, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF + add x9, x9, l_anon.[ID].14@PAGEOFF + stp x8, x9, [sp, #32] + mov w8, #27 + strb w8, [sp, #24] Lloh131: - adrp x3, l_anon.[ID].2@PAGE + adrp x20, l_anon.[ID].11@PAGE Lloh132: - add x3, x3, l_anon.[ID].2@PAGEOFF + add x20, x20, l_anon.[ID].11@PAGEOFF + add x0, sp, #16 + add x5, sp, #24 + mov x1, x20 + mov w2, #5 + mov w3, #8 + mov w4, #2 + bl SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) + ldr x0, [sp, #16] + bl SYM(objc2::declare::ClassBuilder::register::GENERATED_ID, 0) + mov x19, x0 + mov x1, x20 + mov w2, #5 + bl SYM(objc2::runtime::AnyClass::instance_variable::GENERATED_ID, 0) + cbz x0, LBB6_6 + bl _ivar_getOffset Lloh133: - adrp x4, l_anon.[ID].27@PAGE + adrp x8, __MergedGlobals@PAGE+16 + str x19, [x8, __MergedGlobals@PAGEOFF+16] Lloh134: - add x4, x4, l_anon.[ID].27@PAGEOFF - add x2, sp, #8 - mov w1, #0 - bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2)@PAGE + str x0, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2)@PAGEOFF] Lloh135: - adrp x0, l_anon.[ID].14@PAGE + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 2)@PAGE + str xzr, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 2)@PAGEOFF] + ldp x29, x30, [sp, #80] + ldp x20, x19, [sp, #64] + add sp, sp, #96 + ret +LBB6_4: Lloh136: - add x0, x0, l_anon.[ID].14@PAGEOFF - mov w1, #15 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbnz x0, LBB9_2 -LBB9_4: + adrp x0, l_anon.[ID].24@PAGE Lloh137: - adrp x0, l_anon.[ID].11@PAGE + add x0, x0, l_anon.[ID].24@PAGEOFF Lloh138: - add x0, x0, l_anon.[ID].11@PAGEOFF + adrp x2, l_anon.[ID].26@PAGE Lloh139: - adrp x2, l_anon.[ID].27@PAGE -Lloh140: - add x2, x2, l_anon.[ID].27@PAGEOFF + add x2, x2, l_anon.[ID].26@PAGEOFF mov w1, #43 bl SYM(core::panicking::panic::GENERATED_ID, 0) - .loh AdrpAdd Lloh125, Lloh126 - .loh AdrpAdd Lloh127, Lloh128 - .loh AdrpAdd Lloh135, Lloh136 - .loh AdrpAdd Lloh133, Lloh134 +LBB6_5: +Lloh140: + adrp x0, l_anon.[ID].21@PAGE +Lloh141: + add x0, x0, l_anon.[ID].21@PAGEOFF +Lloh142: + adrp x2, l_anon.[ID].31@PAGE +Lloh143: + add x2, x2, l_anon.[ID].31@PAGEOFF + mov w1, #15 + bl SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) +LBB6_6: +Lloh144: + adrp x0, l_anon.[ID].16@PAGE +Lloh145: + add x0, x0, l_anon.[ID].16@PAGEOFF +Lloh146: + adrp x2, l_anon.[ID].18@PAGE +Lloh147: + add x2, x2, l_anon.[ID].18@PAGEOFF + mov w1, #59 + bl SYM(core::option::expect_failed::GENERATED_ID, 0) + .loh AdrpAdd Lloh118, Lloh119 + .loh AdrpLdrGotLdr Lloh115, Lloh116, Lloh117 .loh AdrpAdd Lloh131, Lloh132 .loh AdrpAdd Lloh129, Lloh130 - .loh AdrpAdd Lloh139, Lloh140 - .loh AdrpAdd Lloh137, Lloh138 + .loh AdrpAdd Lloh127, Lloh128 + .loh AdrpAdd Lloh125, Lloh126 + .loh AdrpAdd Lloh123, Lloh124 + .loh AdrpLdrGotLdr Lloh120, Lloh121, Lloh122 + .loh AdrpAdrp Lloh134, Lloh135 + .loh AdrpAdrp Lloh133, Lloh134 + .loh AdrpAdd Lloh138, Lloh139 + .loh AdrpAdd Lloh136, Lloh137 + .loh AdrpAdd Lloh142, Lloh143 + .loh AdrpAdd Lloh140, Lloh141 + .loh AdrpAdd Lloh146, Lloh147 + .loh AdrpAdd Lloh144, Lloh145 - .globl _access_forgetable_ivars .p2align 2 -_access_forgetable_ivars: - stp x20, x19, [sp, #-32]! +SYM(<::call_once<::class::{closure#0}>::{closure#0} as core[CRATE_ID]::ops::function::FnOnce<(&std[CRATE_ID]::sync::once::OnceState,)>>::call_once::{shim:vtable#0}, 0): + sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 - mov x19, x0 -Lloh141: - adrp x1, l_anon.[ID].20@PAGE -Lloh142: - add x1, x1, l_anon.[ID].20@PAGEOFF - mov w2, #4 - bl SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - bl _ivar_getOffset - ldrb w20, [x19, x0] -Lloh143: - adrp x1, l_anon.[ID].19@PAGE -Lloh144: - add x1, x1, l_anon.[ID].19@PAGEOFF - mov x0, x19 - mov w2, #4 - bl SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - bl _ivar_getOffset - ldr w1, [x19, x0] - mov x0, x20 + ldr x8, [x0] + str x8, [sp, #8] + add x0, sp, #8 + bl SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) ldp x29, x30, [sp, #16] - ldp x20, x19, [sp], #32 + add sp, sp, #32 ret - .loh AdrpAdd Lloh143, Lloh144 - .loh AdrpAdd Lloh141, Lloh142 - .globl SYM(::drop, 0) .p2align 2 -SYM(::drop, 0): - ; InlineAsm Start - ; InlineAsm End +SYM(<::call_once<::class::{closure#0}>::{closure#0} as core[CRATE_ID]::ops::function::FnOnce<(&std[CRATE_ID]::sync::once::OnceState,)>>::call_once::{shim:vtable#0}, 0): + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + ldr x8, [x0] + str x8, [sp, #8] + add x0, sp, #8 + bl SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) + ldp x29, x30, [sp, #16] + add sp, sp, #32 ret - .globl _access_drop_ivars_class .p2align 2 -_access_drop_ivars_class: +SYM(<::call_once<::class::{closure#0}>::{closure#0} as core[CRATE_ID]::ops::function::FnOnce<(&std[CRATE_ID]::sync::once::OnceState,)>>::call_once::{shim:vtable#0}, 0): sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 -Lloh145: - adrp x8, SYM(::class::REGISTER_CLASS, 0)@PAGE -Lloh146: - add x8, x8, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF - ldapr x8, [x8] - cmp x8, #3 - b.ne LBB12_3 -Lloh147: - adrp x0, l_anon.[ID].16@PAGE -Lloh148: - add x0, x0, l_anon.[ID].16@PAGEOFF - mov w1, #9 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbz x0, LBB12_4 -LBB12_2: + ldr x8, [x0] + str x8, [sp, #8] + add x0, sp, #8 + bl SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) ldp x29, x30, [sp, #16] add sp, sp, #32 ret -LBB12_3: + + .globl _access_forgetable_ivars_class + .p2align 2 +_access_forgetable_ivars_class: +Lloh148: + adrp x8, __MergedGlobals@PAGE+24 +Lloh149: + add x8, x8, __MergedGlobals@PAGEOFF+24 + ldapr x8, [x8] + cmp x8, #3 + b.ne LBB10_2 +Lloh150: + adrp x8, __MergedGlobals@PAGE+16 +Lloh151: + ldr x0, [x8, __MergedGlobals@PAGEOFF+16] + ret +LBB10_2: + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 mov w8, #1 strb w8, [sp, #7] add x8, sp, #7 str x8, [sp, #8] -Lloh149: - adrp x0, SYM(::class::REGISTER_CLASS, 0)@PAGE -Lloh150: - add x0, x0, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF -Lloh151: - adrp x3, l_anon.[ID].1@PAGE Lloh152: - add x3, x3, l_anon.[ID].1@PAGEOFF + adrp x0, __MergedGlobals@PAGE+24 Lloh153: - adrp x4, l_anon.[ID].28@PAGE + add x0, x0, __MergedGlobals@PAGEOFF+24 Lloh154: - add x4, x4, l_anon.[ID].28@PAGEOFF - add x2, sp, #8 - mov w1, #0 - bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + adrp x3, l_anon.[ID].2@PAGE Lloh155: - adrp x0, l_anon.[ID].16@PAGE + add x3, x3, l_anon.[ID].2@PAGEOFF Lloh156: - add x0, x0, l_anon.[ID].16@PAGEOFF - mov w1, #9 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbnz x0, LBB12_2 -LBB12_4: + adrp x4, l_anon.[ID].31@PAGE Lloh157: - adrp x0, l_anon.[ID].11@PAGE + add x4, x4, l_anon.[ID].31@PAGEOFF + add x2, sp, #8 + mov w1, #0 + bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + ldp x29, x30, [sp, #16] + add sp, sp, #32 Lloh158: - add x0, x0, l_anon.[ID].11@PAGEOFF + adrp x8, __MergedGlobals@PAGE+16 Lloh159: - adrp x2, l_anon.[ID].28@PAGE -Lloh160: - add x2, x2, l_anon.[ID].28@PAGEOFF - mov w1, #43 - bl SYM(core::panicking::panic::GENERATED_ID, 0) - .loh AdrpAdd Lloh145, Lloh146 - .loh AdrpAdd Lloh147, Lloh148 - .loh AdrpAdd Lloh155, Lloh156 - .loh AdrpAdd Lloh153, Lloh154 - .loh AdrpAdd Lloh151, Lloh152 - .loh AdrpAdd Lloh149, Lloh150 - .loh AdrpAdd Lloh159, Lloh160 - .loh AdrpAdd Lloh157, Lloh158 + ldr x0, [x8, __MergedGlobals@PAGEOFF+16] + ret + .loh AdrpAdd Lloh148, Lloh149 + .loh AdrpLdr Lloh150, Lloh151 + .loh AdrpLdr Lloh158, Lloh159 + .loh AdrpAdd Lloh156, Lloh157 + .loh AdrpAdd Lloh154, Lloh155 + .loh AdrpAdd Lloh152, Lloh153 - .globl _access_drop_ivars + .globl _access_forgetable_ivars .p2align 2 -_access_drop_ivars: - stp x20, x19, [sp, #-32]! - stp x29, x30, [sp, #16] - add x29, sp, #16 - mov x19, x0 +_access_forgetable_ivars: +Lloh160: + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2)@PAGE Lloh161: - adrp x1, l_anon.[ID].18@PAGE -Lloh162: - add x1, x1, l_anon.[ID].18@PAGEOFF - mov w2, #4 - bl SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - bl _ivar_getOffset - ldr x0, [x19, x0] - ldp x29, x30, [sp, #16] - ldp x20, x19, [sp], #32 + ldr x8, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2)@PAGEOFF] + add x8, x0, x8 + ldr w1, [x8] + ldrb w0, [x8, #4] ret - .loh AdrpAdd Lloh161, Lloh162 + .loh AdrpLdr Lloh160, Lloh161 - .globl SYM(::class, 0) + .globl SYM(::drop, 0) .p2align 2 -SYM(::class, 0): - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 +SYM(::drop, 0): + ; InlineAsm Start + ; InlineAsm End + ret + + .globl _access_drop_ivars_class + .p2align 2 +_access_drop_ivars_class: +Lloh162: + adrp x8, __MergedGlobals@PAGE+40 Lloh163: - adrp x8, SYM(::class::REGISTER_CLASS, 0)@PAGE -Lloh164: - add x8, x8, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF + add x8, x8, __MergedGlobals@PAGEOFF+40 ldapr x8, [x8] cmp x8, #3 - b.ne LBB14_3 + b.ne LBB13_2 +Lloh164: + adrp x8, __MergedGlobals@PAGE+32 Lloh165: - adrp x0, l_anon.[ID].15@PAGE -Lloh166: - add x0, x0, l_anon.[ID].15@PAGEOFF - mov w1, #7 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbz x0, LBB14_4 -LBB14_2: - ldp x29, x30, [sp, #16] - add sp, sp, #32 + ldr x0, [x8, __MergedGlobals@PAGEOFF+32] ret -LBB14_3: +LBB13_2: + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 mov w8, #1 strb w8, [sp, #7] add x8, sp, #7 str x8, [sp, #8] +Lloh166: + adrp x0, __MergedGlobals@PAGE+40 Lloh167: - adrp x0, SYM(::class::REGISTER_CLASS, 0)@PAGE + add x0, x0, __MergedGlobals@PAGEOFF+40 Lloh168: - add x0, x0, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF + adrp x3, l_anon.[ID].1@PAGE Lloh169: - adrp x3, l_anon.[ID].0@PAGE + add x3, x3, l_anon.[ID].1@PAGEOFF Lloh170: - add x3, x3, l_anon.[ID].0@PAGEOFF + adrp x4, l_anon.[ID].32@PAGE Lloh171: - adrp x4, l_anon.[ID].22@PAGE -Lloh172: - add x4, x4, l_anon.[ID].22@PAGEOFF + add x4, x4, l_anon.[ID].32@PAGEOFF add x2, sp, #8 mov w1, #0 bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + ldp x29, x30, [sp, #16] + add sp, sp, #32 +Lloh172: + adrp x8, __MergedGlobals@PAGE+32 Lloh173: - adrp x0, l_anon.[ID].15@PAGE + ldr x0, [x8, __MergedGlobals@PAGEOFF+32] + ret + .loh AdrpAdd Lloh162, Lloh163 + .loh AdrpLdr Lloh164, Lloh165 + .loh AdrpLdr Lloh172, Lloh173 + .loh AdrpAdd Lloh170, Lloh171 + .loh AdrpAdd Lloh168, Lloh169 + .loh AdrpAdd Lloh166, Lloh167 + + .globl _access_drop_ivars + .p2align 2 +_access_drop_ivars: Lloh174: - add x0, x0, l_anon.[ID].15@PAGEOFF - mov w1, #7 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbnz x0, LBB14_2 -LBB14_4: + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)@PAGE Lloh175: - adrp x0, l_anon.[ID].11@PAGE + ldr x8, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)@PAGEOFF] + add x8, x0, x8 + ldp x0, x1, [x8] + ret + .loh AdrpLdr Lloh174, Lloh175 + + .globl SYM(::class, 0) + .p2align 2 +SYM(::class, 0): Lloh176: - add x0, x0, l_anon.[ID].11@PAGEOFF + adrp x8, __MergedGlobals@PAGE+8 Lloh177: - adrp x2, l_anon.[ID].22@PAGE + add x8, x8, __MergedGlobals@PAGEOFF+8 + ldapr x8, [x8] + cmp x8, #3 + b.ne LBB15_2 Lloh178: - add x2, x2, l_anon.[ID].22@PAGEOFF - mov w1, #43 - bl SYM(core::panicking::panic::GENERATED_ID, 0) - .loh AdrpAdd Lloh163, Lloh164 - .loh AdrpAdd Lloh165, Lloh166 - .loh AdrpAdd Lloh173, Lloh174 - .loh AdrpAdd Lloh171, Lloh172 - .loh AdrpAdd Lloh169, Lloh170 - .loh AdrpAdd Lloh167, Lloh168 - .loh AdrpAdd Lloh177, Lloh178 - .loh AdrpAdd Lloh175, Lloh176 - - .globl _get_class - .p2align 2 -_get_class: + adrp x8, __MergedGlobals@PAGE +Lloh179: + ldr x0, [x8, __MergedGlobals@PAGEOFF] + ret +LBB15_2: sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 -Lloh179: - adrp x8, SYM(::class::REGISTER_CLASS, 0)@PAGE -Lloh180: - add x8, x8, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF - ldapr x8, [x8] - cmp x8, #3 - b.ne LBB15_3 -Lloh181: - adrp x0, l_anon.[ID].15@PAGE -Lloh182: - add x0, x0, l_anon.[ID].15@PAGEOFF - mov w1, #7 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbz x0, LBB15_4 -LBB15_2: - ldp x29, x30, [sp, #16] - add sp, sp, #32 - ret -LBB15_3: mov w8, #1 strb w8, [sp, #7] add x8, sp, #7 str x8, [sp, #8] +Lloh180: + adrp x0, __MergedGlobals@PAGE+8 +Lloh181: + add x0, x0, __MergedGlobals@PAGEOFF+8 +Lloh182: + adrp x3, l_anon.[ID].0@PAGE Lloh183: - adrp x0, SYM(::class::REGISTER_CLASS, 0)@PAGE + add x3, x3, l_anon.[ID].0@PAGEOFF Lloh184: - add x0, x0, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF + adrp x4, l_anon.[ID].28@PAGE Lloh185: - adrp x3, l_anon.[ID].0@PAGE -Lloh186: - add x3, x3, l_anon.[ID].0@PAGEOFF -Lloh187: - adrp x4, l_anon.[ID].22@PAGE -Lloh188: - add x4, x4, l_anon.[ID].22@PAGEOFF + add x4, x4, l_anon.[ID].28@PAGEOFF add x2, sp, #8 mov w1, #0 bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + ldp x29, x30, [sp, #16] + add sp, sp, #32 +Lloh186: + adrp x8, __MergedGlobals@PAGE +Lloh187: + ldr x0, [x8, __MergedGlobals@PAGEOFF] + ret + .loh AdrpAdd Lloh176, Lloh177 + .loh AdrpLdr Lloh178, Lloh179 + .loh AdrpLdr Lloh186, Lloh187 + .loh AdrpAdd Lloh184, Lloh185 + .loh AdrpAdd Lloh182, Lloh183 + .loh AdrpAdd Lloh180, Lloh181 + + .globl _get_class + .p2align 2 +_get_class: +Lloh188: + adrp x8, __MergedGlobals@PAGE+8 Lloh189: - adrp x0, l_anon.[ID].15@PAGE + add x8, x8, __MergedGlobals@PAGEOFF+8 + ldapr x8, [x8] + cmp x8, #3 + b.ne LBB16_2 Lloh190: - add x0, x0, l_anon.[ID].15@PAGEOFF - mov w1, #7 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbnz x0, LBB15_2 -LBB15_4: + adrp x8, __MergedGlobals@PAGE Lloh191: - adrp x0, l_anon.[ID].11@PAGE + ldr x0, [x8, __MergedGlobals@PAGEOFF] + ret +LBB16_2: + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 + mov w8, #1 + strb w8, [sp, #7] + add x8, sp, #7 + str x8, [sp, #8] Lloh192: - add x0, x0, l_anon.[ID].11@PAGEOFF + adrp x0, __MergedGlobals@PAGE+8 Lloh193: - adrp x2, l_anon.[ID].22@PAGE + add x0, x0, __MergedGlobals@PAGEOFF+8 Lloh194: - add x2, x2, l_anon.[ID].22@PAGEOFF - mov w1, #43 - bl SYM(core::panicking::panic::GENERATED_ID, 0) - .loh AdrpAdd Lloh179, Lloh180 - .loh AdrpAdd Lloh181, Lloh182 - .loh AdrpAdd Lloh189, Lloh190 - .loh AdrpAdd Lloh187, Lloh188 - .loh AdrpAdd Lloh185, Lloh186 - .loh AdrpAdd Lloh183, Lloh184 - .loh AdrpAdd Lloh193, Lloh194 - .loh AdrpAdd Lloh191, Lloh192 + adrp x3, l_anon.[ID].0@PAGE +Lloh195: + add x3, x3, l_anon.[ID].0@PAGEOFF +Lloh196: + adrp x4, l_anon.[ID].28@PAGE +Lloh197: + add x4, x4, l_anon.[ID].28@PAGEOFF + add x2, sp, #8 + mov w1, #0 + bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + ldp x29, x30, [sp, #16] + add sp, sp, #32 +Lloh198: + adrp x8, __MergedGlobals@PAGE +Lloh199: + ldr x0, [x8, __MergedGlobals@PAGEOFF] + ret + .loh AdrpAdd Lloh188, Lloh189 + .loh AdrpLdr Lloh190, Lloh191 + .loh AdrpLdr Lloh198, Lloh199 + .loh AdrpAdd Lloh196, Lloh197 + .loh AdrpAdd Lloh194, Lloh195 + .loh AdrpAdd Lloh192, Lloh193 .globl _method_simple .p2align 2 @@ -880,79 +910,57 @@ _method_id: sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 -Lloh195: - adrp x8, SYM(::class::REGISTER_CLASS, 0)@PAGE -Lloh196: - add x8, x8, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF +Lloh200: + adrp x8, __MergedGlobals@PAGE+8 +Lloh201: + add x8, x8, __MergedGlobals@PAGEOFF+8 ldapr x8, [x8] cmp x8, #3 - b.ne LBB18_3 -Lloh197: - adrp x0, l_anon.[ID].15@PAGE -Lloh198: - add x0, x0, l_anon.[ID].15@PAGEOFF - mov w1, #7 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbz x0, LBB18_4 -LBB18_2: -Lloh199: + b.ne LBB19_2 +LBB19_1: +Lloh202: + adrp x8, __MergedGlobals@PAGE +Lloh203: + ldr x0, [x8, __MergedGlobals@PAGEOFF] +Lloh204: adrp x8, L_OBJC_SELECTOR_REFERENCES_new@GOTPAGE -Lloh200: +Lloh205: ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_new@GOTPAGEOFF] -Lloh201: +Lloh206: ldr x1, [x8] bl _objc_msgSend bl _objc_autoreleaseReturnValue ldp x29, x30, [sp, #16] add sp, sp, #32 ret -LBB18_3: +LBB19_2: mov w8, #1 strb w8, [sp, #7] add x8, sp, #7 str x8, [sp, #8] -Lloh202: - adrp x0, SYM(::class::REGISTER_CLASS, 0)@PAGE -Lloh203: - add x0, x0, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF -Lloh204: - adrp x3, l_anon.[ID].0@PAGE -Lloh205: - add x3, x3, l_anon.[ID].0@PAGEOFF -Lloh206: - adrp x4, l_anon.[ID].22@PAGE Lloh207: - add x4, x4, l_anon.[ID].22@PAGEOFF - add x2, sp, #8 - mov w1, #0 - bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + adrp x0, __MergedGlobals@PAGE+8 Lloh208: - adrp x0, l_anon.[ID].15@PAGE + add x0, x0, __MergedGlobals@PAGEOFF+8 Lloh209: - add x0, x0, l_anon.[ID].15@PAGEOFF - mov w1, #7 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbnz x0, LBB18_2 -LBB18_4: + adrp x3, l_anon.[ID].0@PAGE Lloh210: - adrp x0, l_anon.[ID].11@PAGE + add x3, x3, l_anon.[ID].0@PAGEOFF Lloh211: - add x0, x0, l_anon.[ID].11@PAGEOFF + adrp x4, l_anon.[ID].28@PAGE Lloh212: - adrp x2, l_anon.[ID].22@PAGE -Lloh213: - add x2, x2, l_anon.[ID].22@PAGEOFF - mov w1, #43 - bl SYM(core::panicking::panic::GENERATED_ID, 0) - .loh AdrpAdd Lloh195, Lloh196 - .loh AdrpAdd Lloh197, Lloh198 - .loh AdrpLdrGotLdr Lloh199, Lloh200, Lloh201 - .loh AdrpAdd Lloh208, Lloh209 - .loh AdrpAdd Lloh206, Lloh207 - .loh AdrpAdd Lloh204, Lloh205 - .loh AdrpAdd Lloh202, Lloh203 - .loh AdrpAdd Lloh212, Lloh213 - .loh AdrpAdd Lloh210, Lloh211 + add x4, x4, l_anon.[ID].28@PAGEOFF + add x2, sp, #8 + mov w1, #0 + bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + b LBB19_1 + .loh AdrpAdd Lloh200, Lloh201 + .loh AdrpLdrGotLdr Lloh204, Lloh205, Lloh206 + .loh AdrpAdrp Lloh202, Lloh204 + .loh AdrpLdr Lloh202, Lloh203 + .loh AdrpAdd Lloh211, Lloh212 + .loh AdrpAdd Lloh209, Lloh210 + .loh AdrpAdd Lloh207, Lloh208 .globl _method_id_with_param .p2align 2 @@ -963,13 +971,13 @@ _method_id_with_param: mov x20, x2 bl SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) mov x19, x0 - tbz w20, #0, LBB19_2 + tbz w20, #0, LBB20_2 bl SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) mov x20, x0 mov x0, x19 bl _objc_release mov x19, x20 -LBB19_2: +LBB20_2: mov x0, x19 ldp x29, x30, [sp, #16] ldp x20, x19, [sp], #32 @@ -981,402 +989,257 @@ _copyWithZone: sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 +Lloh213: + adrp x8, __MergedGlobals@PAGE+8 Lloh214: - adrp x8, SYM(::class::REGISTER_CLASS, 0)@PAGE -Lloh215: - add x8, x8, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF + add x8, x8, __MergedGlobals@PAGEOFF+8 ldapr x8, [x8] cmp x8, #3 - b.ne LBB20_3 + b.ne LBB21_2 +LBB21_1: +Lloh215: + adrp x8, __MergedGlobals@PAGE Lloh216: - adrp x0, l_anon.[ID].15@PAGE + ldr x0, [x8, __MergedGlobals@PAGEOFF] Lloh217: - add x0, x0, l_anon.[ID].15@PAGEOFF - mov w1, #7 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbz x0, LBB20_4 -LBB20_2: -Lloh218: adrp x8, L_OBJC_SELECTOR_REFERENCES_new@GOTPAGE -Lloh219: +Lloh218: ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_new@GOTPAGEOFF] -Lloh220: +Lloh219: ldr x1, [x8] bl _objc_msgSend ldp x29, x30, [sp, #16] add sp, sp, #32 ret -LBB20_3: +LBB21_2: mov w8, #1 strb w8, [sp, #7] add x8, sp, #7 str x8, [sp, #8] +Lloh220: + adrp x0, __MergedGlobals@PAGE+8 Lloh221: - adrp x0, SYM(::class::REGISTER_CLASS, 0)@PAGE + add x0, x0, __MergedGlobals@PAGEOFF+8 Lloh222: - add x0, x0, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF -Lloh223: adrp x3, l_anon.[ID].0@PAGE -Lloh224: +Lloh223: add x3, x3, l_anon.[ID].0@PAGEOFF +Lloh224: + adrp x4, l_anon.[ID].28@PAGE Lloh225: - adrp x4, l_anon.[ID].22@PAGE -Lloh226: - add x4, x4, l_anon.[ID].22@PAGEOFF + add x4, x4, l_anon.[ID].28@PAGEOFF add x2, sp, #8 - mov w1, #0 - bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) -Lloh227: - adrp x0, l_anon.[ID].15@PAGE -Lloh228: - add x0, x0, l_anon.[ID].15@PAGEOFF - mov w1, #7 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbnz x0, LBB20_2 -LBB20_4: -Lloh229: - adrp x0, l_anon.[ID].11@PAGE -Lloh230: - add x0, x0, l_anon.[ID].11@PAGEOFF -Lloh231: - adrp x2, l_anon.[ID].22@PAGE -Lloh232: - add x2, x2, l_anon.[ID].22@PAGEOFF - mov w1, #43 - bl SYM(core::panicking::panic::GENERATED_ID, 0) - .loh AdrpAdd Lloh214, Lloh215 - .loh AdrpAdd Lloh216, Lloh217 - .loh AdrpLdrGotLdr Lloh218, Lloh219, Lloh220 - .loh AdrpAdd Lloh227, Lloh228 - .loh AdrpAdd Lloh225, Lloh226 - .loh AdrpAdd Lloh223, Lloh224 - .loh AdrpAdd Lloh221, Lloh222 - .loh AdrpAdd Lloh231, Lloh232 - .loh AdrpAdd Lloh229, Lloh230 + mov w1, #0 + bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + b LBB21_1 + .loh AdrpAdd Lloh213, Lloh214 + .loh AdrpLdrGotLdr Lloh217, Lloh218, Lloh219 + .loh AdrpAdrp Lloh215, Lloh217 + .loh AdrpLdr Lloh215, Lloh216 + .loh AdrpAdd Lloh224, Lloh225 + .loh AdrpAdd Lloh222, Lloh223 + .loh AdrpAdd Lloh220, Lloh221 .globl SYM(::class, 0) .p2align 2 SYM(::class, 0): - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 -Lloh233: - adrp x8, SYM(::class::REGISTER_CLASS, 0)@PAGE -Lloh234: - add x8, x8, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF +Lloh226: + adrp x8, __MergedGlobals@PAGE+24 +Lloh227: + add x8, x8, __MergedGlobals@PAGEOFF+24 ldapr x8, [x8] cmp x8, #3 - b.ne LBB21_3 -Lloh235: - adrp x0, l_anon.[ID].14@PAGE -Lloh236: - add x0, x0, l_anon.[ID].14@PAGEOFF - mov w1, #15 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbz x0, LBB21_4 -LBB21_2: - ldp x29, x30, [sp, #16] - add sp, sp, #32 + b.ne LBB22_2 +Lloh228: + adrp x8, __MergedGlobals@PAGE+16 +Lloh229: + ldr x0, [x8, __MergedGlobals@PAGEOFF+16] ret -LBB21_3: +LBB22_2: + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 mov w8, #1 strb w8, [sp, #7] add x8, sp, #7 str x8, [sp, #8] -Lloh237: - adrp x0, SYM(::class::REGISTER_CLASS, 0)@PAGE -Lloh238: - add x0, x0, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF -Lloh239: +Lloh230: + adrp x0, __MergedGlobals@PAGE+24 +Lloh231: + add x0, x0, __MergedGlobals@PAGEOFF+24 +Lloh232: adrp x3, l_anon.[ID].2@PAGE -Lloh240: +Lloh233: add x3, x3, l_anon.[ID].2@PAGEOFF -Lloh241: - adrp x4, l_anon.[ID].27@PAGE -Lloh242: - add x4, x4, l_anon.[ID].27@PAGEOFF +Lloh234: + adrp x4, l_anon.[ID].31@PAGE +Lloh235: + add x4, x4, l_anon.[ID].31@PAGEOFF add x2, sp, #8 mov w1, #0 bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) -Lloh243: - adrp x0, l_anon.[ID].14@PAGE -Lloh244: - add x0, x0, l_anon.[ID].14@PAGEOFF - mov w1, #15 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbnz x0, LBB21_2 -LBB21_4: -Lloh245: - adrp x0, l_anon.[ID].11@PAGE -Lloh246: - add x0, x0, l_anon.[ID].11@PAGEOFF -Lloh247: - adrp x2, l_anon.[ID].27@PAGE -Lloh248: - add x2, x2, l_anon.[ID].27@PAGEOFF - mov w1, #43 - bl SYM(core::panicking::panic::GENERATED_ID, 0) - .loh AdrpAdd Lloh233, Lloh234 - .loh AdrpAdd Lloh235, Lloh236 - .loh AdrpAdd Lloh243, Lloh244 - .loh AdrpAdd Lloh241, Lloh242 - .loh AdrpAdd Lloh239, Lloh240 - .loh AdrpAdd Lloh237, Lloh238 - .loh AdrpAdd Lloh247, Lloh248 - .loh AdrpAdd Lloh245, Lloh246 - - .p2align 2 -SYM(::class::{closure#0}::__objc2_dealloc, 0): - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 -Lloh249: - adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE -Lloh250: - ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] -Lloh251: - ldr x8, [x8] - stp x0, x8, [sp] - mov x0, sp - bl _objc_msgSendSuper ldp x29, x30, [sp, #16] add sp, sp, #32 +Lloh236: + adrp x8, __MergedGlobals@PAGE+16 +Lloh237: + ldr x0, [x8, __MergedGlobals@PAGEOFF+16] ret - .loh AdrpLdrGotLdr Lloh249, Lloh250, Lloh251 + .loh AdrpAdd Lloh226, Lloh227 + .loh AdrpLdr Lloh228, Lloh229 + .loh AdrpLdr Lloh236, Lloh237 + .loh AdrpAdd Lloh234, Lloh235 + .loh AdrpAdd Lloh232, Lloh233 + .loh AdrpAdd Lloh230, Lloh231 .globl _init_forgetable_ivars .p2align 2 _init_forgetable_ivars: - sub sp, sp, #48 - stp x20, x19, [sp, #16] - stp x29, x30, [sp, #32] - add x29, sp, #32 -Lloh252: - adrp x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGE -Lloh253: - ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGEOFF] -Lloh254: - ldr x1, [x8] -Lloh255: + cbz x0, LBB23_2 +Lloh238: + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2)@PAGE +Lloh239: + ldr x8, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2)@PAGEOFF] + add x8, x0, x8 + mov w9, #43 + str w9, [x8] + mov w9, #42 + strb w9, [x8, #4] +LBB23_2: + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 +Lloh240: + adrp x8, L_OBJC_SELECTOR_REFERENCES_af8966656b8b2b6c@PAGE +Lloh241: + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_af8966656b8b2b6c@PAGEOFF] +Lloh242: adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE -Lloh256: +Lloh243: ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] -Lloh257: +Lloh244: ldr x8, [x8] stp x0, x8, [sp] mov x0, sp bl _objc_msgSendSuper - mov x19, x0 - cbz x0, LBB23_2 -Lloh258: - adrp x1, l_anon.[ID].20@PAGE -Lloh259: - add x1, x1, l_anon.[ID].20@PAGEOFF - mov x0, x19 - mov w2, #4 - bl SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - bl _ivar_getOffset - mov w8, #42 - strb w8, [x19, x0] -Lloh260: - adrp x1, l_anon.[ID].19@PAGE -Lloh261: - add x1, x1, l_anon.[ID].19@PAGEOFF - mov x0, x19 - mov w2, #4 - bl SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - bl _ivar_getOffset - mov w8, #43 - str w8, [x19, x0] -LBB23_2: - mov x0, x19 - ldp x29, x30, [sp, #32] - ldp x20, x19, [sp, #16] - add sp, sp, #48 + ldp x29, x30, [sp, #16] + add sp, sp, #32 ret - .loh AdrpLdrGotLdr Lloh255, Lloh256, Lloh257 - .loh AdrpLdrGotLdr Lloh252, Lloh253, Lloh254 - .loh AdrpAdd Lloh260, Lloh261 - .loh AdrpAdd Lloh258, Lloh259 + .loh AdrpLdr Lloh238, Lloh239 + .loh AdrpLdrGotLdr Lloh242, Lloh243, Lloh244 + .loh AdrpAdrp Lloh240, Lloh242 + .loh AdrpLdr Lloh240, Lloh241 .globl SYM(::class, 0) .p2align 2 SYM(::class, 0): - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 -Lloh262: - adrp x8, SYM(::class::REGISTER_CLASS, 0)@PAGE -Lloh263: - add x8, x8, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF +Lloh245: + adrp x8, __MergedGlobals@PAGE+40 +Lloh246: + add x8, x8, __MergedGlobals@PAGEOFF+40 ldapr x8, [x8] cmp x8, #3 - b.ne LBB24_3 -Lloh264: - adrp x0, l_anon.[ID].16@PAGE -Lloh265: - add x0, x0, l_anon.[ID].16@PAGEOFF - mov w1, #9 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbz x0, LBB24_4 -LBB24_2: - ldp x29, x30, [sp, #16] - add sp, sp, #32 + b.ne LBB24_2 +Lloh247: + adrp x8, __MergedGlobals@PAGE+32 +Lloh248: + ldr x0, [x8, __MergedGlobals@PAGEOFF+32] ret -LBB24_3: +LBB24_2: + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 mov w8, #1 strb w8, [sp, #7] add x8, sp, #7 str x8, [sp, #8] -Lloh266: - adrp x0, SYM(::class::REGISTER_CLASS, 0)@PAGE -Lloh267: - add x0, x0, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF -Lloh268: +Lloh249: + adrp x0, __MergedGlobals@PAGE+40 +Lloh250: + add x0, x0, __MergedGlobals@PAGEOFF+40 +Lloh251: adrp x3, l_anon.[ID].1@PAGE -Lloh269: +Lloh252: add x3, x3, l_anon.[ID].1@PAGEOFF -Lloh270: - adrp x4, l_anon.[ID].28@PAGE -Lloh271: - add x4, x4, l_anon.[ID].28@PAGEOFF +Lloh253: + adrp x4, l_anon.[ID].32@PAGE +Lloh254: + add x4, x4, l_anon.[ID].32@PAGEOFF add x2, sp, #8 mov w1, #0 bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) -Lloh272: - adrp x0, l_anon.[ID].16@PAGE -Lloh273: - add x0, x0, l_anon.[ID].16@PAGEOFF - mov w1, #9 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbnz x0, LBB24_2 -LBB24_4: -Lloh274: - adrp x0, l_anon.[ID].11@PAGE -Lloh275: - add x0, x0, l_anon.[ID].11@PAGEOFF -Lloh276: - adrp x2, l_anon.[ID].28@PAGE -Lloh277: - add x2, x2, l_anon.[ID].28@PAGEOFF - mov w1, #43 - bl SYM(core::panicking::panic::GENERATED_ID, 0) - .loh AdrpAdd Lloh262, Lloh263 - .loh AdrpAdd Lloh264, Lloh265 - .loh AdrpAdd Lloh272, Lloh273 - .loh AdrpAdd Lloh270, Lloh271 - .loh AdrpAdd Lloh268, Lloh269 - .loh AdrpAdd Lloh266, Lloh267 - .loh AdrpAdd Lloh276, Lloh277 - .loh AdrpAdd Lloh274, Lloh275 + ldp x29, x30, [sp, #16] + add sp, sp, #32 +Lloh255: + adrp x8, __MergedGlobals@PAGE+32 +Lloh256: + ldr x0, [x8, __MergedGlobals@PAGEOFF+32] + ret + .loh AdrpAdd Lloh245, Lloh246 + .loh AdrpLdr Lloh247, Lloh248 + .loh AdrpLdr Lloh255, Lloh256 + .loh AdrpAdd Lloh253, Lloh254 + .loh AdrpAdd Lloh251, Lloh252 + .loh AdrpAdd Lloh249, Lloh250 + .globl _init_drop_ivars .p2align 2 -SYM(::class::{closure#0}::__objc2_dealloc, 0): - sub sp, sp, #48 - stp x20, x19, [sp, #16] - stp x29, x30, [sp, #32] - add x29, sp, #32 - mov x19, x1 +_init_drop_ivars: + sub sp, sp, #64 + stp x22, x21, [sp, #16] + stp x20, x19, [sp, #32] + stp x29, x30, [sp, #48] + add x29, sp, #48 + mov x19, x0 + bl SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) mov x20, x0 - bl SYM(::drop, 0) -Lloh278: - adrp x1, l_anon.[ID].18@PAGE -Lloh279: - add x1, x1, l_anon.[ID].18@PAGEOFF - mov x0, x20 - mov w2, #4 - bl SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - bl _ivar_getOffset - ldr x0, [x20, x0] - cbz x0, LBB25_2 - bl _objc_release + bl SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) + mov x21, x0 + adrp x22, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0)@PAGE + cbz x19, LBB25_2 +Lloh257: + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)@PAGE +Lloh258: + ldr x8, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)@PAGEOFF] + add x8, x19, x8 + stp x20, x21, [x8] + ldr x8, [x22, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0)@PAGEOFF] + mov w9, #15 + strb w9, [x19, x8] + b LBB25_3 LBB25_2: -Lloh280: - adrp x1, l_anon.[ID].17@PAGE -Lloh281: - add x1, x1, l_anon.[ID].17@PAGEOFF mov x0, x20 - mov w2, #11 - bl SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - bl _ivar_getOffset - ldr x0, [x20, x0] - cbz x0, LBB25_4 bl _objc_release -LBB25_4: -Lloh282: - adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE -Lloh283: - ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] -Lloh284: - ldr x8, [x8] - stp x20, x8, [sp] - mov x0, sp - mov x1, x19 - bl _objc_msgSendSuper - ldp x29, x30, [sp, #32] - ldp x20, x19, [sp, #16] - add sp, sp, #48 - ret - .loh AdrpAdd Lloh278, Lloh279 - .loh AdrpAdd Lloh280, Lloh281 - .loh AdrpLdrGotLdr Lloh282, Lloh283, Lloh284 - - .globl _init_drop_ivars - .p2align 2 -_init_drop_ivars: - sub sp, sp, #48 - stp x20, x19, [sp, #16] - stp x29, x30, [sp, #32] - add x29, sp, #32 -Lloh285: - adrp x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGE -Lloh286: - ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGEOFF] -Lloh287: - ldr x1, [x8] -Lloh288: + mov x0, x21 + bl _objc_release +LBB25_3: +Lloh259: + adrp x8, L_OBJC_SELECTOR_REFERENCES_6edddcebbded8f32@PAGE +Lloh260: + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_6edddcebbded8f32@PAGEOFF] +Lloh261: adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE -Lloh289: +Lloh262: ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] -Lloh290: +Lloh263: ldr x8, [x8] - stp x0, x8, [sp] + stp x19, x8, [sp] mov x0, sp bl _objc_msgSendSuper - mov x19, x0 - cbz x0, LBB26_2 - bl SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) - mov x20, x0 -Lloh291: - adrp x1, l_anon.[ID].18@PAGE -Lloh292: - add x1, x1, l_anon.[ID].18@PAGEOFF - mov x0, x19 - mov w2, #4 - bl SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - bl _ivar_getOffset - str x20, [x19, x0] - bl SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) - mov x20, x0 -Lloh293: - adrp x1, l_anon.[ID].17@PAGE -Lloh294: - add x1, x1, l_anon.[ID].17@PAGEOFF - mov x0, x19 - mov w2, #11 - bl SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - bl _ivar_getOffset - str x20, [x19, x0] -LBB26_2: - mov x0, x19 - ldp x29, x30, [sp, #32] - ldp x20, x19, [sp, #16] - add sp, sp, #48 + cbz x0, LBB25_5 + ldr x8, [x22, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0)@PAGEOFF] + mov w9, #255 + strb w9, [x0, x8] +LBB25_5: + ldp x29, x30, [sp, #48] + ldp x20, x19, [sp, #32] + ldp x22, x21, [sp, #16] + add sp, sp, #64 ret - .loh AdrpLdrGotLdr Lloh288, Lloh289, Lloh290 - .loh AdrpLdrGotLdr Lloh285, Lloh286, Lloh287 - .loh AdrpAdd Lloh293, Lloh294 - .loh AdrpAdd Lloh291, Lloh292 + .loh AdrpLdr Lloh257, Lloh258 + .loh AdrpLdrGotLdr Lloh261, Lloh262, Lloh263 + .loh AdrpAdrp Lloh259, Lloh261 + .loh AdrpLdr Lloh259, Lloh260 .section __DATA,__const .p2align 3, 0x0 @@ -1447,183 +1310,247 @@ l_anon.[ID].10: .space 39 l_anon.[ID].11: - .ascii "called `Option::unwrap()` on a `None` value" + .ascii "ivars" l_anon.[ID].12: - .ascii "$RUSTC/library/std/src/sync/once.rs" + .ascii "drop_flag" - .section __DATA,__const .p2align 3, 0x0 l_anon.[ID].13: - .quad l_anon.[ID].12 - .asciz "p\000\000\000\000\000\000\000\225\000\000\0002\000\000" + .byte 5 + .space 39 - .section __TEXT,__const + .p2align 3, 0x0 l_anon.[ID].14: - .ascii "ForgetableIvars" + .byte 7 + .space 39 + .p2align 3, 0x0 l_anon.[ID].15: - .ascii "NoIvars" + .byte 9 + .space 39 l_anon.[ID].16: - .ascii "DropIvars" + .ascii "failed retrieving instance variable on newly declared class" l_anon.[ID].17: - .ascii "_obj_option" + .ascii "$WORKSPACE/objc2/src/__macro_helpers/declared_ivars.rs" - .section __TEXT,__literal4,4byte_literals + .section __DATA,__const + .p2align 3, 0x0 l_anon.[ID].18: - .ascii "_obj" + .quad l_anon.[ID].17 + .asciz "T\000\000\000\000\000\000\000\022\001\000\000\016\000\000" + .section __TEXT,__const l_anon.[ID].19: - .ascii "_bar" + .ascii "failed retrieving drop flag instance variable on newly declared class" + .section __DATA,__const + .p2align 3, 0x0 l_anon.[ID].20: - .ascii "_foo" + .quad l_anon.[ID].17 + .asciz "T\000\000\000\000\000\000\000\036\001\000\000\016\000\000" .section __TEXT,__const l_anon.[ID].21: + .ascii "ForgetableIvars" + +l_anon.[ID].22: + .ascii "NoIvars" + +l_anon.[ID].23: + .ascii "DropIvars" + +l_anon.[ID].24: + .ascii "called `Option::unwrap()` on a `None` value" + +l_anon.[ID].25: + .ascii "$RUSTC/library/std/src/sync/once.rs" + + .section __DATA,__const + .p2align 3, 0x0 +l_anon.[ID].26: + .quad l_anon.[ID].25 + .asciz "p\000\000\000\000\000\000\000\225\000\000\0002\000\000" + + .globl SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 1) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 1),8,3 + .globl SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 1) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 1),8,3 + .section __TEXT,__const +l_anon.[ID].27: .ascii "crates/$DIR/lib.rs" .section __DATA,__const .p2align 3, 0x0 -l_anon.[ID].22: - .quad l_anon.[ID].21 - .asciz "5\000\000\000\000\000\000\000\017\000\000\000\001\000\000" +l_anon.[ID].28: + .quad l_anon.[ID].27 + .asciz "5\000\000\000\000\000\000\000\016\000\000\000\001\000\000" -.zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),8,3 .section __TEXT,__literal8,8byte_literals -l_anon.[ID].23: +l_anon.[ID].29: .ascii "NSObject" .section __TEXT,__const -l_anon.[ID].24: +l_anon.[ID].30: .ascii "NSCopying" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f249b8b52b9a1205 -L_OBJC_METH_VAR_NAME_f249b8b52b9a1205: + .globl L_OBJC_METH_VAR_NAME_03fd85b0462f54e9 +L_OBJC_METH_VAR_NAME_03fd85b0462f54e9: .asciz "classMethod" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_f249b8b52b9a1205 + .globl L_OBJC_SELECTOR_REFERENCES_03fd85b0462f54e9 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_f249b8b52b9a1205: - .quad L_OBJC_METH_VAR_NAME_f249b8b52b9a1205 +L_OBJC_SELECTOR_REFERENCES_03fd85b0462f54e9: + .quad L_OBJC_METH_VAR_NAME_03fd85b0462f54e9 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_f249b8b52b9a1205 + .globl L_OBJC_IMAGE_INFO_03fd85b0462f54e9 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f249b8b52b9a1205: +L_OBJC_IMAGE_INFO_03fd85b0462f54e9: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_2607fe9c3979c381 -L_OBJC_METH_VAR_NAME_2607fe9c3979c381: + .globl L_OBJC_METH_VAR_NAME_cf773331f3cfba54 +L_OBJC_METH_VAR_NAME_cf773331f3cfba54: .asciz "method" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_2607fe9c3979c381 + .globl L_OBJC_SELECTOR_REFERENCES_cf773331f3cfba54 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_2607fe9c3979c381: - .quad L_OBJC_METH_VAR_NAME_2607fe9c3979c381 +L_OBJC_SELECTOR_REFERENCES_cf773331f3cfba54: + .quad L_OBJC_METH_VAR_NAME_cf773331f3cfba54 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_2607fe9c3979c381 + .globl L_OBJC_IMAGE_INFO_cf773331f3cfba54 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_2607fe9c3979c381: +L_OBJC_IMAGE_INFO_cf773331f3cfba54: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_5a4952192b00de7f -L_OBJC_METH_VAR_NAME_5a4952192b00de7f: + .globl L_OBJC_METH_VAR_NAME_abdcbb85641cd990 +L_OBJC_METH_VAR_NAME_abdcbb85641cd990: .asciz "methodBool:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_5a4952192b00de7f + .globl L_OBJC_SELECTOR_REFERENCES_abdcbb85641cd990 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_5a4952192b00de7f: - .quad L_OBJC_METH_VAR_NAME_5a4952192b00de7f +L_OBJC_SELECTOR_REFERENCES_abdcbb85641cd990: + .quad L_OBJC_METH_VAR_NAME_abdcbb85641cd990 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_5a4952192b00de7f + .globl L_OBJC_IMAGE_INFO_abdcbb85641cd990 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_5a4952192b00de7f: +L_OBJC_IMAGE_INFO_abdcbb85641cd990: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_bf0386bc74a73c00 -L_OBJC_METH_VAR_NAME_bf0386bc74a73c00: + .globl L_OBJC_METH_VAR_NAME_ef8de92414f2d9c8 +L_OBJC_METH_VAR_NAME_ef8de92414f2d9c8: .asciz "methodId" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_bf0386bc74a73c00 + .globl L_OBJC_SELECTOR_REFERENCES_ef8de92414f2d9c8 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_bf0386bc74a73c00: - .quad L_OBJC_METH_VAR_NAME_bf0386bc74a73c00 +L_OBJC_SELECTOR_REFERENCES_ef8de92414f2d9c8: + .quad L_OBJC_METH_VAR_NAME_ef8de92414f2d9c8 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_bf0386bc74a73c00 + .globl L_OBJC_IMAGE_INFO_ef8de92414f2d9c8 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_bf0386bc74a73c00: +L_OBJC_IMAGE_INFO_ef8de92414f2d9c8: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_6f0ab2a047fe4a09 -L_OBJC_METH_VAR_NAME_6f0ab2a047fe4a09: + .globl L_OBJC_METH_VAR_NAME_4a611090161f3fae +L_OBJC_METH_VAR_NAME_4a611090161f3fae: .asciz "methodIdWithParam:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_6f0ab2a047fe4a09 + .globl L_OBJC_SELECTOR_REFERENCES_4a611090161f3fae .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_6f0ab2a047fe4a09: - .quad L_OBJC_METH_VAR_NAME_6f0ab2a047fe4a09 +L_OBJC_SELECTOR_REFERENCES_4a611090161f3fae: + .quad L_OBJC_METH_VAR_NAME_4a611090161f3fae .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_6f0ab2a047fe4a09 + .globl L_OBJC_IMAGE_INFO_4a611090161f3fae .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_6f0ab2a047fe4a09: +L_OBJC_IMAGE_INFO_4a611090161f3fae: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_062e7a5fdd2d2571 -L_OBJC_METH_VAR_NAME_062e7a5fdd2d2571: + .globl L_OBJC_METH_VAR_NAME_2837f061c311eb14 +L_OBJC_METH_VAR_NAME_2837f061c311eb14: .asciz "copyWithZone:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_062e7a5fdd2d2571 + .globl L_OBJC_SELECTOR_REFERENCES_2837f061c311eb14 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_062e7a5fdd2d2571: - .quad L_OBJC_METH_VAR_NAME_062e7a5fdd2d2571 +L_OBJC_SELECTOR_REFERENCES_2837f061c311eb14: + .quad L_OBJC_METH_VAR_NAME_2837f061c311eb14 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_062e7a5fdd2d2571 + .globl L_OBJC_IMAGE_INFO_2837f061c311eb14 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_062e7a5fdd2d2571: +L_OBJC_IMAGE_INFO_2837f061c311eb14: .asciz "\000\000\000\000@\000\000" - .section __TEXT,__const + .globl SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2),8,3 + .globl SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 2) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 2),8,3 + .section __DATA,__const .p2align 3, 0x0 -l_anon.[ID].25: - .byte 5 - .space 39 +l_anon.[ID].31: + .quad l_anon.[ID].27 + .asciz "5\000\000\000\000\000\000\000O\000\000\000\001\000\000" + + .section __TEXT,__objc_methname,cstring_literals + .globl L_OBJC_METH_VAR_NAME_af8966656b8b2b6c +L_OBJC_METH_VAR_NAME_af8966656b8b2b6c: + .asciz "init" + .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip + .globl L_OBJC_SELECTOR_REFERENCES_af8966656b8b2b6c .p2align 3, 0x0 -l_anon.[ID].26: - .byte 7 - .space 39 +L_OBJC_SELECTOR_REFERENCES_af8966656b8b2b6c: + .quad L_OBJC_METH_VAR_NAME_af8966656b8b2b6c + + .section __DATA,__objc_imageinfo,regular,no_dead_strip + .globl L_OBJC_IMAGE_INFO_af8966656b8b2b6c + .p2align 2, 0x0 +L_OBJC_IMAGE_INFO_af8966656b8b2b6c: + .asciz "\000\000\000\000@\000\000" + .globl SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0),8,3 + .globl SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0),8,3 .section __DATA,__const .p2align 3, 0x0 -l_anon.[ID].27: - .quad l_anon.[ID].21 - .asciz "5\000\000\000\000\000\000\000I\000\000\000\001\000\000" +l_anon.[ID].32: + .quad l_anon.[ID].27 + .asciz "5\000\000\000\000\000\000\000x\000\000\000\001\000\000" -.zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),8,3 + .section __TEXT,__objc_methname,cstring_literals + .globl L_OBJC_METH_VAR_NAME_6edddcebbded8f32 +L_OBJC_METH_VAR_NAME_6edddcebbded8f32: + .asciz "init" + + .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip + .globl L_OBJC_SELECTOR_REFERENCES_6edddcebbded8f32 .p2align 3, 0x0 -l_anon.[ID].28: - .quad l_anon.[ID].21 - .asciz "5\000\000\000\000\000\000\000u\000\000\000\001\000\000" +L_OBJC_SELECTOR_REFERENCES_6edddcebbded8f32: + .quad L_OBJC_METH_VAR_NAME_6edddcebbded8f32 + + .section __DATA,__objc_imageinfo,regular,no_dead_strip + .globl L_OBJC_IMAGE_INFO_6edddcebbded8f32 + .p2align 2, 0x0 +L_OBJC_IMAGE_INFO_6edddcebbded8f32: + .asciz "\000\000\000\000@\000\000" -.zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),8,3 +.zerofill __DATA,__bss,__MergedGlobals,48,3 .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_declare_class/expected/apple-x86_64.s b/crates/test-assembly/crates/test_declare_class/expected/apple-x86_64.s index 4716eb56e..da6c6e4f1 100644 --- a/crates/test-assembly/crates/test_declare_class/expected/apple-x86_64.s +++ b/crates/test-assembly/crates/test_declare_class/expected/apple-x86_64.s @@ -21,6 +21,47 @@ SYM(core[CRATE_ID]::ptr::drop_in_place::<::call pop rbp ret + .p2align 4, 0x90 +SYM(objc2[CRATE_ID]::__macro_helpers::declared_ivars::dealloc::, 0): + push rbp + mov rbp, rsp + push r15 + push r14 + push rbx + sub rsp, 24 + mov rbx, rsi + mov r14, rdi + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0)] + movzx eax, byte ptr [rdi + rax] + test eax, eax + je LBB3_5 + cmp eax, 255 + jne LBB3_3 + call SYM(::drop, 0) +LBB3_3: + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)] + mov rdi, qword ptr [r14 + rax] + mov r15, qword ptr [r14 + rax + 8] + call _objc_release + test r15, r15 + je LBB3_5 + mov rdi, r15 + call _objc_release +LBB3_5: + mov rax, qword ptr [rip + L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPCREL] + mov rax, qword ptr [rax] + mov qword ptr [rbp - 40], r14 + mov qword ptr [rbp - 32], rax + lea rdi, [rbp - 40] + mov rsi, rbx + call _objc_msgSendSuper + add rsp, 24 + pop rbx + pop r14 + pop r15 + pop rbp + ret + .p2align 4, 0x90 SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0): push rbp @@ -33,16 +74,16 @@ SYM(::call_once::<::call_once::<::call_once::<::call_once::<::call_once::<::class::{closure#0}>::{closure#0}, 0): push rbp mov rbp, rsp - push r15 push r14 push rbx - push rax + sub rsp, 64 mov rax, qword ptr [rdi] cmp byte ptr [rax], 0 mov byte ptr [rax], 0 - je LBB4_3 + je LBB5_5 mov rax, qword ptr [rip + L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPCREL] mov rdx, qword ptr [rax] - lea rdi, [rip + l_anon.[ID].16] + lea rdi, [rip + l_anon.[ID].23] mov esi, 9 call SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) test rax, rax - je LBB4_4 - mov qword ptr [rbp - 32], rax - lea rsi, [rip + L_anon.[ID].18] - lea rbx, [rip + l_anon.[ID].4] - lea r14, [rbp - 32] - mov edx, 4 - mov ecx, 8 - mov rdi, r14 - mov r8d, 3 - mov r9, rbx - call SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) - lea rsi, [rip + l_anon.[ID].17] - mov edx, 11 - mov ecx, 8 - mov rdi, r14 - mov r8d, 3 - mov r9, rbx - call SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) + je LBB5_6 + mov qword ptr [rbp - 72], rax mov rax, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_dealloc@GOTPCREL] mov rsi, qword ptr [rax] - lea r15, [rip + l_anon.[ID].3] + lea rbx, [rip + l_anon.[ID].3] lea r8, [rip + l_anon.[ID].5] - lea r9, [rip + SYM(::class::{closure#0}::__objc2_dealloc, 0)] - mov rdi, r14 - mov rdx, r15 + lea r9, [rip + SYM(objc2[CRATE_ID]::__macro_helpers::declared_ivars::dealloc::, 0)] + lea rdi, [rbp - 72] + mov rdx, rbx xor ecx, ecx call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) + mov rax, qword ptr [rbp - 72] + mov qword ptr [rbp - 24], rax mov rax, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_init@GOTPCREL] mov rsi, qword ptr [rax] + lea r8, [rip + l_anon.[ID].4] lea r9, [rip + _init_drop_ivars] - mov rdi, r14 - mov rdx, r15 + lea rdi, [rbp - 24] + mov rdx, rbx xor ecx, ecx - mov r8, rbx call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) + mov rax, qword ptr [rbp - 24] + mov qword ptr [rbp - 32], rax + mov qword ptr [rbp - 64], 16 + lea rax, [rip + l_anon.[ID].15] + mov qword ptr [rbp - 56], rax + mov byte ptr [rbp - 72], 27 + lea r14, [rip + l_anon.[ID].11] + lea rbx, [rbp - 32] + lea r9, [rbp - 72] + mov edx, 5 + mov ecx, 16 + mov rdi, rbx + mov rsi, r14 + mov r8d, 3 + call SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) + lea rsi, [rip + l_anon.[ID].12] + lea r9, [rip + l_anon.[ID].13] + mov edx, 9 + mov ecx, 1 + mov rdi, rbx + xor r8d, r8d + call SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) mov rdi, qword ptr [rbp - 32] call SYM(objc2::declare::ClassBuilder::register::GENERATED_ID, 0) - add rsp, 8 + mov rbx, rax + mov edx, 5 + mov rdi, rax + mov rsi, r14 + call SYM(objc2::runtime::AnyClass::instance_variable::GENERATED_ID, 0) + test rax, rax + je LBB5_7 + mov rdi, rax + call _ivar_getOffset + mov r14, rax + lea rsi, [rip + l_anon.[ID].12] + mov edx, 9 + mov rdi, rbx + call SYM(objc2::runtime::AnyClass::instance_variable::GENERATED_ID, 0) + test rax, rax + je LBB5_8 + mov rdi, rax + call _ivar_getOffset + mov qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 1).0], rbx + mov qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)], r14 + mov qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0)], rax + add rsp, 64 pop rbx pop r14 - pop r15 pop rbp ret -LBB4_3: - lea rdi, [rip + l_anon.[ID].11] - lea rdx, [rip + l_anon.[ID].13] +LBB5_5: + lea rdi, [rip + l_anon.[ID].24] + lea rdx, [rip + l_anon.[ID].26] mov esi, 43 call SYM(core::panicking::panic::GENERATED_ID, 0) -LBB4_4: - lea rdi, [rip + l_anon.[ID].16] - lea rdx, [rip + l_anon.[ID].28] +LBB5_6: + lea rdi, [rip + l_anon.[ID].23] + lea rdx, [rip + l_anon.[ID].32] mov esi, 9 call SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) +LBB5_7: + lea rdi, [rip + l_anon.[ID].16] + lea rdx, [rip + l_anon.[ID].18] + mov esi, 59 + call SYM(core::option::expect_failed::GENERATED_ID, 0) +LBB5_8: + lea rdi, [rip + l_anon.[ID].19] + lea rdx, [rip + l_anon.[ID].20] + mov esi, 69 + call SYM(core::option::expect_failed::GENERATED_ID, 0) .p2align 4, 0x90 SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0): @@ -204,68 +285,75 @@ SYM(::call_once::<::class::{closure#0}::__objc2_dealloc, 0)] - mov rdi, rbx - mov rdx, r14 - xor ecx, ecx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) mov rax, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_init@GOTPCREL] mov rsi, qword ptr [rax] + lea rdx, [rip + l_anon.[ID].3] lea r8, [rip + l_anon.[ID].4] lea r9, [rip + _init_forgetable_ivars] - mov rdi, rbx - mov rdx, r14 + lea rdi, [rbp - 24] xor ecx, ecx call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - mov rdi, qword ptr [rbp - 24] + mov rax, qword ptr [rbp - 24] + mov qword ptr [rbp - 32], rax + mov qword ptr [rbp - 64], 8 + lea rax, [rip + l_anon.[ID].14] + mov qword ptr [rbp - 56], rax + mov byte ptr [rbp - 72], 27 + lea r14, [rip + l_anon.[ID].11] + lea rdi, [rbp - 32] + lea r9, [rbp - 72] + mov edx, 5 + mov ecx, 8 + mov rsi, r14 + mov r8d, 2 + call SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) + mov rdi, qword ptr [rbp - 32] call SYM(objc2::declare::ClassBuilder::register::GENERATED_ID, 0) - add rsp, 16 + mov rbx, rax + mov edx, 5 + mov rdi, rax + mov rsi, r14 + call SYM(objc2::runtime::AnyClass::instance_variable::GENERATED_ID, 0) + test rax, rax + je LBB6_6 + mov rdi, rax + call _ivar_getOffset + mov qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 2).0], rbx + mov qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2)], rax + mov qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 2)], 0 + add rsp, 64 pop rbx pop r14 pop rbp ret -LBB5_3: - lea rdi, [rip + l_anon.[ID].11] - lea rdx, [rip + l_anon.[ID].13] +LBB6_4: + lea rdi, [rip + l_anon.[ID].24] + lea rdx, [rip + l_anon.[ID].26] mov esi, 43 call SYM(core::panicking::panic::GENERATED_ID, 0) -LBB5_4: - lea rdi, [rip + l_anon.[ID].14] - lea rdx, [rip + l_anon.[ID].27] +LBB6_5: + lea rdi, [rip + l_anon.[ID].21] + lea rdx, [rip + l_anon.[ID].31] mov esi, 15 call SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) +LBB6_6: + lea rdi, [rip + l_anon.[ID].16] + lea rdx, [rip + l_anon.[ID].18] + mov esi, 59 + call SYM(core::option::expect_failed::GENERATED_ID, 0) .p2align 4, 0x90 SYM(<::call_once<::class::{closure#0}>::{closure#0} as core[CRATE_ID]::ops::function::FnOnce<(&std[CRATE_ID]::sync::once::OnceState,)>>::call_once::{shim:vtable#0}, 0): @@ -309,62 +397,37 @@ SYM(<::call_once<::class::REGISTER_CLASS, 0)] cmp rax, 3 - jne LBB9_1 -LBB9_2: - lea rdi, [rip + l_anon.[ID].14] - mov esi, 15 - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - test rax, rax - je LBB9_4 - add rsp, 16 - pop rbp + jne LBB10_1 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 2).0] ret -LBB9_1: +LBB10_1: + push rbp + mov rbp, rsp + sub rsp, 16 mov byte ptr [rbp - 1], 1 lea rax, [rbp - 1] mov qword ptr [rbp - 16], rax lea rdi, [rip + SYM(::class::REGISTER_CLASS, 0)] lea rcx, [rip + l_anon.[ID].2] - lea r8, [rip + l_anon.[ID].27] + lea r8, [rip + l_anon.[ID].31] lea rdx, [rbp - 16] xor esi, esi call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - jmp LBB9_2 -LBB9_4: - lea rdi, [rip + l_anon.[ID].11] - lea rdx, [rip + l_anon.[ID].27] - mov esi, 43 - call SYM(core::panicking::panic::GENERATED_ID, 0) + add rsp, 16 + pop rbp + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 2).0] + ret .globl _access_forgetable_ivars .p2align 4, 0x90 _access_forgetable_ivars: push rbp mov rbp, rsp - push r14 - push rbx - mov rbx, rdi - lea rsi, [rip + L_anon.[ID].20] - mov edx, 4 - call SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - mov rdi, rax - call _ivar_getOffset - movzx r14d, byte ptr [rbx + rax] - lea rsi, [rip + L_anon.[ID].19] - mov edx, 4 - mov rdi, rbx - call SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - mov rdi, rax - call _ivar_getOffset - mov edx, dword ptr [rbx + rax] - mov eax, r14d - pop rbx - pop r14 + mov rcx, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2)] + movzx eax, byte ptr [rdi + rcx + 4] + mov edx, dword ptr [rdi + rcx] pop rbp ret @@ -381,126 +444,91 @@ SYM( .globl _access_drop_ivars_class .p2align 4, 0x90 _access_drop_ivars_class: - push rbp - mov rbp, rsp - sub rsp, 16 mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] cmp rax, 3 - jne LBB12_1 -LBB12_2: - lea rdi, [rip + l_anon.[ID].16] - mov esi, 9 - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - test rax, rax - je LBB12_4 - add rsp, 16 - pop rbp + jne LBB13_1 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 1).0] ret -LBB12_1: +LBB13_1: + push rbp + mov rbp, rsp + sub rsp, 16 mov byte ptr [rbp - 1], 1 lea rax, [rbp - 1] mov qword ptr [rbp - 16], rax lea rdi, [rip + SYM(::class::REGISTER_CLASS, 0)] lea rcx, [rip + l_anon.[ID].1] - lea r8, [rip + l_anon.[ID].28] + lea r8, [rip + l_anon.[ID].32] lea rdx, [rbp - 16] xor esi, esi call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - jmp LBB12_2 -LBB12_4: - lea rdi, [rip + l_anon.[ID].11] - lea rdx, [rip + l_anon.[ID].28] - mov esi, 43 - call SYM(core::panicking::panic::GENERATED_ID, 0) + add rsp, 16 + pop rbp + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 1).0] + ret .globl _access_drop_ivars .p2align 4, 0x90 _access_drop_ivars: push rbp mov rbp, rsp - push rbx - push rax - mov rbx, rdi - lea rsi, [rip + L_anon.[ID].18] - mov edx, 4 - call SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - mov rdi, rax - call _ivar_getOffset - mov rax, qword ptr [rbx + rax] - add rsp, 8 - pop rbx + mov rcx, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)] + mov rax, qword ptr [rdi + rcx] + mov rdx, qword ptr [rdi + rcx + 8] pop rbp ret .globl SYM(::class, 0) .p2align 4, 0x90 SYM(::class, 0): - push rbp - mov rbp, rsp - sub rsp, 16 mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] cmp rax, 3 - jne LBB14_1 -LBB14_2: - lea rdi, [rip + l_anon.[ID].15] - mov esi, 7 - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - test rax, rax - je LBB14_4 - add rsp, 16 - pop rbp + jne LBB15_1 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 0).0] ret -LBB14_1: +LBB15_1: + push rbp + mov rbp, rsp + sub rsp, 16 mov byte ptr [rbp - 1], 1 lea rax, [rbp - 1] mov qword ptr [rbp - 16], rax lea rdi, [rip + SYM(::class::REGISTER_CLASS, 0)] lea rcx, [rip + l_anon.[ID].0] - lea r8, [rip + l_anon.[ID].22] + lea r8, [rip + l_anon.[ID].28] lea rdx, [rbp - 16] xor esi, esi call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - jmp LBB14_2 -LBB14_4: - lea rdi, [rip + l_anon.[ID].11] - lea rdx, [rip + l_anon.[ID].22] - mov esi, 43 - call SYM(core::panicking::panic::GENERATED_ID, 0) + add rsp, 16 + pop rbp + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 0).0] + ret .globl _get_class .p2align 4, 0x90 _get_class: - push rbp - mov rbp, rsp - sub rsp, 16 mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] cmp rax, 3 - jne LBB15_1 -LBB15_2: - lea rdi, [rip + l_anon.[ID].15] - mov esi, 7 - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - test rax, rax - je LBB15_4 - add rsp, 16 - pop rbp + jne LBB16_1 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 0).0] ret -LBB15_1: +LBB16_1: + push rbp + mov rbp, rsp + sub rsp, 16 mov byte ptr [rbp - 1], 1 lea rax, [rbp - 1] mov qword ptr [rbp - 16], rax lea rdi, [rip + SYM(::class::REGISTER_CLASS, 0)] lea rcx, [rip + l_anon.[ID].0] - lea r8, [rip + l_anon.[ID].22] + lea r8, [rip + l_anon.[ID].28] lea rdx, [rbp - 16] xor esi, esi call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - jmp LBB15_2 -LBB15_4: - lea rdi, [rip + l_anon.[ID].11] - lea rdx, [rip + l_anon.[ID].22] - mov esi, 43 - call SYM(core::panicking::panic::GENERATED_ID, 0) + add rsp, 16 + pop rbp + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 0).0] + ret .globl _method_simple .p2align 4, 0x90 @@ -529,38 +557,28 @@ _method_id: sub rsp, 16 mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] cmp rax, 3 - jne LBB18_1 -LBB18_2: - lea rdi, [rip + l_anon.[ID].15] - mov esi, 7 - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - test rax, rax - je LBB18_4 - mov rcx, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_new@GOTPCREL] - mov rsi, qword ptr [rcx] - mov rdi, rax + jne LBB19_1 +LBB19_2: + mov rdi, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 0).0] + mov rax, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_new@GOTPCREL] + mov rsi, qword ptr [rax] call _objc_msgSend mov rdi, rax call _objc_autoreleaseReturnValue add rsp, 16 pop rbp ret -LBB18_1: +LBB19_1: mov byte ptr [rbp - 1], 1 lea rax, [rbp - 1] mov qword ptr [rbp - 16], rax lea rdi, [rip + SYM(::class::REGISTER_CLASS, 0)] lea rcx, [rip + l_anon.[ID].0] - lea r8, [rip + l_anon.[ID].22] + lea r8, [rip + l_anon.[ID].28] lea rdx, [rbp - 16] xor esi, esi call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - jmp LBB18_2 -LBB18_4: - lea rdi, [rip + l_anon.[ID].11] - lea rdx, [rip + l_anon.[ID].22] - mov esi, 43 - call SYM(core::panicking::panic::GENERATED_ID, 0) + jmp LBB19_2 .globl _method_id_with_param .p2align 4, 0x90 @@ -573,13 +591,13 @@ _method_id_with_param: call SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) mov rbx, rax test r14b, r14b - je LBB19_2 + je LBB20_2 call SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) mov r14, rax mov rdi, rbx call _objc_release mov rbx, r14 -LBB19_2: +LBB20_2: mov rdi, rbx pop rbx pop r14 @@ -594,204 +612,100 @@ _copyWithZone: sub rsp, 16 mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] cmp rax, 3 - jne LBB20_1 -LBB20_2: - lea rdi, [rip + l_anon.[ID].15] - mov esi, 7 - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - test rax, rax - je LBB20_4 - mov rcx, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_new@GOTPCREL] - mov rsi, qword ptr [rcx] - mov rdi, rax + jne LBB21_1 +LBB21_2: + mov rdi, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 0).0] + mov rax, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_new@GOTPCREL] + mov rsi, qword ptr [rax] call _objc_msgSend add rsp, 16 pop rbp ret -LBB20_1: +LBB21_1: mov byte ptr [rbp - 1], 1 lea rax, [rbp - 1] mov qword ptr [rbp - 16], rax lea rdi, [rip + SYM(::class::REGISTER_CLASS, 0)] lea rcx, [rip + l_anon.[ID].0] - lea r8, [rip + l_anon.[ID].22] + lea r8, [rip + l_anon.[ID].28] lea rdx, [rbp - 16] xor esi, esi call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - jmp LBB20_2 -LBB20_4: - lea rdi, [rip + l_anon.[ID].11] - lea rdx, [rip + l_anon.[ID].22] - mov esi, 43 - call SYM(core::panicking::panic::GENERATED_ID, 0) + jmp LBB21_2 .globl SYM(::class, 0) .p2align 4, 0x90 SYM(::class, 0): - push rbp - mov rbp, rsp - sub rsp, 16 mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] cmp rax, 3 - jne LBB21_1 -LBB21_2: - lea rdi, [rip + l_anon.[ID].14] - mov esi, 15 - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - test rax, rax - je LBB21_4 - add rsp, 16 - pop rbp + jne LBB22_1 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 2).0] ret -LBB21_1: +LBB22_1: + push rbp + mov rbp, rsp + sub rsp, 16 mov byte ptr [rbp - 1], 1 lea rax, [rbp - 1] mov qword ptr [rbp - 16], rax lea rdi, [rip + SYM(::class::REGISTER_CLASS, 0)] lea rcx, [rip + l_anon.[ID].2] - lea r8, [rip + l_anon.[ID].27] + lea r8, [rip + l_anon.[ID].31] lea rdx, [rbp - 16] xor esi, esi call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - jmp LBB21_2 -LBB21_4: - lea rdi, [rip + l_anon.[ID].11] - lea rdx, [rip + l_anon.[ID].27] - mov esi, 43 - call SYM(core::panicking::panic::GENERATED_ID, 0) - - .p2align 4, 0x90 -SYM(::class::{closure#0}::__objc2_dealloc, 0): - push rbp - mov rbp, rsp - sub rsp, 16 - mov rax, qword ptr [rip + L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPCREL] - mov rax, qword ptr [rax] - mov qword ptr [rbp - 16], rdi - mov qword ptr [rbp - 8], rax - lea rdi, [rbp - 16] - call _objc_msgSendSuper add rsp, 16 pop rbp + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 2).0] ret .globl _init_forgetable_ivars .p2align 4, 0x90 _init_forgetable_ivars: + test rdi, rdi + je LBB23_2 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2)] + mov dword ptr [rdi + rax], 43 + mov byte ptr [rdi + rax + 4], 42 +LBB23_2: push rbp mov rbp, rsp - push rbx - sub rsp, 24 - mov rax, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_init@GOTPCREL] - mov rsi, qword ptr [rax] + sub rsp, 16 + mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_af8966656b8b2b6c] mov rax, qword ptr [rip + L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPCREL] mov rax, qword ptr [rax] - mov qword ptr [rbp - 24], rdi - mov qword ptr [rbp - 16], rax - lea rdi, [rbp - 24] + mov qword ptr [rbp - 16], rdi + mov qword ptr [rbp - 8], rax + lea rdi, [rbp - 16] call _objc_msgSendSuper - mov rbx, rax - test rax, rax - je LBB23_2 - lea rsi, [rip + L_anon.[ID].20] - mov edx, 4 - mov rdi, rbx - call SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - mov rdi, rax - call _ivar_getOffset - mov byte ptr [rbx + rax], 42 - lea rsi, [rip + L_anon.[ID].19] - mov edx, 4 - mov rdi, rbx - call SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - mov rdi, rax - call _ivar_getOffset - mov dword ptr [rbx + rax], 43 -LBB23_2: - mov rax, rbx - add rsp, 24 - pop rbx + add rsp, 16 pop rbp ret .globl SYM(::class, 0) .p2align 4, 0x90 SYM(::class, 0): - push rbp - mov rbp, rsp - sub rsp, 16 mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] cmp rax, 3 jne LBB24_1 -LBB24_2: - lea rdi, [rip + l_anon.[ID].16] - mov esi, 9 - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - test rax, rax - je LBB24_4 - add rsp, 16 - pop rbp + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 1).0] ret LBB24_1: + push rbp + mov rbp, rsp + sub rsp, 16 mov byte ptr [rbp - 1], 1 lea rax, [rbp - 1] mov qword ptr [rbp - 16], rax lea rdi, [rip + SYM(::class::REGISTER_CLASS, 0)] lea rcx, [rip + l_anon.[ID].1] - lea r8, [rip + l_anon.[ID].28] + lea r8, [rip + l_anon.[ID].32] lea rdx, [rbp - 16] xor esi, esi call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - jmp LBB24_2 -LBB24_4: - lea rdi, [rip + l_anon.[ID].11] - lea rdx, [rip + l_anon.[ID].28] - mov esi, 43 - call SYM(core::panicking::panic::GENERATED_ID, 0) - - .p2align 4, 0x90 -SYM(::class::{closure#0}::__objc2_dealloc, 0): - push rbp - mov rbp, rsp - push r14 - push rbx - sub rsp, 16 - mov rbx, rsi - mov r14, rdi - call SYM(::drop, 0) - lea rsi, [rip + L_anon.[ID].18] - mov edx, 4 - mov rdi, r14 - call SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - mov rdi, rax - call _ivar_getOffset - mov rdi, qword ptr [r14 + rax] - test rdi, rdi - je LBB25_2 - call _objc_release -LBB25_2: - lea rsi, [rip + l_anon.[ID].17] - mov edx, 11 - mov rdi, r14 - call SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - mov rdi, rax - call _ivar_getOffset - mov rdi, qword ptr [r14 + rax] - test rdi, rdi - je LBB25_4 - call _objc_release -LBB25_4: - mov rax, qword ptr [rip + L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPCREL] - mov rax, qword ptr [rax] - mov qword ptr [rbp - 32], r14 - mov qword ptr [rbp - 24], rax - lea rdi, [rbp - 32] - mov rsi, rbx - call _objc_msgSendSuper add rsp, 16 - pop rbx - pop r14 pop rbp + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 1).0] ret .globl _init_drop_ivars @@ -799,43 +713,45 @@ LBB25_4: _init_drop_ivars: push rbp mov rbp, rsp + push r15 push r14 push rbx - sub rsp, 16 - mov rax, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_init@GOTPCREL] - mov rsi, qword ptr [rax] - mov rax, qword ptr [rip + L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPCREL] - mov rax, qword ptr [rax] - mov qword ptr [rbp - 32], rdi - mov qword ptr [rbp - 24], rax - lea rdi, [rbp - 32] - call _objc_msgSendSuper - mov rbx, rax - test rax, rax - je LBB26_2 + sub rsp, 24 + mov rbx, rdi call SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) mov r14, rax - lea rsi, [rip + L_anon.[ID].18] - mov edx, 4 - mov rdi, rbx - call SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - mov rdi, rax - call _ivar_getOffset - mov qword ptr [rbx + rax], r14 call SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) - mov r14, rax - lea rsi, [rip + l_anon.[ID].17] - mov edx, 11 - mov rdi, rbx - call SYM(objc2::runtime::AnyObject::lookup_instance_variable_dynamically::GENERATED_ID, 0) - mov rdi, rax - call _ivar_getOffset + mov r15, rax + test rbx, rbx + je LBB25_2 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)] mov qword ptr [rbx + rax], r14 -LBB26_2: - mov rax, rbx - add rsp, 16 + mov qword ptr [rbx + rax + 8], r15 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0)] + mov byte ptr [rbx + rax], 15 + jmp LBB25_3 +LBB25_2: + mov rdi, r14 + call _objc_release + mov rdi, r15 + call _objc_release +LBB25_3: + mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_6edddcebbded8f32] + mov rax, qword ptr [rip + L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPCREL] + mov rax, qword ptr [rax] + mov qword ptr [rbp - 40], rbx + mov qword ptr [rbp - 32], rax + lea rdi, [rbp - 40] + call _objc_msgSendSuper + test rax, rax + je LBB25_5 + mov rcx, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0)] + mov byte ptr [rax + rcx], -1 +LBB25_5: + add rsp, 24 pop rbx pop r14 + pop r15 pop rbp ret @@ -908,183 +824,252 @@ l_anon.[ID].10: .space 39 l_anon.[ID].11: - .ascii "called `Option::unwrap()` on a `None` value" + .ascii "ivars" l_anon.[ID].12: - .ascii "$RUSTC/library/std/src/sync/once.rs" + .ascii "drop_flag" - .section __DATA,__const .p2align 3, 0x0 l_anon.[ID].13: - .quad l_anon.[ID].12 - .asciz "p\000\000\000\000\000\000\000\225\000\000\0002\000\000" + .byte 5 + .space 39 - .section __TEXT,__const + .p2align 3, 0x0 l_anon.[ID].14: - .ascii "ForgetableIvars" + .byte 7 + .space 39 + .p2align 3, 0x0 l_anon.[ID].15: - .ascii "NoIvars" + .byte 9 + .space 39 l_anon.[ID].16: - .ascii "DropIvars" + .ascii "failed retrieving instance variable on newly declared class" l_anon.[ID].17: - .ascii "_obj_option" + .ascii "$WORKSPACE/objc2/src/__macro_helpers/declared_ivars.rs" - .section __TEXT,__literal4,4byte_literals -L_anon.[ID].18: - .ascii "_obj" + .section __DATA,__const + .p2align 3, 0x0 +l_anon.[ID].18: + .quad l_anon.[ID].17 + .asciz "T\000\000\000\000\000\000\000\022\001\000\000\016\000\000" -L_anon.[ID].19: - .ascii "_bar" + .section __TEXT,__const +l_anon.[ID].19: + .ascii "failed retrieving drop flag instance variable on newly declared class" -L_anon.[ID].20: - .ascii "_foo" + .section __DATA,__const + .p2align 3, 0x0 +l_anon.[ID].20: + .quad l_anon.[ID].17 + .asciz "T\000\000\000\000\000\000\000\036\001\000\000\016\000\000" .section __TEXT,__const l_anon.[ID].21: + .ascii "ForgetableIvars" + +l_anon.[ID].22: + .ascii "NoIvars" + +l_anon.[ID].23: + .ascii "DropIvars" + +l_anon.[ID].24: + .ascii "called `Option::unwrap()` on a `None` value" + +l_anon.[ID].25: + .ascii "$RUSTC/library/std/src/sync/once.rs" + + .section __DATA,__const + .p2align 3, 0x0 +l_anon.[ID].26: + .quad l_anon.[ID].25 + .asciz "p\000\000\000\000\000\000\000\225\000\000\0002\000\000" + +.zerofill __DATA,__bss,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 0).0,8,3 + .globl SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 1) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 1),8,3 + .globl SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 1) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 1),8,3 + .section __TEXT,__const +l_anon.[ID].27: .ascii "crates/$DIR/lib.rs" .section __DATA,__const .p2align 3, 0x0 -l_anon.[ID].22: - .quad l_anon.[ID].21 - .asciz "5\000\000\000\000\000\000\000\017\000\000\000\001\000\000" +l_anon.[ID].28: + .quad l_anon.[ID].27 + .asciz "5\000\000\000\000\000\000\000\016\000\000\000\001\000\000" .zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),8,3 .section __TEXT,__literal8,8byte_literals -L_anon.[ID].23: +L_anon.[ID].29: .ascii "NSObject" .section __TEXT,__const -l_anon.[ID].24: +l_anon.[ID].30: .ascii "NSCopying" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f249b8b52b9a1205 -L_OBJC_METH_VAR_NAME_f249b8b52b9a1205: + .globl L_OBJC_METH_VAR_NAME_03fd85b0462f54e9 +L_OBJC_METH_VAR_NAME_03fd85b0462f54e9: .asciz "classMethod" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_f249b8b52b9a1205 + .globl L_OBJC_SELECTOR_REFERENCES_03fd85b0462f54e9 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_f249b8b52b9a1205: - .quad L_OBJC_METH_VAR_NAME_f249b8b52b9a1205 +L_OBJC_SELECTOR_REFERENCES_03fd85b0462f54e9: + .quad L_OBJC_METH_VAR_NAME_03fd85b0462f54e9 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_f249b8b52b9a1205 + .globl L_OBJC_IMAGE_INFO_03fd85b0462f54e9 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f249b8b52b9a1205: +L_OBJC_IMAGE_INFO_03fd85b0462f54e9: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_2607fe9c3979c381 -L_OBJC_METH_VAR_NAME_2607fe9c3979c381: + .globl L_OBJC_METH_VAR_NAME_cf773331f3cfba54 +L_OBJC_METH_VAR_NAME_cf773331f3cfba54: .asciz "method" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_2607fe9c3979c381 + .globl L_OBJC_SELECTOR_REFERENCES_cf773331f3cfba54 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_2607fe9c3979c381: - .quad L_OBJC_METH_VAR_NAME_2607fe9c3979c381 +L_OBJC_SELECTOR_REFERENCES_cf773331f3cfba54: + .quad L_OBJC_METH_VAR_NAME_cf773331f3cfba54 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_2607fe9c3979c381 + .globl L_OBJC_IMAGE_INFO_cf773331f3cfba54 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_2607fe9c3979c381: +L_OBJC_IMAGE_INFO_cf773331f3cfba54: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_5a4952192b00de7f -L_OBJC_METH_VAR_NAME_5a4952192b00de7f: + .globl L_OBJC_METH_VAR_NAME_abdcbb85641cd990 +L_OBJC_METH_VAR_NAME_abdcbb85641cd990: .asciz "methodBool:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_5a4952192b00de7f + .globl L_OBJC_SELECTOR_REFERENCES_abdcbb85641cd990 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_5a4952192b00de7f: - .quad L_OBJC_METH_VAR_NAME_5a4952192b00de7f +L_OBJC_SELECTOR_REFERENCES_abdcbb85641cd990: + .quad L_OBJC_METH_VAR_NAME_abdcbb85641cd990 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_5a4952192b00de7f + .globl L_OBJC_IMAGE_INFO_abdcbb85641cd990 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_5a4952192b00de7f: +L_OBJC_IMAGE_INFO_abdcbb85641cd990: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_bf0386bc74a73c00 -L_OBJC_METH_VAR_NAME_bf0386bc74a73c00: + .globl L_OBJC_METH_VAR_NAME_ef8de92414f2d9c8 +L_OBJC_METH_VAR_NAME_ef8de92414f2d9c8: .asciz "methodId" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_bf0386bc74a73c00 + .globl L_OBJC_SELECTOR_REFERENCES_ef8de92414f2d9c8 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_bf0386bc74a73c00: - .quad L_OBJC_METH_VAR_NAME_bf0386bc74a73c00 +L_OBJC_SELECTOR_REFERENCES_ef8de92414f2d9c8: + .quad L_OBJC_METH_VAR_NAME_ef8de92414f2d9c8 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_bf0386bc74a73c00 + .globl L_OBJC_IMAGE_INFO_ef8de92414f2d9c8 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_bf0386bc74a73c00: +L_OBJC_IMAGE_INFO_ef8de92414f2d9c8: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_6f0ab2a047fe4a09 -L_OBJC_METH_VAR_NAME_6f0ab2a047fe4a09: + .globl L_OBJC_METH_VAR_NAME_4a611090161f3fae +L_OBJC_METH_VAR_NAME_4a611090161f3fae: .asciz "methodIdWithParam:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_6f0ab2a047fe4a09 + .globl L_OBJC_SELECTOR_REFERENCES_4a611090161f3fae .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_6f0ab2a047fe4a09: - .quad L_OBJC_METH_VAR_NAME_6f0ab2a047fe4a09 +L_OBJC_SELECTOR_REFERENCES_4a611090161f3fae: + .quad L_OBJC_METH_VAR_NAME_4a611090161f3fae .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_6f0ab2a047fe4a09 + .globl L_OBJC_IMAGE_INFO_4a611090161f3fae .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_6f0ab2a047fe4a09: +L_OBJC_IMAGE_INFO_4a611090161f3fae: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_062e7a5fdd2d2571 -L_OBJC_METH_VAR_NAME_062e7a5fdd2d2571: + .globl L_OBJC_METH_VAR_NAME_2837f061c311eb14 +L_OBJC_METH_VAR_NAME_2837f061c311eb14: .asciz "copyWithZone:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_062e7a5fdd2d2571 + .globl L_OBJC_SELECTOR_REFERENCES_2837f061c311eb14 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_062e7a5fdd2d2571: - .quad L_OBJC_METH_VAR_NAME_062e7a5fdd2d2571 +L_OBJC_SELECTOR_REFERENCES_2837f061c311eb14: + .quad L_OBJC_METH_VAR_NAME_2837f061c311eb14 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_062e7a5fdd2d2571 + .globl L_OBJC_IMAGE_INFO_2837f061c311eb14 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_062e7a5fdd2d2571: +L_OBJC_IMAGE_INFO_2837f061c311eb14: .asciz "\000\000\000\000@\000\000" - .section __TEXT,__const +.zerofill __DATA,__bss,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 2).0,8,3 + .globl SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 2),8,3 + .globl SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 2) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 2),8,3 + .section __DATA,__const .p2align 3, 0x0 -l_anon.[ID].25: - .byte 5 - .space 39 +l_anon.[ID].31: + .quad l_anon.[ID].27 + .asciz "5\000\000\000\000\000\000\000O\000\000\000\001\000\000" +.zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),8,3 + .section __TEXT,__objc_methname,cstring_literals + .globl L_OBJC_METH_VAR_NAME_af8966656b8b2b6c +L_OBJC_METH_VAR_NAME_af8966656b8b2b6c: + .asciz "init" + + .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip + .globl L_OBJC_SELECTOR_REFERENCES_af8966656b8b2b6c .p2align 3, 0x0 -l_anon.[ID].26: - .byte 7 - .space 39 +L_OBJC_SELECTOR_REFERENCES_af8966656b8b2b6c: + .quad L_OBJC_METH_VAR_NAME_af8966656b8b2b6c + + .section __DATA,__objc_imageinfo,regular,no_dead_strip + .globl L_OBJC_IMAGE_INFO_af8966656b8b2b6c + .p2align 2, 0x0 +L_OBJC_IMAGE_INFO_af8966656b8b2b6c: + .asciz "\000\000\000\000@\000\000" +.zerofill __DATA,__bss,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 1).0,8,3 + .globl SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0),8,3 + .globl SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0),8,3 .section __DATA,__const .p2align 3, 0x0 -l_anon.[ID].27: - .quad l_anon.[ID].21 - .asciz "5\000\000\000\000\000\000\000I\000\000\000\001\000\000" +l_anon.[ID].32: + .quad l_anon.[ID].27 + .asciz "5\000\000\000\000\000\000\000x\000\000\000\001\000\000" -.zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),8,3 +.zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),8,3 + .section __TEXT,__objc_methname,cstring_literals + .globl L_OBJC_METH_VAR_NAME_6edddcebbded8f32 +L_OBJC_METH_VAR_NAME_6edddcebbded8f32: + .asciz "init" + + .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip + .globl L_OBJC_SELECTOR_REFERENCES_6edddcebbded8f32 .p2align 3, 0x0 -l_anon.[ID].28: - .quad l_anon.[ID].21 - .asciz "5\000\000\000\000\000\000\000u\000\000\000\001\000\000" +L_OBJC_SELECTOR_REFERENCES_6edddcebbded8f32: + .quad L_OBJC_METH_VAR_NAME_6edddcebbded8f32 + + .section __DATA,__objc_imageinfo,regular,no_dead_strip + .globl L_OBJC_IMAGE_INFO_6edddcebbded8f32 + .p2align 2, 0x0 +L_OBJC_IMAGE_INFO_6edddcebbded8f32: + .asciz "\000\000\000\000@\000\000" -.zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),8,3 .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_declare_class/lib.rs b/crates/test-assembly/crates/test_declare_class/lib.rs index 5db61d5b4..e65c67d18 100644 --- a/crates/test-assembly/crates/test_declare_class/lib.rs +++ b/crates/test-assembly/crates/test_declare_class/lib.rs @@ -7,10 +7,9 @@ use core::ptr; use icrate::Foundation::{NSCopying, NSObject, NSObjectProtocol, NSZone}; -use objc2::declare::{Ivar, IvarDrop, IvarEncode}; -use objc2::rc::Id; +use objc2::rc::{Allocated, Id}; use objc2::runtime::AnyClass; -use objc2::{declare_class, msg_send, msg_send_id, mutability, ClassType}; +use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass}; declare_class!( #[no_mangle] @@ -22,6 +21,8 @@ declare_class!( const NAME: &'static str = "NoIvars"; } + impl DeclaredClass for NoIvars {} + unsafe impl NoIvars { #[no_mangle] #[method(classMethod)] @@ -70,14 +71,14 @@ declare_class!( } ); +pub struct ForgetableIvarsIvars { + foo: u8, + bar: u32, +} + declare_class!( #[no_mangle] - pub struct ForgetableIvars { - foo: IvarEncode, - bar: IvarEncode, - } - - mod forgetable_ivars; + pub struct ForgetableIvars; unsafe impl ClassType for ForgetableIvars { type Super = NSObject; @@ -85,19 +86,16 @@ declare_class!( const NAME: &'static str = "ForgetableIvars"; } + impl DeclaredClass for ForgetableIvars { + type Ivars = ForgetableIvarsIvars; + } + unsafe impl ForgetableIvars { #[no_mangle] - #[method(init)] - unsafe fn init_forgetable_ivars(this: *mut Self) -> *mut Self { - let this: Option<&mut Self> = unsafe { msg_send![super(this), init] }; - - this.map(|this| { - Ivar::write(&mut this.foo, 42); - Ivar::write(&mut this.bar, 43); - let this: *mut Self = this; - this - }) - .unwrap_or_else(ptr::null_mut) + #[method_id(init)] + fn init_forgetable_ivars(this: Allocated) -> Option> { + let this = this.set_ivars(ForgetableIvarsIvars { foo: 42, bar: 43 }); + unsafe { msg_send_id![super(this), init] } } } ); @@ -110,18 +108,18 @@ impl ForgetableIvars { #[no_mangle] pub fn access_forgetable_ivars(&self) -> (u8, u32) { - (*self.foo, *self.bar) + (self.ivars().foo, self.ivars().bar) } } +pub struct DropIvarsIvars { + obj: Id, + obj_option: Option>, +} + declare_class!( #[no_mangle] - pub struct DropIvars { - obj: IvarDrop, "_obj">, - obj_option: IvarDrop>, "_obj_option">, - } - - mod drop_ivars; + pub struct DropIvars; unsafe impl ClassType for DropIvars { type Super = NSObject; @@ -129,19 +127,19 @@ declare_class!( const NAME: &'static str = "DropIvars"; } + impl DeclaredClass for DropIvars { + type Ivars = DropIvarsIvars; + } + unsafe impl DropIvars { #[no_mangle] - #[method(init)] - unsafe fn init_drop_ivars(this: *mut Self) -> *mut Self { - let this: Option<&mut Self> = unsafe { msg_send![super(this), init] }; - - this.map(|this| { - Ivar::write(&mut this.obj, NSObject::new()); - Ivar::write(&mut this.obj_option, Some(NSObject::new())); - let this: *mut Self = this; - this - }) - .unwrap_or_else(ptr::null_mut) + #[method_id(init)] + fn init_drop_ivars(this: Allocated) -> Option> { + let this = this.set_ivars(DropIvarsIvars { + obj: NSObject::new(), + obj_option: Some(NSObject::new()), + }); + unsafe { msg_send_id![super(this), init] } } } ); @@ -160,7 +158,14 @@ impl DropIvars { } #[no_mangle] - pub fn access_drop_ivars(&self) -> *const NSObject { - Id::as_ptr(&*self.obj) + pub fn access_drop_ivars(&self) -> (*const NSObject, *const NSObject) { + ( + Id::as_ptr(&self.ivars().obj), + self.ivars() + .obj_option + .as_ref() + .map(Id::as_ptr) + .unwrap_or_else(ptr::null), + ) } } diff --git a/crates/test-ui/ui/declare_class_classtype_imported.rs b/crates/test-ui/ui/declare_class_classtype_imported.rs index 7ab550cd2..6fc85f270 100644 --- a/crates/test-ui/ui/declare_class_classtype_imported.rs +++ b/crates/test-ui/ui/declare_class_classtype_imported.rs @@ -10,6 +10,8 @@ declare_class!( type Mutability = mutability::InteriorMutable; const NAME: &'static str = "CustomObject"; } + + impl objc2::DeclaredClass for CustomObject {} ); fn main() {} diff --git a/crates/test-ui/ui/declare_class_classtype_imported.stderr b/crates/test-ui/ui/declare_class_classtype_imported.stderr index 38a59afb6..a60443068 100644 --- a/crates/test-ui/ui/declare_class_classtype_imported.stderr +++ b/crates/test-ui/ui/declare_class_classtype_imported.stderr @@ -7,5 +7,5 @@ error: no rules expected the token `objc2` note: while trying to match `ClassType` --> $WORKSPACE/crates/objc2/src/macros/declare_class.rs | - | unsafe impl ClassType for $for:ty { + | unsafe impl ClassType for $for_class:ty { | ^^^^^^^^^ diff --git a/crates/test-ui/ui/declare_class_delegate_not_mainthreadonly.rs b/crates/test-ui/ui/declare_class_delegate_not_mainthreadonly.rs index af10f877b..cb43b051e 100644 --- a/crates/test-ui/ui/declare_class_delegate_not_mainthreadonly.rs +++ b/crates/test-ui/ui/declare_class_delegate_not_mainthreadonly.rs @@ -4,7 +4,7 @@ use icrate::AppKit::{NSApplication, NSApplicationDelegate}; use icrate::Foundation::{MainThreadMarker, NSNotification, NSObject, NSObjectProtocol}; use objc2::rc::Id; use objc2::runtime::ProtocolObject; -use objc2::{declare_class, extern_methods, mutability, ClassType}; +use objc2::{declare_class, extern_methods, mutability, ClassType, DeclaredClass}; declare_class!( struct CustomObject; @@ -15,6 +15,8 @@ declare_class!( const NAME: &'static str = "CustomObject"; } + impl DeclaredClass for CustomObject {} + unsafe impl NSObjectProtocol for CustomObject {} unsafe impl NSApplicationDelegate for CustomObject { diff --git a/crates/test-ui/ui/declare_class_invalid_receiver.rs b/crates/test-ui/ui/declare_class_invalid_receiver.rs index 63ff3f45c..5cfb493f9 100644 --- a/crates/test-ui/ui/declare_class_invalid_receiver.rs +++ b/crates/test-ui/ui/declare_class_invalid_receiver.rs @@ -1,6 +1,6 @@ use objc2::rc::{Allocated, Id}; use objc2::runtime::{AnyClass, NSObject}; -use objc2::{declare_class, mutability, ClassType}; +use objc2::{declare_class, mutability, ClassType, DeclaredClass}; declare_class!( struct CustomObject; @@ -11,6 +11,8 @@ declare_class!( const NAME: &'static str = "CustomObject"; } + impl DeclaredClass for CustomObject {} + unsafe impl CustomObject { #[method(testBox)] fn test_box(self: Box) { diff --git a/crates/test-ui/ui/declare_class_invalid_syntax.rs b/crates/test-ui/ui/declare_class_invalid_syntax.rs index d7e38b188..e0757b960 100644 --- a/crates/test-ui/ui/declare_class_invalid_syntax.rs +++ b/crates/test-ui/ui/declare_class_invalid_syntax.rs @@ -1,9 +1,6 @@ -use std::marker::PhantomData; - -use objc2::declare::IvarEncode; use objc2::rc::Id; use objc2::runtime::NSObject; -use objc2::{declare_class, mutability, ClassType}; +use objc2::{declare_class, mutability, ClassType, DeclaredClass}; declare_class!( struct InvalidMethodDeclarations; @@ -14,6 +11,8 @@ declare_class!( const NAME: &'static str = "InvalidMethodDeclarations"; } + impl DeclaredClass for InvalidMethodDeclarations {} + unsafe impl InvalidMethodDeclarations { fn test_no_attribute() { unimplemented!() @@ -140,63 +139,27 @@ declare_class!( type Super = NSObject; type Mutability = mutability::InteriorMutable; } -); - -declare_class!( - struct InvalidField { - field: i32, - } - unsafe impl ClassType for InvalidField { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - const NAME: &'static str = "InvalidField"; - } + impl DeclaredClass for MissingName {} ); declare_class!( - struct UnnecessaryIvarModule; - - mod ivars; + struct MissingMutability; - unsafe impl ClassType for UnnecessaryIvarModule { + unsafe impl ClassType for MissingMutability { type Super = NSObject; - type Mutability = mutability::InteriorMutable; - const NAME: &'static str = "UnnecessaryIvarModule"; - } -); - -declare_class!( - struct UnnecessaryIvarModuleWithFields { - p: PhantomData, } - mod ivars; - - unsafe impl ClassType for UnnecessaryIvarModuleWithFields { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - const NAME: &'static str = "UnnecessaryIvarModuleWithFields"; - } + impl DeclaredClass for MissingMutability {} ); declare_class!( - struct MissingIvarModule { - field: IvarEncode, - } + struct MissingDeclaredClass; - unsafe impl ClassType for MissingIvarModule { + unsafe impl ClassType for MissingDeclaredClass { type Super = NSObject; type Mutability = mutability::InteriorMutable; - const NAME: &'static str = "MissingIvarModule"; - } -); - -declare_class!( - struct MissingMutability; - - unsafe impl ClassType for MissingMutability { - type Super = NSObject; + const NAME: &'static str = "MissingDeclaredClass"; } ); diff --git a/crates/test-ui/ui/declare_class_invalid_syntax.stderr b/crates/test-ui/ui/declare_class_invalid_syntax.stderr index faa2b6cee..add5de7c8 100644 --- a/crates/test-ui/ui/declare_class_invalid_syntax.stderr +++ b/crates/test-ui/ui/declare_class_invalid_syntax.stderr @@ -457,71 +457,29 @@ note: while trying to match `const` | const NAME: &'static str = $name_const:expr; | ^^^^^ -error: invalid type i32 in field field. Type must be either `PhantomData`, `IvarDrop`, `IvarBool` or `IvarEncode`. - --> ui/declare_class_invalid_syntax.rs - | - | / declare_class!( - | | struct InvalidField { - | | field: i32, - | | } -... | - | | } - | | ); - | |_^ - | - = note: this error originates in the macro `$crate::__parse_fields` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: no rules expected the token `mod` +error: no rules expected the token `}` --> ui/declare_class_invalid_syntax.rs | - | mod ivars; - | ^^^ no rules expected this token in macro call + | } + | ^ no rules expected this token in macro call | -note: while trying to match `unsafe` +note: while trying to match `type` --> $WORKSPACE/crates/objc2/src/macros/declare_class.rs | - | unsafe impl ClassType for $for:ty { - | ^^^^^^ - -error: no need to specify an ivar module when the type has no ivars - --> ui/declare_class_invalid_syntax.rs - | - | / declare_class!( - | | struct UnnecessaryIvarModuleWithFields { - | | p: PhantomData, - | | } -... | - | | } - | | ); - | |_^ - | - = note: this error originates in the macro `$crate::__parse_fields` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: must specify an ivar module when the type has ivars - --> ui/declare_class_invalid_syntax.rs - | - | / declare_class!( - | | struct MissingIvarModule { - | | field: IvarEncode, - | | } -... | - | | } - | | ); - | |_^ - | - = note: this error originates in the macro `$crate::__parse_fields` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) + | type Mutability = $mutability:ty; + | ^^^^ -error: no rules expected the token `}` +error: unexpected end of macro invocation --> ui/declare_class_invalid_syntax.rs | | } - | ^ no rules expected this token in macro call + | ^ missing tokens in macro arguments | -note: while trying to match `type` +note: while trying to match `impl` --> $WORKSPACE/crates/objc2/src/macros/declare_class.rs | - | type Mutability = $mutability:ty; - | ^^^^ + | impl DeclaredClass for $for_declared:ty { + | ^^^^ error[E0599]: no function or associated item named `test_pattern` found for struct `InvalidMethodDeclarations` in the current scope --> ui/declare_class_invalid_syntax.rs diff --git a/crates/test-ui/ui/declare_class_invalid_type.rs b/crates/test-ui/ui/declare_class_invalid_type.rs index a34b00e1b..ce8c704bf 100644 --- a/crates/test-ui/ui/declare_class_invalid_type.rs +++ b/crates/test-ui/ui/declare_class_invalid_type.rs @@ -1,6 +1,6 @@ use objc2::rc::Id; use objc2::runtime::NSObject; -use objc2::{declare_class, mutability, ClassType}; +use objc2::{declare_class, mutability, ClassType, DeclaredClass}; declare_class!( struct CustomObject; @@ -11,6 +11,8 @@ declare_class!( const NAME: &'static str = "CustomObject"; } + impl DeclaredClass for CustomObject {} + unsafe impl CustomObject { #[method(test1)] fn test1() -> Id { diff --git a/crates/test-ui/ui/declare_class_invalid_type2.rs b/crates/test-ui/ui/declare_class_invalid_type2.rs index 18dcf5da3..fb1129632 100644 --- a/crates/test-ui/ui/declare_class_invalid_type2.rs +++ b/crates/test-ui/ui/declare_class_invalid_type2.rs @@ -1,6 +1,6 @@ use objc2::rc::{Allocated, Id}; use objc2::runtime::NSObject; -use objc2::{declare_class, mutability, ClassType}; +use objc2::{declare_class, mutability, ClassType, DeclaredClass}; declare_class!( struct CustomObject; @@ -11,6 +11,8 @@ declare_class!( const NAME: &'static str = "CustomObject"; } + impl DeclaredClass for CustomObject {} + unsafe impl CustomObject { #[method_id(initNotSameGenerics)] fn test_init_not_same_generics(this: Allocated) -> Id { diff --git a/crates/test-ui/ui/declare_class_invalid_type3.rs b/crates/test-ui/ui/declare_class_invalid_type3.rs deleted file mode 100644 index 99480de84..000000000 --- a/crates/test-ui/ui/declare_class_invalid_type3.rs +++ /dev/null @@ -1,19 +0,0 @@ -use objc2::declare::IvarEncode; -use objc2::runtime::NSObject; -use objc2::{declare_class, mutability, ClassType}; - -declare_class!( - struct CustomObject { - field: IvarEncode<(), "_field">, - } - - mod ivars; - - unsafe impl ClassType for CustomObject { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - const NAME: &'static str = "CustomObject"; - } -); - -fn main() {} diff --git a/crates/test-ui/ui/declare_class_invalid_type3.stderr b/crates/test-ui/ui/declare_class_invalid_type3.stderr deleted file mode 100644 index 9e0f48a51..000000000 --- a/crates/test-ui/ui/declare_class_invalid_type3.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error[E0277]: the trait bound `(): Encode` is not satisfied - --> ui/declare_class_invalid_type3.rs - | - | / declare_class!( - | | struct CustomObject { - | | field: IvarEncode<(), "_field">, - | | } -... | - | | } - | | ); - | |_^ the trait `Encode` is not implemented for `()` - | - = help: the following other types implement trait `Encode`: - isize - i8 - i16 - i32 - i64 - usize - u8 - u16 - and $N others - = note: required for `objc2::declare::IvarEncode<()>` to implement `InnerIvarType` -note: required by a bound in `objc2::declare::IvarType::Type` - --> $WORKSPACE/crates/objc2/src/declare/ivar.rs - | - | type Type: InnerIvarType; - | ^^^^^^^^^^^^^ required by this bound in `IvarType::Type` - = note: this error originates in the macro `$crate::__parse_fields` which comes from the expansion of the macro `declare_class` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/test-ui/ui/declare_class_mut_self_not_mutable.rs b/crates/test-ui/ui/declare_class_mut_self_not_mutable.rs index 96d865f7d..9505f8775 100644 --- a/crates/test-ui/ui/declare_class_mut_self_not_mutable.rs +++ b/crates/test-ui/ui/declare_class_mut_self_not_mutable.rs @@ -1,6 +1,6 @@ use objc2::rc::Id; use objc2::runtime::NSObject; -use objc2::{declare_class, mutability, ClassType}; +use objc2::{declare_class, mutability, ClassType, DeclaredClass}; declare_class!( struct CustomObject; @@ -11,6 +11,8 @@ declare_class!( const NAME: &'static str = "CustomObject"; } + impl DeclaredClass for CustomObject {} + unsafe impl CustomObject { #[method(initTest)] fn init_test(&mut self) -> &mut Self { diff --git a/crates/test-ui/ui/implement_protocol_missing_super.rs b/crates/test-ui/ui/implement_protocol_missing_super.rs index 7e2fd36ad..dbb131610 100644 --- a/crates/test-ui/ui/implement_protocol_missing_super.rs +++ b/crates/test-ui/ui/implement_protocol_missing_super.rs @@ -2,7 +2,7 @@ //! protocols like `NSObjectProtocol` to also be implemented. use icrate::AppKit::NSApplicationDelegate; use icrate::Foundation::NSObject; -use objc2::{declare_class, mutability, ClassType}; +use objc2::{declare_class, mutability, ClassType, DeclaredClass}; declare_class!( struct CustomObject; @@ -13,6 +13,8 @@ declare_class!( const NAME: &'static str = "CustomObject"; } + impl DeclaredClass for CustomObject {} + unsafe impl NSApplicationDelegate for CustomObject {} ); diff --git a/crates/test-ui/ui/main_thread_only_not_allocable.rs b/crates/test-ui/ui/main_thread_only_not_allocable.rs index 2b67f131a..b5b2a6f58 100644 --- a/crates/test-ui/ui/main_thread_only_not_allocable.rs +++ b/crates/test-ui/ui/main_thread_only_not_allocable.rs @@ -1,5 +1,5 @@ use objc2::runtime::NSObject; -use objc2::{declare_class, mutability, ClassType}; +use objc2::{declare_class, mutability, ClassType, DeclaredClass}; declare_class!( struct MyMainThreadOnlyClass; @@ -9,6 +9,8 @@ declare_class!( type Mutability = mutability::MainThreadOnly; const NAME: &'static str = "MyMainThreadOnlyClass"; } + + impl DeclaredClass for MyMainThreadOnlyClass {} ); fn main() { diff --git a/crates/test-ui/ui/msg_send_missing_comma.rs b/crates/test-ui/ui/msg_send_missing_comma.rs index e23c4fe87..7e7d2a0c9 100644 --- a/crates/test-ui/ui/msg_send_missing_comma.rs +++ b/crates/test-ui/ui/msg_send_missing_comma.rs @@ -12,5 +12,7 @@ fn main() { unsafe { msg_send_bool![obj, c:obj d:obj] }; + let _: Id = unsafe { msg_send_id![super(obj), e:obj f:obj] }; + let _: Id = unsafe { msg_send_id![super(obj, NSString::class()), e:obj f:obj] }; let _: Id = unsafe { msg_send_id![obj, e:obj f:obj] }; } diff --git a/crates/test-ui/ui/msg_send_missing_comma.stderr b/crates/test-ui/ui/msg_send_missing_comma.stderr index 0b1c55abb..c49284a9f 100644 --- a/crates/test-ui/ui/msg_send_missing_comma.stderr +++ b/crates/test-ui/ui/msg_send_missing_comma.stderr @@ -49,6 +49,24 @@ error: use of deprecated function `main::__msg_send_missing_comma`: using msg_se | = note: this error originates in the macro `$crate::__comma_between_args_inner` which comes from the expansion of the macro `msg_send_bool` (in Nightly builds, run with -Z macro-backtrace for more info) +error: use of deprecated function `main::__msg_send_missing_comma`: using msg_send_id! without a comma between arguments is technically not valid macro syntax, and may break in a future version of Rust. You should use the following instead: + msg_send_id![super(obj), e: obj, f: obj] + --> ui/msg_send_missing_comma.rs + | + | let _: Id = unsafe { msg_send_id![super(obj), e:obj f:obj] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `$crate::__comma_between_args_inner` which comes from the expansion of the macro `msg_send_id` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: use of deprecated function `main::__msg_send_missing_comma`: using msg_send_id! without a comma between arguments is technically not valid macro syntax, and may break in a future version of Rust. You should use the following instead: + msg_send_id![super(obj, NSString::class()), e: obj, f: obj] + --> ui/msg_send_missing_comma.rs + | + | let _: Id = unsafe { msg_send_id![super(obj, NSString::class()), e:obj f:obj] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `$crate::__comma_between_args_inner` which comes from the expansion of the macro `msg_send_id` (in Nightly builds, run with -Z macro-backtrace for more info) + error: use of deprecated function `main::__msg_send_missing_comma`: using msg_send_id! without a comma between arguments is technically not valid macro syntax, and may break in a future version of Rust. You should use the following instead: msg_send_id![obj, e: obj, f: obj] --> ui/msg_send_missing_comma.rs diff --git a/crates/test-ui/ui/mutability_traits_unimplementable.rs b/crates/test-ui/ui/mutability_traits_unimplementable.rs index 96fe63505..8eedc6be9 100644 --- a/crates/test-ui/ui/mutability_traits_unimplementable.rs +++ b/crates/test-ui/ui/mutability_traits_unimplementable.rs @@ -1,6 +1,6 @@ //! Check that `mutability` traits are not implementable manually. use objc2::runtime::NSObject; -use objc2::{declare_class, mutability, ClassType}; +use objc2::{declare_class, mutability, ClassType, DeclaredClass}; declare_class!( struct CustomObject; @@ -10,6 +10,8 @@ declare_class!( type Mutability = mutability::InteriorMutable; const NAME: &'static str = "CustomObject"; } + + impl DeclaredClass for CustomObject {} ); unsafe impl mutability::IsMutable for CustomObject {} diff --git a/crates/test-ui/ui/wrong_optional.rs b/crates/test-ui/ui/wrong_optional.rs index 78203d187..721a430aa 100644 --- a/crates/test-ui/ui/wrong_optional.rs +++ b/crates/test-ui/ui/wrong_optional.rs @@ -1,6 +1,6 @@ use objc2::rc::Id; use objc2::runtime::NSObject; -use objc2::{declare_class, extern_class, extern_methods, mutability, ClassType}; +use objc2::{declare_class, extern_class, extern_methods, mutability, ClassType, DeclaredClass}; extern_class!( pub struct MyObject; @@ -38,6 +38,8 @@ declare_class!( const NAME: &'static str = "CustomObject1"; } + impl DeclaredClass for CustomObject1 {} + unsafe impl CustomObject1 { #[method(c)] #[optional] @@ -55,6 +57,8 @@ declare_class!( const NAME: &'static str = "CustomObject2"; } + impl DeclaredClass for CustomObject2 {} + unsafe impl CustomObject2 { #[optional] /// Doc comment diff --git a/crates/tests/src/test_declare_class_protocol.rs b/crates/tests/src/test_declare_class_protocol.rs index e408e296a..1a1c9cd67 100644 --- a/crates/tests/src/test_declare_class_protocol.rs +++ b/crates/tests/src/test_declare_class_protocol.rs @@ -3,7 +3,7 @@ use icrate::Foundation::NSCopying; use objc2::mutability::Immutable; use objc2::rc::Id; use objc2::runtime::{NSObject, NSZone}; -use objc2::{declare_class, ClassType, ProtocolType}; +use objc2::{declare_class, ClassType, DeclaredClass, ProtocolType}; #[test] #[should_panic = "could not create new class TestDeclareClassDuplicate. Perhaps a class with that name already exists?"] @@ -16,6 +16,8 @@ fn test_declare_class_duplicate() { type Mutability = Immutable; const NAME: &'static str = "TestDeclareClassDuplicate"; } + + impl DeclaredClass for Custom1 {} ); declare_class!( @@ -26,6 +28,8 @@ fn test_declare_class_duplicate() { type Mutability = Immutable; const NAME: &'static str = "TestDeclareClassDuplicate"; } + + impl DeclaredClass for Custom2 {} ); let _cls = Custom1::class(); @@ -44,6 +48,8 @@ fn test_declare_class_protocol() { const NAME: &'static str = "TestDeclareClassProtocolNotFound"; } + impl DeclaredClass for Custom {} + unsafe impl NSCopying for Custom { #[method_id(copyWithZone:)] fn copy_with_zone(&self, _zone: *const NSZone) -> Id { @@ -71,6 +77,8 @@ fn test_declare_class_invalid_method() { const NAME: &'static str = "TestDeclareClassInvalidMethod"; } + impl DeclaredClass for Custom {} + unsafe impl Custom { // Override `description` with a bad return type #[method(description)] @@ -96,6 +104,8 @@ fn test_declare_class_missing_protocol_method() { const NAME: &'static str = "TestDeclareClassMissingProtocolMethod"; } + impl DeclaredClass for Custom {} + unsafe impl NSCopying for Custom { // Missing required method } @@ -116,6 +126,8 @@ fn test_declare_class_invalid_protocol_method() { const NAME: &'static str = "TestDeclareClassInvalidProtocolMethod"; } + impl DeclaredClass for Custom {} + unsafe impl NSCopying for Custom { // Override with a bad return type #[method(copyWithZone:)] @@ -143,6 +155,8 @@ fn test_declare_class_extra_protocol_method() { const NAME: &'static str = "TestDeclareClassExtraProtocolMethod"; } + impl DeclaredClass for Custom {} + unsafe impl NSCopying for Custom { #[method_id(copyWithZone:)] fn copy_with_zone(&self, _zone: *const NSZone) -> Id {