diff --git a/Cargo.lock b/Cargo.lock index f1968698e..c935d3617 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "basic-toml" version = "0.1.7" @@ -279,6 +285,15 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -302,6 +317,7 @@ version = "0.4.1" dependencies = [ "iai", "malloc_buf", + "memoffset", "objc-sys", "objc2-encode", "objc2-proc-macros", 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 e9ccd18fd..0826dd68b 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 @@ -85,11 +181,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). implement `IsAllowedMutable`. * Disallow the ability to use non-`Self`-like types as the receiver in `declare_class!`. +* Allow adding instance variables with the same name on Apple platforms. +* **BREAKING**: Make loading instance variables robust and sound in the face + of instance variables with the same name. + + To read or write the instance variable for an object, you should now use the + `load`, `load_ptr` and `load_mut` methods on `Ivar`, instead of the `ivar`, + `ivar_ptr` and `ivar_mut` methods on `AnyObject`. + + This _is_ more verbose, but it also ensures that the class for the instance + variable you're loading is the same as the one the instance variable you + want to access is defined on. + + ```rust + // Before + let number = unsafe { *obj.ivar::("number") }; + + // After + let ivar = cls.instance_variable("number").unwrap(); + let number = unsafe { *ivar.load::(&obj) }; + ``` ### Removed * **BREAKING**: Removed `ProtocolType` implementation for `NSObject`. Use the more precise `NSObjectProtocol` trait instead! * **BREAKING**: Removed the `MessageArguments` trait. +* **BREAKING**: Removed the following items from the `declare` module: `Ivar`, + `IvarEncode`, `IvarBool`, `IvarDrop`, `IvarType` and `InnerIvarType`. + + Ivar functionality is available in a different form now, see above under + "Changed". +* **BREAKING**: Removed `ClassBuilder::add_static_ivar`. ## 0.4.1 - 2023-07-31 diff --git a/crates/objc2/Cargo.toml b/crates/objc2/Cargo.toml index fd3e781d4..05df438de 100644 --- a/crates/objc2/Cargo.toml +++ b/crates/objc2/Cargo.toml @@ -108,6 +108,7 @@ objc2-proc-macros = { path = "../objc2-proc-macros", version = "0.1.1", optional [dev-dependencies] iai = { version = "0.1", git = "https://github.com/madsmtm/iai", branch = "callgrind" } static_assertions = "1.1.0" +memoffset = "0.9.0" [[bench]] name = "autorelease" diff --git a/crates/objc2/examples/class_with_lifetime.rs b/crates/objc2/examples/class_with_lifetime.rs index 54410530d..50d9ca844 100644 --- a/crates/objc2/examples/class_with_lifetime.rs +++ b/crates/objc2/examples/class_with_lifetime.rs @@ -4,36 +4,19 @@ use std::marker::PhantomData; use std::sync::Once; -use objc2::declare::{ClassBuilder, Ivar, IvarEncode, IvarType}; +use objc2::declare::ClassBuilder; use objc2::mutability::Mutable; use objc2::rc::Id; use objc2::runtime::{AnyClass, NSObject, Sel}; use objc2::{msg_send, msg_send_id, sel}; use objc2::{ClassType, Encoding, Message, RefEncode}; -/// Helper type for the instance variable -struct NumberIvar<'a> { - // Doesn't actually matter what we put here, but we have to use the - // lifetime parameter somehow - p: PhantomData<&'a mut u8>, -} - -unsafe impl<'a> IvarType for NumberIvar<'a> { - type Type = IvarEncode<&'a mut u8>; - const NAME: &'static str = "_number_ptr"; -} - /// Struct that represents our custom object. #[repr(C)] pub struct MyObject<'a> { // Required to give MyObject the proper layout superclass: NSObject, - // SAFETY: The ivar is declared below, and is properly initialized in the - // designated initializer. - // - // Note! Attempting to acess the ivar before it has been initialized is - // undefined behaviour! - number: Ivar>, + p: PhantomData<&'a mut u8>, } unsafe impl RefEncode for MyObject<'_> { @@ -50,8 +33,12 @@ impl<'a> MyObject<'a> { ) -> Option<&'s mut Self> { let this: Option<&mut Self> = unsafe { msg_send![super(self), init] }; this.map(|this| { - // Properly initialize the number reference - Ivar::write(&mut this.number, ptr.expect("got NULL number ptr")); + let ivar = Self::class().instance_variable("number").unwrap(); + // SAFETY: The ivar is added with the same type below + unsafe { + ivar.load_ptr::<&mut u8>(&this.superclass) + .write(ptr.expect("got NULL number ptr")) + }; this }) } @@ -62,12 +49,16 @@ impl<'a> MyObject<'a> { unsafe { msg_send_id![Self::alloc(), initWithPtr: number] } } - pub fn get(&self) -> &u8 { - &self.number + pub fn get(&self) -> u8 { + let ivar = Self::class().instance_variable("number").unwrap(); + // SAFETY: The ivar is added with the same type below, and is initialized in `init_with_ptr` + unsafe { **ivar.load::<&mut u8>(&self.superclass) } } pub fn set(&mut self, number: u8) { - **self.number = number; + let ivar = Self::class().instance_variable("number").unwrap(); + // SAFETY: The ivar is added with the same type below, and is initialized in `init_with_ptr` + unsafe { **ivar.load_mut::<&mut u8>(&mut self.superclass) = number }; } } @@ -84,7 +75,7 @@ unsafe impl<'a> ClassType for MyObject<'a> { let superclass = NSObject::class(); let mut builder = ClassBuilder::new(Self::NAME, superclass).unwrap(); - builder.add_static_ivar::>(); + builder.add_ivar::<&mut u8>("number"); unsafe { builder.add_method( @@ -112,7 +103,7 @@ fn main() { let mut number = 54; let mut obj = MyObject::new(&mut number); - assert_eq!(*obj.get(), 54); + assert_eq!(obj.get(), 54); // It is not possible to convert to `Id`, since that would loose // the lifetime information that `MyObject` stores. @@ -131,7 +122,7 @@ fn main() { // But we can now mutate the referenced `number` obj.set(7); - assert_eq!(*obj.get(), 7); + assert_eq!(obj.get(), 7); drop(obj); // And now that we've dropped `obj`, we can access `number` again diff --git a/crates/objc2/examples/introspection.rs b/crates/objc2/examples/introspection.rs index 66f62c3f9..a7c8109b0 100644 --- a/crates/objc2/examples/introspection.rs +++ b/crates/objc2/examples/introspection.rs @@ -49,9 +49,7 @@ fn main() { println!("NSObject address: {obj:p}"); - // Access an ivar of the object - // - // As before, you should not rely on the `isa` ivar being available! - let isa = unsafe { *obj.ivar::<*const AnyClass>("isa") }; + // Read an ivar on the object + let isa: *const AnyClass = unsafe { *ivar.load(&obj) }; println!("NSObject isa: {isa:?}"); } diff --git a/crates/objc2/src/__macro_helpers/common_selectors.rs b/crates/objc2/src/__macro_helpers/common_selectors.rs index 508a2f3c1..05ab9308e 100644 --- a/crates/objc2/src/__macro_helpers/common_selectors.rs +++ b/crates/objc2/src/__macro_helpers/common_selectors.rs @@ -28,3 +28,90 @@ 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_dynamic() { + 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); + let has_run_destruct = HAS_RUN.load(Ordering::Relaxed); + + // This does work on GNUStep, but unfortunately not in Apple's objc4 + if cfg!(feature = "gnustep-1-7") { + assert!(has_run_destruct); + } else { + assert!(!has_run_destruct); + } + } +} diff --git a/crates/objc2/src/__macro_helpers/declare_class.rs b/crates/objc2/src/__macro_helpers/declare_class.rs index 38eaba628..b1dbd67ed 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..d3ae34a35 --- /dev/null +++ b/crates/objc2/src/__macro_helpers/declared_ivars.rs @@ -0,0 +1,931 @@ +//! # 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; remember, `dealloc` may be called even on +//! newly, non-initialized instances. +//! +//! Note that Swift [doesn't handle this][swift-deinit-unsound], but that +//! doesn't mean we can simply stick our heads in the sand. +//! +//! 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: +//! ```ignore +//! 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::ZeroableInOption`. +//! +//! [swift-deinit-unsound]: https://github.com/apple/swift/issues/68734 +//! [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, 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 + #[allow(dead_code)] + 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 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 the offset will be zero, so we + // can still compute a valid 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, "type did not have 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, and method contract is + // upheld inside `dealloc`. + unsafe { builder.add_method(sel!(dealloc), func) }; + } else { + // Users should not rely on this ommision, it is only an optimization. + } +} + +/// 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. + // + // It is very important that we do this after the `Drop` of the class + // itself above, though. + // + // 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 { + // Monomorphized error handling + // Intentionally not #[track_caller], we expect this error to never occur + fn get_ivar_failed() -> ! { + unreachable!("failed retrieving instance variable on newly declared class") + } + + cls.instance_variable(&ivar_name) + .unwrap_or_else(|| get_ivar_failed()) + .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 { + // Monomorphized error handling + // Intentionally not #[track_caller], we expect this error to never occur + fn get_drop_flag_failed() -> ! { + unreachable!("failed retrieving drop flag instance variable on newly declared class") + } + + cls.instance_variable(&drop_flag_name) + .unwrap_or_else(|| get_drop_flag_failed()) + .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] +#[track_caller] +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] +#[track_caller] +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] +#[track_caller] +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 std::println; + use std::sync::Mutex; + + use alloc::vec::Vec; + + use super::*; + use crate::mutability::{InteriorMutable, Mutable}; + use crate::rc::{Allocated, Id, PartialInit, __RcTestObject, __ThreadTestData}; + use crate::runtime::NSObject; + use crate::{declare_class, msg_send, msg_send_id, ClassType, DeclaredClass}; + + /// Initialize superclasses, but not own class. + unsafe fn init_only_superclasses(obj: Allocated) -> Id + where + T::Super: ClassType, + { + unsafe { Id::new(msg_send![super(Allocated::into_ptr(obj)), init]) }.unwrap() + } + + /// Initialize, but fail to finalize (which is only done by `msg_send_id!`). + unsafe fn init_no_finalize(obj: Allocated) -> Id + where + T::Super: ClassType, + T::Ivars: Default, + { + let obj = obj.set_ivars(Default::default()); + unsafe { Id::new(msg_send![super(PartialInit::into_ptr(obj)), init]) }.unwrap() + } + + /// Initialize properly. + unsafe fn init(obj: Allocated) -> Id { + unsafe { msg_send_id![obj, init] } + } + + #[test] + fn assert_size() { + assert_eq!(mem::size_of::(), 1); + } + + #[test] + fn test_dealloc_and_dealloc_subclasses() { + #[derive(Debug, PartialEq)] + enum Operation { + DropIvar, + DropClass, + } + + static OPERATIONS: Mutex> = Mutex::new(Vec::new()); + + #[derive(Default)] + struct IvarThatImplsDrop; + + impl Drop for IvarThatImplsDrop { + fn drop(&mut self) { + OPERATIONS.lock().unwrap().push(Operation::DropIvar); + } + } + + #[track_caller] + fn check(expected: [Operation; N]) { + let mut operations = OPERATIONS.lock().unwrap(); + assert_eq!(&**operations, expected); + operations.clear(); + } + + // First class + + declare_class!( + struct ImplsDrop; + + unsafe impl ClassType for ImplsDrop { + type Super = NSObject; + type Mutability = InteriorMutable; + const NAME: &'static str = "ImplsDrop"; + } + + impl DeclaredClass for ImplsDrop { + type Ivars = (); + } + + unsafe impl ImplsDrop { + #[method_id(init)] + fn init(this: Allocated) -> Option> { + unsafe { msg_send_id![super(this.set_ivars(())), init] } + } + } + ); + + impl Drop for ImplsDrop { + fn drop(&mut self) { + OPERATIONS.lock().unwrap().push(Operation::DropClass); + } + } + + let _ = ImplsDrop::alloc(); + check([]); + + let _ = unsafe { init_only_superclasses(ImplsDrop::alloc()) }; + check([]); + + let _ = unsafe { init_no_finalize(ImplsDrop::alloc()) }; + check([]); + + let _ = unsafe { init(ImplsDrop::alloc()) }; + check([Operation::DropClass]); + + // Subclass + + declare_class!( + struct IvarsImplDrop; + + unsafe impl ClassType for IvarsImplDrop { + type Super = ImplsDrop; + type Mutability = InteriorMutable; + const NAME: &'static str = "IvarsImplDrop"; + } + + impl DeclaredClass for IvarsImplDrop { + type Ivars = IvarThatImplsDrop; + } + + unsafe impl IvarsImplDrop { + #[method_id(init)] + fn init(this: Allocated) -> Option> { + unsafe { msg_send_id![super(this.set_ivars(IvarThatImplsDrop)), init] } + } + } + ); + + let _ = IvarsImplDrop::alloc(); + check([]); + + let _ = unsafe { init_only_superclasses(IvarsImplDrop::alloc()) }; + check([Operation::DropClass]); + + let _ = unsafe { init_no_finalize(IvarsImplDrop::alloc()) }; + check([Operation::DropIvar, Operation::DropClass]); + + let _ = unsafe { init(IvarsImplDrop::alloc()) }; + check([Operation::DropIvar, Operation::DropClass]); + + // Further subclass + + declare_class!( + struct BothIvarsAndTypeImplsDrop; + + unsafe impl ClassType for BothIvarsAndTypeImplsDrop { + type Super = IvarsImplDrop; + type Mutability = InteriorMutable; + const NAME: &'static str = "BothIvarsAndTypeImplsDrop"; + } + + impl DeclaredClass for BothIvarsAndTypeImplsDrop { + type Ivars = IvarThatImplsDrop; + } + + unsafe impl BothIvarsAndTypeImplsDrop { + #[method_id(init)] + fn init(this: Allocated) -> Option> { + unsafe { msg_send_id![super(this.set_ivars(IvarThatImplsDrop)), init] } + } + } + ); + + impl Drop for BothIvarsAndTypeImplsDrop { + fn drop(&mut self) { + OPERATIONS.lock().unwrap().push(Operation::DropClass); + } + } + + let _ = BothIvarsAndTypeImplsDrop::alloc(); + check([]); + + let _ = unsafe { init_only_superclasses(BothIvarsAndTypeImplsDrop::alloc()) }; + check([Operation::DropIvar, Operation::DropClass]); + + let _ = unsafe { init_no_finalize(BothIvarsAndTypeImplsDrop::alloc()) }; + check([ + Operation::DropIvar, + Operation::DropIvar, + Operation::DropClass, + ]); + + let _ = unsafe { init(BothIvarsAndTypeImplsDrop::alloc()) }; + check([ + Operation::DropClass, + Operation::DropIvar, + Operation::DropIvar, + Operation::DropClass, + ]); + } + + #[test] + fn test_no_generated_dealloc_if_not_needed() { + #[allow(unused)] + struct Ivar { + field1: u8, + field2: bool, + } + + declare_class!( + struct IvarsNoDrop; + + unsafe impl ClassType for IvarsNoDrop { + type Super = NSObject; + type Mutability = InteriorMutable; + const NAME: &'static str = "IvarsNoDrop"; + } + + impl DeclaredClass for IvarsNoDrop { + type Ivars = Ivar; + } + ); + + assert!(!mem::needs_drop::()); + assert!(!mem::needs_drop::()); + assert_eq!( + IvarsNoDrop::class().instance_method(sel!(dealloc)), + NSObject::class().instance_method(sel!(dealloc)), + ); + } + + #[test] + fn zst_ivar() { + #[derive(Default, Debug)] + struct Ivar; + + declare_class!( + struct IvarZst; + + unsafe impl ClassType for IvarZst { + type Super = NSObject; + type Mutability = Mutable; + const NAME: &'static str = "IvarZst"; + } + + impl DeclaredClass for IvarZst { + type Ivars = Ivar; + } + + unsafe impl IvarZst { + #[method_id(init)] + fn init(this: Allocated) -> Option> { + unsafe { msg_send_id![super(this.set_ivars(Ivar)), init] } + } + } + ); + + assert_eq!( + IvarZst::class().instance_size(), + NSObject::class().instance_size(), + ); + let ivar_name = if cfg!(feature = "gnustep-1-7") { + "IvarZst_ivars" + } else { + "ivars" + }; + assert!(IvarZst::class().instance_variable(ivar_name).is_none()); + + let mut obj = unsafe { init(IvarZst::alloc()) }; + println!("{:?}", obj.ivars()); + *obj.ivars_mut() = Ivar; + } + + #[test] + #[should_panic = "unsupported alignment 16 for `HasIvarWithHighAlignment::Ivars`"] + fn test_generate_ivar_high_alignment() { + #[repr(align(16))] + struct HighAlignment; + + declare_class!( + struct HasIvarWithHighAlignment; + + unsafe impl ClassType for HasIvarWithHighAlignment { + type Super = NSObject; + type Mutability = InteriorMutable; + const NAME: &'static str = "HasIvarWithHighAlignment"; + } + + impl DeclaredClass for HasIvarWithHighAlignment { + type Ivars = HighAlignment; + } + ); + + // Have to allocate up to the desired alignment, but no need to go + // further, since the object is zero-sized. + assert_eq!(HasIvarWithHighAlignment::class().instance_size(), 16); + + let ivar_name = if cfg!(feature = "gnustep-1-7") { + "IvarZst_ivars" + } else { + "ivars" + }; + let ivar = HasIvarWithHighAlignment::class() + .instance_variable(ivar_name) + .unwrap(); + assert_eq!(ivar.offset(), 16); + } + + #[test] + fn test_ivar_access() { + declare_class!( + struct RcIvar; + + unsafe impl ClassType for RcIvar { + type Super = NSObject; + type Mutability = Mutable; + const NAME: &'static str = "RcIvar"; + } + + impl DeclaredClass for RcIvar { + type Ivars = Option>; + } + + unsafe impl RcIvar { + #[method_id(init)] + fn init(this: Allocated) -> Option> { + let this = this.set_ivars(Some(__RcTestObject::new())); + unsafe { msg_send_id![super(this), init] } + } + } + ); + + let mut expected = __ThreadTestData::current(); + + let _ = RcIvar::alloc(); + expected.assert_current(); + + let _ = unsafe { init_only_superclasses(RcIvar::alloc()) }; + expected.assert_current(); + + // Ivar access is valid even if the class is not finalized. + let mut obj = unsafe { init_no_finalize(RcIvar::alloc()) }; + expected.assert_current(); + + *obj.ivars_mut() = Some(__RcTestObject::new()); + expected.alloc += 1; + expected.init += 1; + expected.assert_current(); + + drop(obj); + expected.release += 1; + expected.drop += 1; + expected.assert_current(); + + let mut obj = unsafe { init(RcIvar::alloc()) }; + expected.alloc += 1; + expected.init += 1; + expected.assert_current(); + + *obj.ivars_mut() = obj.ivars().clone(); + expected.retain += 1; + expected.release += 1; + expected.assert_current(); + + drop(obj); + expected.release += 1; + expected.drop += 1; + expected.assert_current(); + + #[derive(Default, Debug, PartialEq, Eq)] + struct RcIvarSubclassIvars { + int: i32, + obj: Id<__RcTestObject>, + } + + declare_class!( + struct RcIvarSubclass; + + unsafe impl ClassType for RcIvarSubclass { + type Super = RcIvar; + type Mutability = Mutable; + const NAME: &'static str = "RcIvarSubclass"; + } + + impl DeclaredClass for RcIvarSubclass { + type Ivars = RcIvarSubclassIvars; + } + + unsafe impl RcIvarSubclass { + #[method_id(init)] + fn init(this: Allocated) -> Option> { + let this = this.set_ivars(RcIvarSubclassIvars { + int: 42, + obj: __RcTestObject::new(), + }); + unsafe { msg_send_id![super(this), init] } + } + } + ); + + let mut obj = unsafe { init(RcIvarSubclass::alloc()) }; + expected.alloc += 2; + expected.init += 2; + expected.assert_current(); + assert_eq!(obj.ivars().int, 42); + + obj.ivars_mut().int += 1; + assert_eq!(obj.ivars().int, 43); + + obj.ivars_mut().obj = (**obj).ivars().clone().unwrap(); + expected.retain += 1; + expected.release += 1; + expected.drop += 1; + expected.assert_current(); + + *(**obj).ivars_mut() = None; + expected.release += 1; + expected.assert_current(); + + drop(obj); + expected.release += 1; + expected.drop += 1; + expected.assert_current(); + + let obj = unsafe { init_only_superclasses(RcIvarSubclass::alloc()) }; + expected.alloc += 1; + expected.init += 1; + expected.assert_current(); + + // Accessing superclass ivars is valid + println!("{:?}", (**obj).ivars()); + + drop(obj); + expected.release += 1; + expected.drop += 1; + expected.assert_current(); + } + + #[test] + #[cfg_attr(not(debug_assertions), ignore = "only panics with debug assertions")] + #[should_panic = "tried to access uninitialized instance variable"] + fn access_invalid() { + declare_class!( + struct InvalidAccess; + + unsafe impl ClassType for InvalidAccess { + type Super = NSObject; + type Mutability = InteriorMutable; + const NAME: &'static str = "InvalidAccess"; + } + + impl DeclaredClass for InvalidAccess { + // Type has to have a drop flag to detect invalid access + type Ivars = Id; + } + ); + + let obj = unsafe { init_only_superclasses(InvalidAccess::alloc()) }; + println!("{:?}", obj.ivars()); + } + + #[test] + #[should_panic = "panic in drop"] + fn test_panic_in_drop() { + declare_class!( + struct DropPanics; + + unsafe impl ClassType for DropPanics { + type Super = NSObject; + type Mutability = InteriorMutable; + const NAME: &'static str = "DropPanics"; + } + + impl DeclaredClass for DropPanics {} + ); + + impl Drop for DropPanics { + fn drop(&mut self) { + panic!("panic in drop"); + } + } + + let obj = DropPanics::alloc().set_ivars(()); + let obj: Id = unsafe { msg_send_id![super(obj), init] }; + drop(obj); + } + + #[test] + #[should_panic = "panic in ivar drop"] + fn test_panic_in_ivar_drop() { + struct DropPanics; + + impl Drop for DropPanics { + fn drop(&mut self) { + panic!("panic in ivar drop"); + } + } + + declare_class!( + struct IvarDropPanics; + + unsafe impl ClassType for IvarDropPanics { + type Super = NSObject; + type Mutability = InteriorMutable; + const NAME: &'static str = "IvarDropPanics"; + } + + impl DeclaredClass for IvarDropPanics { + type Ivars = DropPanics; + } + ); + + let obj = IvarDropPanics::alloc().set_ivars(DropPanics); + let obj: Id = unsafe { msg_send_id![super(obj), init] }; + drop(obj); + } +} 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..11112b552 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] @@ -318,21 +509,60 @@ mod tests { use crate::runtime::{AnyObject, NSObject, NSZone}; use crate::{class, msg_send_id, ClassType}; + mod test_trait_disambugated { + use super::*; + + trait Abc { + fn send_message_id(&self) {} + } + + impl Abc for T {} + + #[test] + fn test_macro_still_works() { + let _: Id = unsafe { msg_send_id![NSObject::class(), new] }; + } + } + + // `new` family + #[test] fn test_new() { - let _obj: Id = unsafe { msg_send_id![NSObject::class(), new] }; - let _obj: Option> = unsafe { msg_send_id![NSObject::class(), new] }; + let mut expected = __ThreadTestData::current(); + let cls = __RcTestObject::class(); + + let _obj: Id = unsafe { msg_send_id![cls, new] }; + let _obj: Option> = unsafe { msg_send_id![cls, new] }; + // This is just a roundabout way of calling `[__RcTestObject new]`. + let _obj: Id = unsafe { msg_send_id![super(cls, cls.metaclass()), new] }; + let _obj: Option> = unsafe { msg_send_id![super(cls, cls.metaclass()), new] }; + + // `__RcTestObject` does not override `new`, so this just ends up + // calling `[[__RcTestObject alloc] init]` as usual. + let _obj: Id<__RcTestObject> = + unsafe { msg_send_id![super(cls, NSObject::class().metaclass()), new] }; + + expected.alloc += 5; + expected.init += 5; + expected.assert_current(); } #[test] fn test_new_not_on_class() { let mut expected = __ThreadTestData::current(); let obj = __RcTestObject::new(); + expected.alloc += 1; + expected.init += 1; + expected.assert_current(); let _obj: Id = unsafe { msg_send_id![&obj, newMethodOnInstance] }; let _obj: Option> = unsafe { msg_send_id![&obj, newMethodOnInstance] }; - expected.alloc += 3; - expected.init += 3; + let _obj: Id = + unsafe { msg_send_id![super(&obj, __RcTestObject::class()), newMethodOnInstance] }; + let _obj: Option> = + unsafe { msg_send_id![super(&obj, __RcTestObject::class()), newMethodOnInstance] }; + expected.alloc += 4; + expected.init += 4; expected.assert_current(); } @@ -362,7 +592,82 @@ mod tests { } #[test] - fn test_macro_alloc() { + #[should_panic = "failed creating new instance of NSValue"] + // GNUStep instead returns an invalid instance that panics on accesses + #[cfg_attr(feature = "gnustep-1-7", ignore)] + fn new_nsvalue_fails() { + let _val: Id = unsafe { msg_send_id![class!(NSValue), new] }; + } + + #[test] + #[should_panic = "failed creating new instance using +[__RcTestObject newReturningNull]"] + fn test_new_with_null() { + let _obj: Id<__RcTestObject> = + unsafe { msg_send_id![__RcTestObject::class(), newReturningNull] }; + } + + #[test] + #[should_panic = "failed creating new instance using +[__RcTestObject newReturningNull]"] + fn test_super_new_with_null() { + let _: Id<__RcTestObject> = unsafe { + msg_send_id![ + super(__RcTestObject::class(), __RcTestObject::class().metaclass()), + newReturningNull + ] + }; + } + + #[test] + #[should_panic = "unexpected NULL returned from -[__RcTestObject newMethodOnInstanceNull]"] + fn test_new_any_with_null() { + let obj = __RcTestObject::new(); + let _obj: Id = unsafe { msg_send_id![&obj, newMethodOnInstanceNull] }; + } + + #[test] + #[should_panic = "unexpected NULL returned from -[__RcTestObject newMethodOnInstanceNull]"] + fn test_super_new_any_with_null() { + let obj = __RcTestObject::new(); + let _obj: Id = unsafe { + msg_send_id![ + super(&obj, __RcTestObject::class()), + newMethodOnInstanceNull + ] + }; + } + + #[test] + #[cfg_attr( + debug_assertions, + should_panic = "messsaging newMethodOnInstance to nil" + )] + #[cfg_attr( + not(debug_assertions), + ignore = "unexpected NULL newMethodOnInstance; receiver was NULL" + )] + fn test_new_any_with_null_receiver() { + let obj: *const NSObject = ptr::null(); + let _obj: Id = unsafe { msg_send_id![obj, newMethodOnInstance] }; + } + + #[test] + #[cfg_attr( + debug_assertions, + should_panic = "messsaging newMethodOnInstance to nil" + )] + #[cfg_attr( + not(debug_assertions), + ignore = "unexpected NULL newMethodOnInstance; receiver was NULL" + )] + fn test_super_new_any_with_null_receiver() { + let obj: *const __RcTestObject = ptr::null(); + let _obj: Id = unsafe { msg_send_id![super(obj), newMethodOnInstance] }; + } + + // `alloc` family + + #[test] + fn test_alloc() { let mut expected = __ThreadTestData::current(); let cls = __RcTestObject::class(); @@ -372,7 +677,18 @@ mod tests { drop(obj); expected.release += 1; - expected.dealloc += 1; + // Drop flag ensures uninitialized do not Drop + // expected.drop += 1; + expected.assert_current(); + + // `+[NSObject alloc]` forwards to `allocWithZone:`, so this still + // allocates a `__RcTestObject`. + let _: Allocated = + unsafe { msg_send_id![super(cls, NSObject::class().metaclass()), alloc] }; + expected.alloc += 1; + expected.release += 1; + // Drop flag ensures uninitialized do not Drop + // expected.drop += 1; expected.assert_current(); } @@ -380,138 +696,184 @@ mod tests { fn test_alloc_with_zone() { let mut expected = __ThreadTestData::current(); let cls = __RcTestObject::class(); - let zone: *const NSZone = ptr::null(); + let _obj: Allocated<__RcTestObject> = unsafe { msg_send_id![cls, allocWithZone: zone] }; expected.alloc += 1; expected.assert_current(); + + let _obj: Allocated<__RcTestObject> = + unsafe { msg_send_id![super(cls, cls.metaclass()), allocWithZone: zone] }; + expected.alloc += 1; + expected.assert_current(); + + let _obj: Allocated = + unsafe { msg_send_id![super(cls, NSObject::class().metaclass()), allocWithZone: zone] }; + expected.assert_current(); } #[test] - fn test_macro_init() { + fn test_alloc_with_null() { + let obj: Allocated<__RcTestObject> = + unsafe { msg_send_id![__RcTestObject::class(), allocReturningNull] }; + assert!(Allocated::as_ptr(&obj).is_null()); + } + + // `init` family + + #[test] + fn test_init() { let mut expected = __ThreadTestData::current(); - let cls = __RcTestObject::class(); - let obj: Allocated<__RcTestObject> = unsafe { msg_send_id![cls, alloc] }; + let _: Id<__RcTestObject> = unsafe { msg_send_id![__RcTestObject::alloc(), init] }; expected.alloc += 1; - expected.assert_current(); - - // Don't check allocation error - let _obj: Id<__RcTestObject> = unsafe { msg_send_id![obj, init] }; expected.init += 1; + expected.release += 1; + expected.drop += 1; expected.assert_current(); - let obj: Allocated<__RcTestObject> = unsafe { msg_send_id![cls, alloc] }; + let obj = __RcTestObject::alloc().set_ivars(()); + let _: Id<__RcTestObject> = unsafe { msg_send_id![super(obj), init] }; expected.alloc += 1; + expected.release += 1; + expected.drop += 1; expected.assert_current(); // Check allocation error before init + let obj = __RcTestObject::alloc(); + expected.alloc += 1; assert!(!Allocated::as_ptr(&obj).is_null()); - let _obj: Id<__RcTestObject> = unsafe { msg_send_id![obj, init] }; + let _: Id<__RcTestObject> = unsafe { msg_send_id![obj, init] }; expected.init += 1; + expected.release += 1; + expected.drop += 1; expected.assert_current(); } #[test] - fn test_macro() { - let mut expected = __ThreadTestData::current(); - let cls = __RcTestObject::class(); - crate::rc::autoreleasepool(|_| { - let _obj: Id<__RcTestObject> = unsafe { msg_send_id![cls, new] }; - expected.alloc += 1; - expected.init += 1; - expected.assert_current(); - - let obj = unsafe { msg_send_id![cls, alloc] }; - expected.alloc += 1; - expected.assert_current(); - - let obj: Id<__RcTestObject> = unsafe { msg_send_id![obj, init] }; - expected.init += 1; - expected.assert_current(); - - let _copy: Id<__RcTestObject> = unsafe { msg_send_id![&obj, copy] }; - expected.copy += 1; - expected.alloc += 1; - expected.init += 1; - expected.assert_current(); - - let _mutable_copy: Id<__RcTestObject> = unsafe { msg_send_id![&obj, mutableCopy] }; - expected.mutable_copy += 1; - expected.alloc += 1; - expected.init += 1; - expected.assert_current(); - - let _self: Id<__RcTestObject> = unsafe { msg_send_id![&obj, self] }; - expected.retain += 1; - expected.assert_current(); - - let _desc: Option> = unsafe { msg_send_id![&obj, description] }; - expected.assert_current(); - }); - expected.release += 5; - expected.dealloc += 4; - expected.assert_current(); + #[should_panic = "failed initializing object with -initReturningNull"] + fn test_init_with_null() { + let obj: Allocated<__RcTestObject> = + unsafe { msg_send_id![__RcTestObject::class(), alloc] }; + let _obj: Id<__RcTestObject> = unsafe { msg_send_id![obj, initReturningNull] }; } #[test] - #[should_panic = "failed creating new instance of NSValue"] - // GNUStep instead returns an invalid instance that panics on accesses - #[cfg_attr(feature = "gnustep-1-7", ignore)] - fn new_nsvalue_fails() { - let _val: Id = unsafe { msg_send_id![class!(NSValue), new] }; + #[cfg_attr(debug_assertions, should_panic = "messsaging init to nil")] + #[cfg_attr(not(debug_assertions), ignore = "failed allocating object")] + fn test_init_with_null_receiver() { + let obj: Allocated<__RcTestObject> = + unsafe { msg_send_id![__RcTestObject::class(), allocReturningNull] }; + let _obj: Id<__RcTestObject> = unsafe { msg_send_id![obj, init] }; } #[test] - #[should_panic = "failed creating new instance using +[__RcTestObject newReturningNull]"] - fn test_new_with_null() { - let _obj: Id<__RcTestObject> = - unsafe { msg_send_id![__RcTestObject::class(), newReturningNull] }; + #[should_panic = "tried to initialize ivars after they were already initialized"] + #[cfg_attr(not(debug_assertions), ignore = "only checked with debug assertions")] + fn test_super_init_not_initialized() { + let obj = __RcTestObject::alloc().set_ivars(()); + let _: Id<__RcTestObject> = + unsafe { msg_send_id![super(obj, __RcTestObject::class()), init] }; } #[test] - #[should_panic = "unexpected NULL returned from -[__RcTestObject newMethodOnInstanceNull]"] - fn test_new_any_with_null() { - let obj = __RcTestObject::new(); - let _obj: Id = unsafe { msg_send_id![&obj, newMethodOnInstanceNull] }; + #[should_panic = "tried to finalize an already finalized object"] + #[cfg_attr(not(debug_assertions), ignore = "only checked with debug assertions")] + fn test_super_init_not_finalized() { + let obj = unsafe { PartialInit::new(Allocated::into_ptr(__RcTestObject::alloc())) }; + let _: Id<__RcTestObject> = + unsafe { msg_send_id![super(obj, __RcTestObject::class()), init] }; } + // `copy` family + #[test] - #[should_panic = "unexpected NULL newMethodOnInstance; receiver was NULL"] - #[cfg(not(debug_assertions))] // Does NULL receiver checks - fn test_new_any_with_null_receiver() { - let obj: *const NSObject = ptr::null(); - let _obj: Id = unsafe { msg_send_id![obj, newMethodOnInstance] }; + fn test_copy() { + let obj = __RcTestObject::new(); + let mut expected = __ThreadTestData::current(); + + let _: Id<__RcTestObject> = unsafe { msg_send_id![&obj, copy] }; + expected.copy += 1; + expected.alloc += 1; + expected.init += 1; + expected.release += 1; + expected.drop += 1; + expected.assert_current(); + + // `+[NSObject copy]` forwards to `copyWithZone:`, so this still + // creates a `__RcTestObject`. + let _: Id = unsafe { msg_send_id![super(&obj), copy] }; + expected.copy += 1; + expected.alloc += 1; + expected.init += 1; + expected.release += 1; + expected.drop += 1; + expected.assert_current(); } #[test] - fn test_alloc_with_null() { - let _obj: Allocated<__RcTestObject> = - unsafe { msg_send_id![__RcTestObject::class(), allocReturningNull] }; + #[should_panic = "failed copying object"] + fn test_copy_with_null() { + let obj = __RcTestObject::new(); + let _obj: Id<__RcTestObject> = unsafe { msg_send_id![&obj, copyReturningNull] }; } #[test] - #[should_panic = "failed initializing object with -initReturningNull"] - fn test_init_with_null() { - let obj: Allocated<__RcTestObject> = - unsafe { msg_send_id![__RcTestObject::class(), alloc] }; - let _obj: Id<__RcTestObject> = unsafe { msg_send_id![obj, initReturningNull] }; + #[should_panic = "failed copying object"] + fn test_super_copy_with_null() { + let obj = __RcTestObject::new(); + let _obj: Id<__RcTestObject> = + unsafe { msg_send_id![super(&obj, __RcTestObject::class()), copyReturningNull] }; } + // `mutableCopy` family + #[test] - #[should_panic = "failed allocating object"] - #[cfg(not(debug_assertions))] // Does NULL receiver checks - fn test_init_with_null_receiver() { - let obj: Allocated<__RcTestObject> = - unsafe { msg_send_id![__RcTestObject::class(), allocReturningNull] }; - let _obj: Id<__RcTestObject> = unsafe { msg_send_id![obj, init] }; + fn test_mutable_copy() { + let obj = __RcTestObject::new(); + let mut expected = __ThreadTestData::current(); + + let _: Id<__RcTestObject> = unsafe { msg_send_id![&obj, mutableCopy] }; + expected.mutable_copy += 1; + expected.alloc += 1; + expected.init += 1; + expected.release += 1; + expected.drop += 1; + expected.assert_current(); + + // `+[NSObject mutableCopy]` forwards to `mutableCopyWithZone:`, so + // this still creates a `__RcTestObject`. + let _: Id = unsafe { msg_send_id![super(&obj), mutableCopy] }; + expected.mutable_copy += 1; + expected.alloc += 1; + expected.init += 1; + expected.release += 1; + expected.drop += 1; + expected.assert_current(); } + // No method family + #[test] - #[should_panic = "failed copying object"] - fn test_copy_with_null() { + fn test_normal() { let obj = __RcTestObject::new(); - let _obj: Id<__RcTestObject> = unsafe { msg_send_id![&obj, copyReturningNull] }; + let mut expected = __ThreadTestData::current(); + + let _: Id<__RcTestObject> = unsafe { msg_send_id![&obj, self] }; + expected.retain += 1; + expected.release += 1; + expected.assert_current(); + + let _: Id<__RcTestObject> = unsafe { msg_send_id![super(&obj), self] }; + expected.retain += 1; + expected.release += 1; + expected.assert_current(); + + let _: Option> = unsafe { msg_send_id![&obj, description] }; + expected.assert_current(); + + let _: Option> = unsafe { msg_send_id![super(&obj), description] }; + expected.assert_current(); } #[test] @@ -529,26 +891,13 @@ mod tests { } #[test] - #[should_panic = "unexpected NULL description; receiver was NULL"] - #[cfg(not(debug_assertions))] // Does NULL receiver checks + #[cfg_attr(debug_assertions, should_panic = "messsaging description to nil")] + #[cfg_attr( + not(debug_assertions), + ignore = "unexpected NULL description; receiver was NULL" + )] fn test_normal_with_null_receiver() { let obj: *const NSObject = ptr::null(); let _obj: Id = unsafe { msg_send_id![obj, description] }; } - - mod test_trait_disambugated { - use super::*; - - trait Abc { - fn send_message_id() {} - } - - impl Abc for T {} - - #[test] - fn test_macro_still_works() { - let cls = class!(NSObject); - let _obj: Id = unsafe { msg_send_id![cls, new] }; - } - } } 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 d9dbedd74..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::{ivar_offset, 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 __offset(ptr: NonNull) -> isize { - let obj = unsafe { ptr.as_ref() }; - ivar_offset(obj.class(), Self::NAME, &Self::Type::ENCODING) - } -} - -/// 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. - let offset = unsafe { T::__offset(ptr) }; - // SAFETY: The offset is valid - unsafe { AnyObject::ivar_at_offset::(ptr, offset) } - } - - /// 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` - let offset = unsafe { T::__offset(ptr) }; - // SAFETY: The offset is valid - unsafe { AnyObject::ivar_at_offset::(ptr, offset) } - } - - /// 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 490da7310..d2e3226ff 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); @@ -96,8 +85,9 @@ impl Log2Alignment for T { /// ) -> Option<&mut AnyObject> { /// let this: Option<&mut AnyObject> = msg_send![super(this, NSObject::class()), init]; /// this.map(|this| { +/// let ivar = AnyClass::get("MyNumber").unwrap().instance_variable("_number").unwrap(); /// // SAFETY: The ivar is added with the same type above -/// this.set_ivar::>("_number", Cell::new(number)); +/// *ivar.load_mut::>(this) = Cell::new(number); /// this /// }) /// } @@ -131,8 +121,9 @@ impl Log2Alignment for T { /// /// // Add an Objective-C method for setting the number /// extern "C" fn my_number_set(this: &NSObject, _cmd: Sel, number: u32) { +/// let ivar = AnyClass::get("MyNumber").unwrap().instance_variable("_number").unwrap(); /// // SAFETY: The ivar is added with the same type above -/// unsafe { this.ivar::>("_number") }.set(number); +/// unsafe { ivar.load::>(this) }.set(number); /// } /// unsafe { /// builder.add_method(sel!(setNumber:), my_number_set as extern "C" fn(_, _, _)); @@ -140,8 +131,9 @@ impl Log2Alignment for T { /// /// // Add an Objective-C method for getting the number /// extern "C" fn my_number_get(this: &NSObject, _cmd: Sel) -> u32 { +/// let ivar = AnyClass::get("MyNumber").unwrap().instance_variable("_number").unwrap(); /// // SAFETY: The ivar is added with the same type above -/// unsafe { this.ivar::>("_number") }.get() +/// unsafe { ivar.load::>(this) }.get() /// } /// unsafe { /// builder.add_method(sel!(number), my_number_get as extern "C" fn(_, _) -> _); @@ -396,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, @@ -404,25 +400,17 @@ impl ClassBuilder { align: u8, encoding: &Encoding, ) { - // `class_addIvar` sadly doesn't check this for us. - // - // We must _always_ do the check, since there is no way for the user - // to statically know if the superclass has a declared instance - // variable on it, since that may change if a new version of the - // library/framework the class came from is released. - if let Some(_ivar) = self - .superclass() - .and_then(|superclass| superclass.instance_variable(name)) - { - panic!("instance variable {name:?} already exists on a superclass"); - } - let c_name = CString::new(name).unwrap(); let encoding = CString::new(encoding.to_string()).unwrap(); // Note: The Objective-C runtime contains functionality to do stuff // with "instance variable layouts", but we don't have to touch any of // that, it was only used in the garbage-collecting runtime. + // + // Note: On GNUStep, instance variables cannot have the same name + // on subclasses as it has on superclasses. + // + // See let success = Bool::from_raw(unsafe { ffi::class_addIvar( self.as_mut_ptr(), @@ -435,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 @@ -614,12 +587,17 @@ mod tests { use std::collections::hash_map::DefaultHasher; use std::hash::Hash; + use memoffset::offset_of; + use super::*; use crate::encode::RefEncode; use crate::mutability::Immutable; use crate::rc::Id; use crate::runtime::{NSObject, NSObjectProtocol}; - use crate::{declare_class, extern_methods, msg_send, test_utils, ClassType, ProtocolType}; + use crate::{ + declare_class, extern_methods, msg_send, msg_send_id, test_utils, ClassType, DeclaredClass, + ProtocolType, + }; #[test] fn test_alignment() { @@ -906,6 +884,8 @@ mod tests { type Mutability = Immutable; const NAME: &'static str = "TestInheritedNSObjectMethodsWork"; } + + impl DeclaredClass for Custom {} ); extern_methods!( @@ -924,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 @@ -945,4 +925,145 @@ mod tests { assert!(obj1.is_kind_of::()); assert!(obj1.is_kind_of::()); } + + #[test] + #[cfg_attr( + feature = "gnustep-1-7", + ignore = "ivars cannot have the same name on GNUStep" + )] + fn test_ivar_sizing() { + #[repr(align(16))] + struct U128align16([u64; 2]); + + unsafe impl Encode for U128align16 { + const ENCODING: Encoding = <[u64; 2]>::ENCODING; + } + + let mut superclass = + ClassBuilder::new("DeclareClassDuplicateIvarSuperclass", NSObject::class()).unwrap(); + superclass.add_ivar::("ivar1"); + superclass.add_ivar::("ivar2"); + superclass.add_ivar::("ivar3"); + superclass.add_ivar::<[u8; 0]>("ivar4"); + let superclass = superclass.register(); + + let mut subclass = + ClassBuilder::new("DeclareClassDuplicateIvarSubclass", superclass).unwrap(); + // Try to overwrite instance variables + subclass.add_ivar::("ivar1"); + subclass.add_ivar::("ivar2"); + subclass.add_ivar::<*const AnyObject>("ivar3"); + subclass.add_ivar::("ivar4"); + let subclass = subclass.register(); + + // Test that ivar layout matches that of C + // + // In particular, ivars are not reordered, though any extra padding on + // superclasses are utilized on subclasses. + #[repr(C)] + struct NSObjectLayout { + isa: *const AnyClass, + } + assert_eq!( + NSObject::class().instance_size(), + mem::size_of::(), + ); + + #[repr(C)] + struct SuperLayout { + isa: *const AnyClass, + ivar1: u8, + // Padding (7 on 64bit, 11 on 32bit) + ivar2: U128align16, + ivar3: u8, + ivar4: [u8; 0], + // Padding (15 in Rust, 7 on 64bit, 3 on 32bit) + } + // Class's ivar size is only rounded up to a pointer-sized boundary, + // not all the way up to the maximum alignment. + // + // This is surprising, but actually fine, since Objective-C objects + // are never packed closely like Rust structs would be in an array. + assert_eq!( + superclass.instance_size(), + mem::size_of::() - 16 + mem::size_of::<*const AnyClass>(), + ); + + #[repr(C)] + struct SubLayout { + isa: *const AnyClass, + ivar1: u8, + // Padding (7 on 64bit, 11 on 32bit) + ivar2: U128align16, + ivar3: u8, + ivar4: [u8; 0], + // Padding (1) + ivar1_b: i16, + // Padding (4) + ivar2_b: usize, + ivar3_b: *const AnyObject, + ivar4_b: usize, + } + assert_eq!(subclass.instance_size(), mem::size_of::()); + + let superclass_ivar1 = superclass.instance_variable("ivar1").unwrap(); + let superclass_ivar2 = superclass.instance_variable("ivar2").unwrap(); + let superclass_ivar3 = superclass.instance_variable("ivar3").unwrap(); + let superclass_ivar4 = superclass.instance_variable("ivar4").unwrap(); + let subclass_ivar1 = subclass.instance_variable("ivar1").unwrap(); + let subclass_ivar2 = subclass.instance_variable("ivar2").unwrap(); + let subclass_ivar3 = subclass.instance_variable("ivar3").unwrap(); + let subclass_ivar4 = subclass.instance_variable("ivar4").unwrap(); + + // Ensure that duplicate names do not conflict + assert_ne!(superclass_ivar1, subclass_ivar1); + assert_ne!(superclass_ivar2, subclass_ivar2); + assert_ne!(superclass_ivar3, subclass_ivar3); + assert_ne!(superclass_ivar4, subclass_ivar4); + + // Ensure that all offsets are as expected + assert_eq!( + superclass_ivar1.offset(), + offset_of!(SuperLayout, ivar1) as isize + ); + assert_eq!( + superclass_ivar2.offset(), + offset_of!(SuperLayout, ivar2) as isize + ); + assert_eq!( + superclass_ivar3.offset(), + offset_of!(SuperLayout, ivar3) as isize + ); + assert_eq!( + superclass_ivar4.offset(), + offset_of!(SuperLayout, ivar4) as isize + ); + assert_eq!( + subclass_ivar1.offset(), + offset_of!(SubLayout, ivar1_b) as isize + ); + assert_eq!( + subclass_ivar2.offset(), + offset_of!(SubLayout, ivar2_b) as isize + ); + assert_eq!( + subclass_ivar3.offset(), + offset_of!(SubLayout, ivar3_b) as isize + ); + assert_eq!( + subclass_ivar4.offset(), + offset_of!(SubLayout, ivar4_b) as isize + ); + + // Ensure our ivar loading works correctly + let obj: Id = unsafe { msg_send_id![subclass, new] }; + let ptr = unsafe { *subclass_ivar3.load::<*const AnyObject>(&obj) }; + assert!(ptr.is_null()); + + // Illustration of what goes wrong with the naive approach of loading + // the Ivar dynamically; in short, we can't be sure of which instance + // variable we're refering to here. + // + // let ivar = *obj.get_ivar::("ivar3"); + } } 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..db9d073a2 100644 --- a/crates/objc2/src/macros/declare_class.rs +++ b/crates/objc2/src/macros/declare_class.rs @@ -16,62 +16,74 @@ /// /// # 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, which will call `drop` 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 if you want to use the class' inherited initializers (such as +/// `init`), you must override the subclass' designated initializers, and +/// initialize your ivars properly in there. +/// +/// [`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 +154,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 +167,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 +191,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 +205,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 +263,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 +274,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 +304,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 +324,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 +364,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,62 +376,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; - - 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) - ($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)* - } + $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; @@ -435,101 +389,24 @@ macro_rules! declare_class { const NAME: &'static str = $name_const:expr; } - $($impls:tt)* - } => { - $crate::__emit_struct_and_ivars! { - ($(#[$m])*) - ($v) - ($name) - () - ($($fields)*) - ( - // Superclasses are deallocated by calling `[super dealloc]`. - __superclass: $crate::__macro_helpers::ManuallyDrop<$superclass>, - ) - } - - $crate::__declare_class_inner! { - () - - unsafe impl ClassType for $for { - $(#[inherits($($inheritance_rest),+)])? - type Super = $superclass; - - type Mutability = $mutability; - - const NAME: &'static str = $name_const; - } - - $($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; + impl DeclaredClass for $for_declared:ty { + $(type Ivars = $ivars:ty;)? } $($impls:tt)* } => { - $crate::__emit_struct_and_ivars! { - ($(#[$m])*) - ($v) - ($name) - () - () - ( - // Superclasses are deallocated by calling `[super dealloc]`. - __superclass: $crate::__macro_helpers::ManuallyDrop<$superclass>, - ) - } - - $crate::__declare_class_inner! { - () - - unsafe impl ClassType for $for { - $(#[inherits($($inheritance_rest),+)])? - type Super = $superclass; - - type Mutability = $mutability; - - const NAME: &'static str = $name_const; - } - - $($impls)* - } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __declare_class_inner { - { - ($($ivar_helper_module: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; + $(#[$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>, } - $($impls:tt)* - } => { $crate::__extern_class_impl_traits! { // SAFETY: Upheld by caller - unsafe impl () for $for { + unsafe impl () for $for_class { INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::AnyObject]; fn as_super(&self) { @@ -542,85 +419,103 @@ macro_rules! __declare_class_inner { } } - // Creation - unsafe impl ClassType for $for { - type Super = $superclass; - type Mutability = $mutability; - const NAME: &'static $crate::__macro_helpers::str = $name_const; + // 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(); - fn class() -> &'static $crate::runtime::AnyClass { - $crate::__macro_helpers::assert_mutability_matches_superclass_mutability::(); + // Creation + unsafe impl ClassType for $for_class { + type Super = $superclass; + type Mutability = $mutability; + const NAME: &'static $crate::__macro_helpers::str = $name_const; - // TODO: Use `core::cell::LazyCell` - static REGISTER_CLASS: $crate::__macro_helpers::Once = $crate::__macro_helpers::Once::new(); + fn class() -> &'static $crate::runtime::AnyClass { + $crate::__macro_helpers::assert_mutability_matches_superclass_mutability::(); - REGISTER_CLASS.call_once(|| { - let mut __objc2_builder = $crate::__macro_helpers::ClassBuilderHelper::::new(); + // TODO: Use `std::sync::OnceLock` + static REGISTER_CLASS: $crate::__macro_helpers::Once = $crate::__macro_helpers::Once::new(); - $($ivar_helper_module::__objc2_declare_ivars(&mut __objc2_builder);)? + REGISTER_CLASS.call_once(|| { + let mut __objc2_builder = $crate::__macro_helpers::ClassBuilderHelper::::new(); - // 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 - ) + // Implement protocols and methods + $crate::__declare_class_register_impls! { + (__objc2_builder) + $($impls)* } - } - if $crate::__macro_helpers::needs_drop::() { + let (__objc2_cls, __objc2_ivar_offset, __objc2_drop_flag_offset) = __objc2_builder.register(); + + // SAFETY: Modification is ensured by `Once` to happen + // before any access to the variables. unsafe { - __objc2_builder.add_method( - $crate::sel!(dealloc), - __objc2_dealloc as unsafe extern "C" fn(_, _), - ); + __OBJC2_CLASS.write(__objc2_cls); + if ::HAS_IVARS { + __OBJC2_IVAR_OFFSET.write(__objc2_ivar_offset); + } + if ::HAS_DROP_FLAG { + __OBJC2_DROP_FLAG_OFFSET.write(__objc2_drop_flag_offset); + } } - } + }); - // Implement protocols and methods - $crate::__declare_class_register_impls! { - (__objc2_builder) - $($impls)* - } + // SAFETY: We just registered the class, so is now available + unsafe { __OBJC2_CLASS.assume_init() } + } - let _cls = __objc2_builder.register(); - }); + #[inline] + fn as_super(&self) -> &Self::Super { + &*self.__superclass + } - // We just registered the class, so it should be available - $crate::runtime::AnyClass::get(::NAME).unwrap() + #[inline] + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut *self.__superclass + } } - #[inline] - fn as_super(&self) -> &Self::Super { - &*self.__superclass - } + impl DeclaredClass for $for_declared { + type Ivars = $crate::__select_ivars!($($ivars)?); + + #[inline] + fn __ivars_offset() -> $crate::__macro_helpers::isize { + // Only access ivar offset if we have an ivar. + // + // This makes the offset not be included in the final + // executable if it's not needed. + if ::HAS_IVARS { + // SAFETY: Accessing the offset is guaranteed to only be + // done after the class has been initialized. + unsafe { __OBJC2_IVAR_OFFSET.assume_init() } + } else { + // Fall back 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 + } + } + + #[inline] + fn __drop_flag_offset() -> $crate::__macro_helpers::isize { + if ::HAS_DROP_FLAG { + // SAFETY: Same as above. + unsafe { __OBJC2_DROP_FLAG_OFFSET.assume_init() } + } 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 + } + } - #[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 +535,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 +1013,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_class.rs b/crates/objc2/src/macros/extern_class.rs index 1907a9d2e..93edafd1e 100644 --- a/crates/objc2/src/macros/extern_class.rs +++ b/crates/objc2/src/macros/extern_class.rs @@ -28,8 +28,8 @@ /// - [`Borrow<$inheritance_chain>`][core::borrow::Borrow] /// - [`BorrowMut<$inheritance_chain>`][core::borrow::BorrowMut] /// -/// The macro allows specifying fields on the struct, but _only_ zero-sized -/// types like [`PhantomData`] and [`declare::Ivar`] are allowed here. +/// The macro allows specifying zero-sized fields like [`PhantomData`] on the +/// struct. /// /// You can add most attributes to the class, including `#[cfg(...)]`, /// `#[derive(...)]` and doc comments (but not ABI-modifying attributes like @@ -37,7 +37,6 @@ /// /// [rustfmt-macros]: https://github.com/rust-lang/rustfmt/discussions/5437 /// [`PhantomData`]: core::marker::PhantomData -/// [`declare::Ivar`]: crate::declare::Ivar /// /// /// ## `ClassType` implementation 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 deleted file mode 100644 index fee507913..000000000 --- a/crates/objc2/src/rc/allocated.rs +++ /dev/null @@ -1,174 +0,0 @@ -use core::fmt; -use core::marker::PhantomData; -use core::mem::ManuallyDrop; - -use crate::mutability::IsMutable; -use crate::runtime::{objc_release_fast, AnyObject}; -use crate::Message; - -/// An Objective-C object that has been allocated, but not initialized. -/// -/// Objective-C splits the allocation and initialization steps up into two, so -/// we need to track it in the type-system whether something has been -/// intialized or not. -/// -/// Note that allocation in Objective-C can fail, e.g. in Out Of Memory -/// situations! This is handled by `objc2` automatically, but if you really -/// need to, you can check for this explicitly by inspecting the pointer -/// returned from [`as_ptr`]. -/// -/// Note also that this represents that the _current_ class's instance -/// variables are not yet initialized; but subclass instance variables may -/// have been so. -/// -/// See [Apple's documentation on Object Allocation][object-allocation] for a -/// few more details. -/// -/// [`as_ptr`]: Self::as_ptr -/// [object-allocation]: https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ObjectAllocation/ObjectAllocation.html -/// -/// -/// # Memory layout -/// -/// This is guaranteed to have the same size and alignment as a pointer to the -/// object, `*const T`. The pointer may be NULL. -#[repr(transparent)] -#[derive(Debug)] -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 - /// makes it difficult to ensure correctness if such things are split - /// across different files). Additionally, we want to have fine control - /// over NULL-ness. - /// - /// Covariance is correct, same as `Id`. - ptr: *const T, // Intentially not `NonNull`! - /// Necessary for dropck, as with `Id`. - p: PhantomData, - /// Necessary for restricting auto traits. - 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! - -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. - #[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 `Allocated` is held. - /// - /// See [`Allocated::as_mut_ptr`] for the mutable equivalent. - /// - /// This is an associated method, and must be called as - /// `Allocated::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 `Allocated` is held. - /// - /// See [`Allocated::as_ptr`] for the immutable equivalent. - /// - /// This is an associated method, and must be called as - /// `Allocated::as_mut_ptr(obj)`. - /// - /// - /// # Note about mutable references - /// - /// In general, you're not allowed to create a mutable reference from - /// `Allocated`, unless you're defining the object and know that to be - /// safe. - /// - /// For example, `+[NSMutableString alloc]` is allowed to return a - /// non-unique object as an optimization, and then only figure out - /// afterwards whether it needs to allocate, or if it can store an - /// `NSString` internally. - /// - /// Similarly, while e.g. `+[NSData alloc]` may return a unique object, - /// calling `-[NSData init]` afterwards could return a shared empty - /// `NSData` instance. - #[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 Allocated { - #[inline] - fn drop(&mut self) { - // SAFETY: Allocated objects can always safely be released, since - // destructors are written to take into account that the object may - // not have been initialized. - // - // This is also safe in the case where the object is NULL, - // since `objc_release` allows NULL pointers. - // - // Rest is same as `Id`'s `Drop`. - unsafe { objc_release_fast(self.ptr as *mut _) }; - } -} - -impl fmt::Pointer for Allocated { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&self.ptr, f) - } -} - -#[cfg(test)] -mod tests { - use core::panic::{RefUnwindSafe, UnwindSafe}; - - use static_assertions::assert_not_impl_any; - - use super::*; - use crate::runtime::NSObject; - - #[test] - fn auto_traits() { - assert_not_impl_any!(Allocated<()>: Send, Sync, UnwindSafe, RefUnwindSafe, Unpin); - } - - #[repr(C)] - struct MyObject<'a> { - inner: NSObject, - p: PhantomData<&'a str>, - } - - /// Test that `Allocated` is covariant over `T`. - #[allow(unused)] - fn assert_variance<'b>(obj: Allocated>) -> Allocated> { - obj - } -} diff --git a/crates/objc2/src/rc/allocated_partial_init.rs b/crates/objc2/src/rc/allocated_partial_init.rs new file mode 100644 index 000000000..a616c1864 --- /dev/null +++ b/crates/objc2/src/rc/allocated_partial_init.rs @@ -0,0 +1,353 @@ +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::runtime::{objc_release_fast, AnyObject}; +use crate::{DeclaredClass, Message}; + +/// An Objective-C object that has been allocated, but not initialized. +/// +/// Objective-C splits the allocation and initialization steps up into two, so +/// we need to track it in the type-system whether something has been +/// intialized or not. +/// +/// Note that allocation in Objective-C can fail, e.g. in Out Of Memory +/// situations! This is handled by `objc2` automatically, but if you really +/// need to, you can check for this explicitly by inspecting the pointer +/// returned from [`as_ptr`]. +/// +/// Note also that this represents that the _current_ class's instance +/// variables are not yet initialized; but subclass instance variables may +/// have been so. +/// +/// See [Apple's documentation on Object Allocation][object-allocation] for a +/// few more details. +/// +/// [`as_ptr`]: Self::as_ptr +/// [object-allocation]: https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ObjectAllocation/ObjectAllocation.html +/// +/// +/// # Memory layout +/// +/// This is guaranteed to have the same size and alignment as a pointer to the +/// object, `*const T`. The pointer may be NULL. +#[repr(transparent)] +#[derive(Debug)] +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 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. + /// + /// Covariance is correct, same as `Id`. + ptr: *const T, // Intentially not `NonNull`! + /// 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, +} + +// Explicitly don't implement `Deref`, `Message` nor `RefEncode`. + +impl Allocated { + /// # Safety + /// + /// 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 { + 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 `Allocated` is held. + /// + /// See [`Allocated::as_mut_ptr`] for the mutable equivalent. + /// + /// This is an associated method, and must be called as + /// `Allocated::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 `Allocated` is held. + /// + /// See [`Allocated::as_ptr`] for the immutable equivalent. + /// + /// This is an associated method, and must be called as + /// `Allocated::as_mut_ptr(obj)`. + /// + /// + /// # Note about mutable references + /// + /// In general, you're not allowed to create a mutable reference from + /// `Allocated`, unless you're defining the object and know that to be + /// safe. + /// + /// For example, `+[NSMutableString alloc]` is allowed to return a + /// non-unique object as an optimization, and then only figure out + /// afterwards whether it needs to allocate, or if it can store an + /// `NSString` internally. + /// + /// Similarly, while e.g. `+[NSData alloc]` may return a unique object, + /// calling `-[NSData init]` afterwards could return a shared empty + /// `NSData` instance. + #[inline] + #[allow(unknown_lints)] // New lint below + #[allow(clippy::needless_pass_by_ref_mut)] + pub fn as_mut_ptr(this: &mut Self) -> *mut T { + // Note: Not bound by `T: IsMutable`, since mutable pointers _can_ be + // safe for non-mutable classes, especially right when they're being + // allocated / initialized. + 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 + } + + /// 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. + /// + /// This works very similarly to [Swift's two-phase initialization + /// scheme][two-phase-init], see that for details. + /// + /// [`msg_send_id!`]: crate::msg_send_id + /// [two-phase-init]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization/#Two-Phase-Initialization + /// + /// + /// # 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 { + #[inline] + fn drop(&mut self) { + // SAFETY: Allocated objects can always safely be released, since + // destructors are written to take into account that the object may + // not have been initialized. + // + // This is also safe in the case where the object is NULL, + // since `objc_release` allows NULL pointers. + // + // Rest is same as `Id`'s `Drop`. + unsafe { objc_release_fast(self.ptr as *mut _) }; + } +} + +impl fmt::Pointer for Allocated { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.ptr, f) + } +} + +/// 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)`. + #[inline] + #[allow(unknown_lints)] // New lint below + #[allow(clippy::needless_pass_by_ref_mut)] + pub fn as_mut_ptr(this: &mut Self) -> *mut T { + 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}; + + use static_assertions::assert_not_impl_any; + + use super::*; + use crate::rc::__RcTestObject; + use crate::runtime::NSObject; + + #[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)] + struct MyObject<'a> { + inner: NSObject, + p: PhantomData<&'a str>, + } + + /// Test that `Allocated` is covariant over `T`. + #[allow(unused)] + 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 + } + + #[test] + #[cfg_attr( + debug_assertions, + should_panic = "tried to initialize instance variables on a NULL allocated object" + )] + fn test_set_ivars_null() { + // SAFETY: The pointer is NULL + let obj: Allocated<__RcTestObject> = unsafe { Allocated::new(ptr::null_mut()) }; + let _ = obj.set_ivars(()); + } +} 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..da9c80fb3 100644 --- a/crates/objc2/src/rc/mod.rs +++ b/crates/objc2/src/rc/mod.rs @@ -48,7 +48,7 @@ //! assert!(weak.load().is_none()); //! ``` -mod allocated; +mod allocated_partial_init; mod autorelease; mod id; mod id_forwarding_impls; @@ -56,7 +56,7 @@ mod id_traits; mod test_object; mod weak_id; -pub use self::allocated::Allocated; +pub use self::allocated_partial_init::{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..f86953842 100644 --- a/crates/objc2/src/rc/test_object.rs +++ b/crates/objc2/src/rc/test_object.rs @@ -1,10 +1,10 @@ use core::cell::RefCell; use core::ptr; -use super::{Allocated, Id}; +use super::{Allocated, DefaultId, 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,13 @@ 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); + } +} + +impl DefaultId for __RcTestObject { + fn default_id() -> Id { + Self::new() } } @@ -311,6 +320,8 @@ declare_class!( type Mutability = Immutable; const NAME: &'static str = "RcTestObjectSubclass"; } + + impl DeclaredClass for RcTestObjectSubclass {} ); #[cfg_attr(not(test), allow(unused))] @@ -357,7 +368,7 @@ mod tests { drop(res); $expected.release += 1; - $expected.dealloc += 1; + $expected.drop += 1; $expected.assert_current(); } } @@ -429,7 +440,7 @@ mod tests { drop(res); $expected.release += 1; - $expected.dealloc += 1; + $expected.drop += 1; $expected.assert_current(); // Errors @@ -449,7 +460,7 @@ mod tests { drop(res); $expected.release += 1; - $expected.dealloc += 1; + $expected.drop += 1; $expected.assert_current(); } } @@ -474,11 +485,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 +510,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 +533,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/mod.rs b/crates/objc2/src/runtime/mod.rs index b8cfd823b..d54ccaa2a 100644 --- a/crates/objc2/src/runtime/mod.rs +++ b/crates/objc2/src/runtime/mod.rs @@ -333,6 +333,126 @@ impl Ivar { let encoding = unsafe { CStr::from_ptr(ffi::ivar_getTypeEncoding(self.as_ptr())) }; str::from_utf8(encoding.to_bytes()).unwrap() } + + #[inline] + pub(crate) fn debug_assert_encoding(&self, _expected: &Encoding) { + #[cfg(debug_assertions)] + { + let encoding = self.type_encoding(); + assert!( + _expected.equivalent_to_str(encoding), + "wrong encoding. Tried to retrieve ivar with encoding {encoding}, but the encoding of the given type was {_expected}", + ); + } + } + + /// Returns a pointer to the instance variable / ivar on the given object. + /// + /// This is similar to [`UnsafeCell::get`], see that for more information + /// on what is and isn't safe to do. + /// + /// Usually you will have defined the instance variable yourself with + /// [`ClassBuilder::add_ivar`], the type of the ivar `T` must match the + /// type used in that. + /// + /// Library implementors are strongly encouraged to expose a safe + /// interface to the ivar. + /// + /// [`UnsafeCell::get`]: core::cell::UnsafeCell::get + /// [`ClassBuilder::add_ivar`]: crate::declare::ClassBuilder::add_ivar + /// + /// + /// # Panics + /// + /// Panics when `debug_assertions` are enabled if the type encoding of the + /// ivar differs from the type encoding of `T`. + /// + /// + /// # Safety + /// + /// The object must have the given instance variable on it, and it must be + /// of type `T`. Any invariants that the object have assumed about the + /// value of the instance variable must not be violated. + /// + /// Note that an object can have multiple instance variables with the same + /// name; you must ensure that when the instance variable was retrieved, + /// was retrieved from the class that it was defined on. In particular, + /// getting a class dynamically using e.g. [`AnyObject::class`], and using + /// an instance variable from that here is _not_ sound in general. + /// + /// No thread syncronization is done on accesses to the variable, so you + /// must ensure that any access to the returned pointer do not cause data + /// races, and that Rust's mutability rules are not otherwise violated. + #[inline] + pub unsafe fn load_ptr(&self, obj: &AnyObject) -> *mut T { + self.debug_assert_encoding(&T::ENCODING); + + let ptr = NonNull::from(obj); + // SAFETY: That the ivar is valid is ensured by the caller + let ptr = unsafe { AnyObject::ivar_at_offset::(ptr, self.offset()) }; + + // Safe as *mut T because `self` is `UnsafeCell` + ptr.as_ptr() + } + + /// Returns a reference to the instance variable with the given name. + /// + /// See [`Ivar::load_ptr`] for more information. + /// + /// + /// # Panics + /// + /// Panics when `debug_assertions` are enabled if the type encoding of the + /// ivar differs from the type encoding of `T`. + /// + /// + /// # Safety + /// + /// The object must have the given instance variable on it, and it must be + /// of type `T`. + /// + /// No thread syncronization is done, so you must ensure that no other + /// thread is concurrently mutating the variable. This requirement can be + /// considered upheld if all mutation happens through [`Ivar::load_mut`] + /// (since that takes the object mutably). + #[inline] + pub unsafe fn load<'obj, T: Encode>(&self, obj: &'obj AnyObject) -> &'obj T { + // SAFETY: That the ivar is valid as `&T` is ensured by the caller, + // and the reference is properly bound to the object. + unsafe { self.load_ptr::(obj).as_ref().unwrap_unchecked() } + } + + /// Returns a mutable reference to the ivar with the given name. + /// + /// See [`Ivar::load_ptr`] for more information. + /// + /// + /// # Panics + /// + /// Panics when `debug_assertions` are enabled if the type encoding of the + /// ivar differs from the type encoding of `T`. + /// + /// + /// # Safety + /// + /// The object must have an instance variable with the given name, and it + /// must be of type `T`. + /// + /// This access happens through `&mut`, which means we know it to be the + /// only reference, hence you do not need to do any work to ensure that + /// data races do not happen. + #[inline] + pub unsafe fn load_mut<'obj, T: Encode>(&self, obj: &'obj mut AnyObject) -> &'obj mut T { + self.debug_assert_encoding(&T::ENCODING); + + let ptr = NonNull::from(obj); + // SAFETY: That the ivar is valid is ensured by the caller + let mut ptr = unsafe { AnyObject::ivar_at_offset::(ptr, self.offset()) }; + + // SAFETY: That the ivar is valid as `&mut T` is ensured by taking an + // `&mut` object + unsafe { ptr.as_mut() } + } } standard_pointer_impls!(Ivar); @@ -711,6 +831,12 @@ impl AnyClass { /// Returns the ivar for a specified instance variable of self, or /// [`None`] if self has no ivar with the given name. + /// + /// If the instance variable was not found on the specified class, the + /// superclasses are searched. + /// + /// Attempting to access or modify instance variables of a class that you + /// do no control may invoke undefined behaviour. #[doc(alias = "class_getInstanceVariable")] pub fn instance_variable(&self, name: &str) -> Option<&Ivar> { let name = CString::new(name).unwrap(); @@ -999,20 +1125,6 @@ impl fmt::Display for AnyProtocol { } } -pub(crate) fn ivar_offset(cls: &AnyClass, name: &str, expected: &Encoding) -> isize { - match cls.instance_variable(name) { - Some(ivar) => { - let encoding = ivar.type_encoding(); - assert!( - expected.equivalent_to_str(encoding), - "wrong encoding. Tried to retrieve ivar with encoding {encoding}, but the encoding of the given type was {expected}", - ); - ivar.offset() - } - None => panic!("ivar {name} not found on class {cls}"), - } -} - /// An Objective-C object. /// /// This is slightly different from [`NSObject`] in that it may represent an @@ -1134,60 +1246,13 @@ impl AnyObject { ptr } - /// Returns a pointer to the instance variable / ivar with the given name. - /// - /// This is similar to [`UnsafeCell::get`], see that for more information - /// on what is and isn't safe to do. - /// - /// Usually you will have defined the instance variable yourself with - /// [`ClassBuilder::add_ivar`], the type of the ivar `T` must match the - /// type used in that. - /// - /// Attempting to access or modify private implementation details of a - /// class that you do no control using this is not supported, and may - /// invoke undefined behaviour. - /// - /// Library implementors are strongly encouraged to expose a safe - /// interface to the ivar. - /// - /// [`UnsafeCell::get`]: core::cell::UnsafeCell::get - /// [`ClassBuilder::add_ivar`]: crate::declare::ClassBuilder::add_ivar - /// - /// - /// # Panics - /// - /// May panic if the object has no ivar with the given name. May also - /// panic if the type encoding of the ivar differs from the type encoding - /// of `T`. - /// - /// This should purely seen as help while debugging and is not guaranteed - /// (e.g. it may be disabled when `debug_assertions` are off). - /// - /// - /// # Safety - /// - /// The object must have an instance variable with the given name, and it - /// must be of type `T`. Any invariants that the object have assumed about - /// the value of the instance variable must not be violated. - /// - /// No thread syncronization is done on accesses to the variable, so you - /// must ensure that any access to the returned pointer do not cause data - /// races, and that Rust's mutability rules are not otherwise violated. - pub unsafe fn ivar_ptr(&self, name: &str) -> *mut T { - let offset = ivar_offset(self.class(), name, &T::ENCODING); - - let ptr = NonNull::from(self); - // SAFETY: The offset is valid - let ptr = unsafe { Self::ivar_at_offset::(ptr, offset) }; - - // Safe as *mut T because `self` is `UnsafeCell` - ptr.as_ptr() + pub(crate) fn lookup_instance_variable_dynamically(&self, name: &str) -> &'static Ivar { + let cls = self.class(); + cls.instance_variable(name) + .unwrap_or_else(|| panic!("ivar {name} not found on class {cls}")) } - /// Returns a reference to the instance variable with the given name. - /// - /// See [`AnyObject::ivar_ptr`] for more information, including on when - /// this panics. + /// Use [`Ivar::load`] instead. /// /// /// # Safety @@ -1195,31 +1260,15 @@ impl AnyObject { /// The object must have an instance variable with the given name, and it /// must be of type `T`. /// - /// No thread syncronization is done, so you must ensure that no other - /// thread is concurrently mutating the variable. This requirement can be - /// considered upheld if all mutation happens through - /// [`AnyObject::ivar_mut`] (since that takes `&mut self`). - pub unsafe fn ivar(&self, name: &str) -> &T { - // SAFETY: Upheld by caller. - unsafe { self.ivar_ptr::(name).as_ref().unwrap_unchecked() } - } - - /// Use [`AnyObject::ivar`] instead. - /// - /// - /// # Safety - /// - /// See [`AnyObject::ivar`]. - #[deprecated = "Use `AnyObject::ivar` instead."] + /// See [`Ivar::load_ptr`] for details surrounding this. + #[deprecated = "this is difficult to use correctly, use `Ivar::load` instead."] pub unsafe fn get_ivar(&self, name: &str) -> &T { + let ivar = self.lookup_instance_variable_dynamically(name); // SAFETY: Upheld by caller - unsafe { self.ivar::(name) } + unsafe { ivar.load::(self) } } - /// Returns a mutable reference to the ivar with the given name. - /// - /// See [`AnyObject::ivar_ptr`] for more information, including on when - /// this panics. + /// Use [`Ivar::load_mut`] instead. /// /// /// # Safety @@ -1227,44 +1276,12 @@ impl AnyObject { /// The object must have an instance variable with the given name, and it /// must be of type `T`. /// - /// This access happens through `&mut self`, which means we know it to be - /// the only reference, hence you do not need to do any work to ensure - /// that data races do not happen. - pub unsafe fn ivar_mut(&mut self, name: &str) -> &mut T { - let offset = ivar_offset(self.class(), name, &T::ENCODING); - - let ptr = NonNull::from(self); - // SAFETY: The offset is valid - let mut ptr = unsafe { Self::ivar_at_offset::(ptr, offset) }; - - // SAFETY: - unsafe { ptr.as_mut() } - } - - /// Use [`AnyObject::ivar_mut`] instead. - /// - /// - /// # Safety - /// - /// Same as [`AnyObject::ivar_mut`]. - #[deprecated = "Use `AnyObject::ivar_mut` instead."] + /// See [`Ivar::load_ptr`] for details surrounding this. + #[deprecated = "this is difficult to use correctly, use `Ivar::load_mut` instead."] pub unsafe fn get_mut_ivar(&mut self, name: &str) -> &mut T { + let ivar = self.lookup_instance_variable_dynamically(name); // SAFETY: Upheld by caller - unsafe { self.ivar_mut::(name) } - } - - /// Sets the value of the ivar with the given name. - /// - /// This is a shorthand for [`AnyObject::ivar_mut`], see that for more - /// information. - /// - /// - /// # Safety - /// - /// Same as [`AnyObject::ivar_mut`]. - pub unsafe fn set_ivar(&mut self, name: &str, value: T) { - // SAFETY: Invariants upheld by caller - unsafe { *self.ivar_mut::(name) = value }; + unsafe { ivar.load_mut::(self) } } // objc_setAssociatedObject @@ -1476,27 +1493,32 @@ mod tests { #[test] fn test_object() { let mut obj = test_utils::custom_object(); - assert_eq!(obj.class(), test_utils::custom_class()); + let cls = test_utils::custom_class(); + assert_eq!(obj.class(), cls); - let result = unsafe { - obj.set_ivar::("_foo", 4); - *obj.ivar::("_foo") - }; + let ivar = cls.instance_variable("_foo").unwrap(); + + unsafe { *ivar.load_mut::(&mut obj) = 4 }; + let result = unsafe { *ivar.load::(&obj) }; assert_eq!(result, 4); } #[test] - #[should_panic = "ivar unknown not found on class CustomObject"] fn test_object_ivar_unknown() { - let obj = test_utils::custom_object(); - let _ = unsafe { *obj.ivar::("unknown") }; + let cls = test_utils::custom_class(); + assert_eq!(cls.instance_variable("unknown"), None); } #[test] - #[should_panic = "wrong encoding. Tried to retrieve ivar with encoding I, but the encoding of the given type was C"] + #[cfg_attr( + debug_assertions, + should_panic = "wrong encoding. Tried to retrieve ivar with encoding I, but the encoding of the given type was C" + )] fn test_object_ivar_wrong_type() { let obj = test_utils::custom_object(); - let _ = unsafe { *obj.ivar::("_foo") }; + let cls = test_utils::custom_class(); + let ivar = cls.instance_variable("_foo").unwrap(); + let _ = unsafe { *ivar.load::(&obj) }; } #[test] 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/test_utils.rs b/crates/objc2/src/test_utils.rs index 6b0e964b2..efd66a82d 100644 --- a/crates/objc2/src/test_utils.rs +++ b/crates/objc2/src/test_utils.rs @@ -93,15 +93,18 @@ pub(crate) fn custom_class() -> &'static AnyClass { } extern "C" fn custom_obj_set_foo(this: &mut AnyObject, _cmd: Sel, foo: u32) { - unsafe { this.set_ivar::("_foo", foo) } + let ivar = this.class().instance_variable("_foo").unwrap(); + unsafe { *ivar.load_mut::(this) = foo } } extern "C" fn custom_obj_get_foo(this: &AnyObject, _cmd: Sel) -> u32 { - unsafe { *this.ivar::("_foo") } + let ivar = this.class().instance_variable("_foo").unwrap(); + unsafe { *ivar.load::(this) } } extern "C" fn custom_obj_get_foo_reference(this: &AnyObject, _cmd: Sel) -> &u32 { - unsafe { this.ivar::("_foo") } + let ivar = this.class().instance_variable("_foo").unwrap(); + unsafe { ivar.load::(this) } } extern "C" fn custom_obj_get_struct(_this: &AnyObject, _cmd: Sel) -> CustomStruct { @@ -118,9 +121,8 @@ pub(crate) fn custom_class() -> &'static AnyClass { } extern "C" fn custom_obj_set_bar(this: &mut AnyObject, _cmd: Sel, bar: u32) { - unsafe { - this.set_ivar::("_foo", bar); - } + let ivar = this.class().instance_variable("_bar").unwrap(); + unsafe { *ivar.load_mut::(this) = bar } } extern "C" fn custom_obj_add_number_to_number( diff --git a/crates/objc2/src/top_level_traits.rs b/crates/objc2/src/top_level_traits.rs index b852fb182..d8ddecb3d 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,70 @@ 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] + #[track_caller] + fn ivars(&self) -> &Self::Ivars + where + Self: Sized, // Required because of MSRV + { + 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] + #[track_caller] + fn ivars_mut(&mut self) -> &mut Self::Ivars + where + Self: Sized, // Required because of MSRV + { + 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 329a500f6..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, Mutable}; +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,92 +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(); -} - -#[test] -#[should_panic = "instance variable \"ivar\" already exists on a superclass"] -fn test_subclass_duplicate_ivar() { - declare_class!( - struct Cls { - ivar_superclass: IvarEncode, - } - - mod ivars; - - unsafe impl ClassType for Cls { - type Super = NSObject; - type Mutability = Mutable; - const NAME: &'static str = "DeclareClassDuplicateIvarSuperclass"; - } - ); - - declare_class!( - struct SubCls { - ivar_subclass: IvarEncode, - } - - mod ivars_subclass; - - unsafe impl ClassType for SubCls { - type Super = Cls; - type Mutability = Mutable; - const NAME: &'static str = "DeclareClassDuplicateIvarSubclass"; - } - ); - - extern_methods!( - unsafe impl SubCls { - #[method_id(new)] - fn new() -> Id; - } - ); - - let _ = SubCls::class(); - - // The rest is to show what would go wrong if it didn't panic - - assert_eq!(Cls::class().instance_size(), 16); - assert_eq!(SubCls::class().instance_size(), 16); - - let mut obj = SubCls::new(); - - // Zero-initialized - assert_eq!(*obj.ivar_superclass, 0); - assert_eq!(*obj.ivar_subclass, 0); - - *obj.ivar_subclass = 2; - - assert_eq!(*obj.ivar_superclass, 2); - assert_eq!(*obj.ivar_subclass, 2); - - *obj.ivar_superclass = 3; - - assert_eq!(*obj.ivar_superclass, 3); - assert_eq!(*obj.ivar_subclass, 3); - - let ivar_dynamically = unsafe { obj.ivar::("ivar") }; - assert_eq!(*ivar_dynamically, 3); -} - declare_class!( #[derive(Debug)] struct OutParam; @@ -463,6 +385,8 @@ declare_class!( const NAME: &'static str = "OutParam"; } + impl DeclaredClass for OutParam {} + unsafe impl OutParam { #[method(unsupported1:)] fn _unsupported1(_param: &mut Id) {} @@ -551,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 c366c0484..2ea0c3e1a 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 @@ -1,590 +1,855 @@ .section __TEXT,__text,regular,pure_instructions .p2align 2 -SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0): +SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0): ret .p2align 2 -SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0): +SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0): + ret + + .p2align 2 +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 - ldr x8, [x0] - ldrb w9, [x8] - strb wzr, [x8] - cbz w9, LBB1_5 + mov x19, x1 + mov x20, x0 Lloh0: - adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 0)@PAGE Lloh1: - ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] + 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: - ldr x2, [x8] + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)@PAGE Lloh3: - adrp x0, l_anon.[ID].11@PAGE + 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: - add x0, x0, l_anon.[ID].11@PAGEOFF - mov w1, #15 - bl SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) - cbz x0, LBB1_6 - str x0, [sp, #8] + adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE Lloh5: - adrp x1, l_anon.[ID].12@PAGE + ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] Lloh6: - add x1, x1, l_anon.[ID].12@PAGEOFF + 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 + stp x22, x21, [sp, #16] + stp x20, x19, [sp, #32] + stp x29, x30, [sp, #48] + add x29, sp, #48 + ldr x8, [x0] + ldrb w9, [x8] + strb wzr, [x8] + cbz w9, LBB4_7 Lloh7: - adrp x5, l_anon.[ID].13@PAGE + adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE Lloh8: - add x5, x5, l_anon.[ID].13@PAGEOFF - add x0, sp, #8 - mov w2, #4 - mov w3, #1 - mov w4, #0 - bl SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) + ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] Lloh9: - adrp x1, l_anon.[ID].14@PAGE + ldr x2, [x8] Lloh10: - add x1, x1, l_anon.[ID].14@PAGEOFF + adrp x0, l_anon.[ID].17@PAGE Lloh11: - adrp x19, l_anon.[ID].2@PAGE + add x0, x0, l_anon.[ID].17@PAGEOFF + mov w1, #7 + bl SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) + cbz x0, LBB4_8 + str x0, [sp, #8] Lloh12: - add x19, x19, l_anon.[ID].2@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) + adrp x8, L_OBJC_SELECTOR_REFERENCES_03fd85b0462f54e9@PAGE Lloh13: - adrp x8, L_OBJC_SELECTOR_REFERENCES_dealloc@GOTPAGE + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_03fd85b0462f54e9@PAGEOFF] Lloh14: - ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_dealloc@GOTPAGEOFF] + adrp x19, l_anon.[ID].3@PAGE Lloh15: - ldr x1, [x8] + add x19, x19, l_anon.[ID].3@PAGEOFF Lloh16: - adrp x20, l_anon.[ID].1@PAGE + adrp x4, l_anon.[ID].10@PAGE Lloh17: - add x20, x20, l_anon.[ID].1@PAGEOFF + add x4, x4, l_anon.[ID].10@PAGEOFF Lloh18: - adrp x21, l_anon.[ID].3@PAGE + adrp x5, _get_class@PAGE Lloh19: - add x21, x21, l_anon.[ID].3@PAGEOFF -Lloh20: - adrp x5, SYM(::class::{closure#0}::__objc2_dealloc, 0)@PAGE -Lloh21: - add x5, x5, SYM(::class::{closure#0}::__objc2_dealloc, 0)@PAGEOFF + add x5, x5, _get_class@PAGEOFF add x0, sp, #8 - mov x2, x20 + mov x2, x19 mov x3, #0 - mov x4, x21 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) + bl SYM(objc2::declare::ClassBuilder::add_class_method_inner::GENERATED_ID, 0) +Lloh20: + adrp x8, L_OBJC_SELECTOR_REFERENCES_cf773331f3cfba54@PAGE +Lloh21: + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_cf773331f3cfba54@PAGEOFF] Lloh22: - adrp x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGE + adrp x4, l_anon.[ID].5@PAGE Lloh23: - ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGEOFF] + add x4, x4, l_anon.[ID].5@PAGEOFF Lloh24: - ldr x1, [x8] + adrp x5, _method_simple@PAGE Lloh25: - adrp x5, _init@PAGE -Lloh26: - add x5, x5, _init@PAGEOFF + add x5, x5, _method_simple@PAGEOFF add x0, sp, #8 - mov x2, x20 + mov x2, x19 mov x3, #0 - mov x4, x19 bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) +Lloh26: + adrp x8, L_OBJC_SELECTOR_REFERENCES_abdcbb85641cd990@PAGE Lloh27: - adrp x8, L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2@PAGE + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_abdcbb85641cd990@PAGEOFF] Lloh28: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2@PAGEOFF] + adrp x20, l_anon.[ID].6@PAGE Lloh29: - adrp x5, _class_method@PAGE + add x20, x20, l_anon.[ID].6@PAGEOFF Lloh30: - add x5, x5, _class_method@PAGEOFF + adrp x5, _method_bool@PAGE +Lloh31: + add x5, x5, _method_bool@PAGEOFF add x0, sp, #8 mov x2, x20 - mov x3, #0 - mov x4, x21 - bl SYM(objc2::declare::ClassBuilder::add_class_method_inner::GENERATED_ID, 0) -Lloh31: - adrp x8, L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc@PAGE + mov w3, #1 + mov x4, x20 + bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) Lloh32: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc@PAGEOFF] + adrp x8, L_OBJC_SELECTOR_REFERENCES_ef8de92414f2d9c8@PAGE Lloh33: - adrp x5, _method@PAGE + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_ef8de92414f2d9c8@PAGEOFF] Lloh34: - add x5, x5, _method@PAGEOFF + adrp x21, l_anon.[ID].4@PAGE +Lloh35: + add x21, x21, l_anon.[ID].4@PAGEOFF +Lloh36: + adrp x5, _method_id@PAGE +Lloh37: + add x5, x5, _method_id@PAGEOFF add x0, sp, #8 - mov x2, x20 + mov x2, x19 mov x3, #0 mov x4, x21 bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) -Lloh35: - adrp x8, L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5@PAGE -Lloh36: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5@PAGEOFF] -Lloh37: - adrp x21, l_anon.[ID].4@PAGE Lloh38: - add x21, x21, l_anon.[ID].4@PAGEOFF + adrp x8, L_OBJC_SELECTOR_REFERENCES_4a611090161f3fae@PAGE Lloh39: - adrp x5, _method_bool@PAGE + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_4a611090161f3fae@PAGEOFF] Lloh40: - add x5, x5, _method_bool@PAGEOFF + adrp x5, _method_id_with_param@PAGE +Lloh41: + add x5, x5, _method_id_with_param@PAGEOFF add x0, sp, #8 - mov x2, x21 + mov x2, x20 mov w3, #1 mov x4, x21 bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) -Lloh41: - adrp x8, L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce@PAGE Lloh42: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce@PAGEOFF] + adrp x0, l_anon.[ID].24@PAGE Lloh43: - adrp x5, _method_id@PAGE -Lloh44: - add x5, x5, _method_id@PAGEOFF + add x0, x0, l_anon.[ID].24@PAGEOFF + mov w1, #8 + bl SYM(objc2::runtime::AnyProtocol::get::GENERATED_ID, 0) + cbz x0, LBB4_4 + mov x1, x0 add x0, sp, #8 - mov x2, x20 - mov x3, #0 - mov x4, x19 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) + bl SYM(objc2::declare::ClassBuilder::add_protocol::GENERATED_ID, 0) +LBB4_4: +Lloh44: + adrp x0, l_anon.[ID].25@PAGE Lloh45: - adrp x8, L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f@PAGE -Lloh46: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f@PAGEOFF] -Lloh47: - adrp x5, _method_id_with_param@PAGE -Lloh48: - add x5, x5, _method_id_with_param@PAGEOFF - add x0, sp, #8 - mov x2, x21 - mov w3, #1 - mov x4, x19 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) -Lloh49: - adrp x0, l_anon.[ID].17@PAGE -Lloh50: - add x0, x0, l_anon.[ID].17@PAGEOFF + add x0, x0, l_anon.[ID].25@PAGEOFF mov w1, #9 bl SYM(objc2::runtime::AnyProtocol::get::GENERATED_ID, 0) - cbz x0, LBB1_4 + cbz x0, LBB4_6 mov x1, x0 add x0, sp, #8 bl SYM(objc2::declare::ClassBuilder::add_protocol::GENERATED_ID, 0) -LBB1_4: +LBB4_6: +Lloh46: + adrp x8, L_OBJC_SELECTOR_REFERENCES_2837f061c311eb14@PAGE +Lloh47: + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_2837f061c311eb14@PAGEOFF] +Lloh48: + adrp x2, l_anon.[ID].9@PAGE +Lloh49: + add x2, x2, l_anon.[ID].9@PAGEOFF +Lloh50: + adrp x4, l_anon.[ID].4@PAGE Lloh51: - adrp x8, L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166@PAGE + add x4, x4, l_anon.[ID].4@PAGEOFF Lloh52: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166@PAGEOFF] -Lloh53: - adrp x2, l_anon.[ID].7@PAGE -Lloh54: - add x2, x2, l_anon.[ID].7@PAGEOFF -Lloh55: - adrp x4, l_anon.[ID].2@PAGE -Lloh56: - add x4, x4, l_anon.[ID].2@PAGEOFF -Lloh57: adrp x5, _copyWithZone@PAGE -Lloh58: +Lloh53: add x5, x5, _copyWithZone@PAGEOFF add x0, sp, #8 mov w3, #1 bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) ldr x0, [sp, #8] bl SYM(objc2::declare::ClassBuilder::register::GENERATED_ID, 0) + adrp x8, __MergedGlobals@PAGE + str x0, [x8, __MergedGlobals@PAGEOFF] ldp x29, x30, [sp, #48] ldp x20, x19, [sp, #32] ldp x22, x21, [sp, #16] add sp, sp, #64 ret -LBB1_5: +LBB4_7: +Lloh54: + adrp x0, l_anon.[ID].19@PAGE +Lloh55: + add x0, x0, l_anon.[ID].19@PAGEOFF +Lloh56: + adrp x2, l_anon.[ID].21@PAGE +Lloh57: + add x2, x2, l_anon.[ID].21@PAGEOFF + mov w1, #43 + bl SYM(core::panicking::panic::GENERATED_ID, 0) +LBB4_8: +Lloh58: + adrp x0, l_anon.[ID].17@PAGE Lloh59: - adrp x0, l_anon.[ID].8@PAGE + add x0, x0, l_anon.[ID].17@PAGEOFF Lloh60: - add x0, x0, l_anon.[ID].8@PAGEOFF + adrp x2, l_anon.[ID].23@PAGE Lloh61: - adrp x2, l_anon.[ID].10@PAGE -Lloh62: - add x2, x2, l_anon.[ID].10@PAGEOFF - mov w1, #43 - bl SYM(core::panicking::panic::GENERATED_ID, 0) -LBB1_6: -Lloh63: - adrp x0, l_anon.[ID].11@PAGE -Lloh64: - add x0, x0, l_anon.[ID].11@PAGEOFF -Lloh65: - adrp x2, l_anon.[ID].16@PAGE -Lloh66: - add x2, x2, l_anon.[ID].16@PAGEOFF - mov w1, #15 + add x2, x2, l_anon.[ID].23@PAGEOFF + mov w1, #7 bl SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) - .loh AdrpAdd Lloh3, Lloh4 - .loh AdrpLdrGotLdr Lloh0, Lloh1, Lloh2 - .loh AdrpAdd Lloh49, Lloh50 - .loh AdrpAdd Lloh47, Lloh48 - .loh AdrpLdr Lloh45, Lloh46 - .loh AdrpAdd Lloh43, Lloh44 - .loh AdrpLdr Lloh41, Lloh42 - .loh AdrpAdd Lloh39, Lloh40 - .loh AdrpAdd Lloh37, Lloh38 - .loh AdrpLdr Lloh35, Lloh36 - .loh AdrpAdd Lloh33, Lloh34 - .loh AdrpLdr Lloh31, Lloh32 - .loh AdrpAdd Lloh29, Lloh30 - .loh AdrpLdr Lloh27, Lloh28 - .loh AdrpAdd Lloh25, Lloh26 - .loh AdrpLdrGotLdr Lloh22, Lloh23, Lloh24 - .loh AdrpAdd Lloh20, Lloh21 + .loh AdrpAdd Lloh10, Lloh11 + .loh AdrpLdrGotLdr Lloh7, Lloh8, Lloh9 + .loh AdrpAdd Lloh42, Lloh43 + .loh AdrpAdd Lloh40, Lloh41 + .loh AdrpLdr Lloh38, Lloh39 + .loh AdrpAdd Lloh36, Lloh37 + .loh AdrpAdd Lloh34, Lloh35 + .loh AdrpLdr Lloh32, Lloh33 + .loh AdrpAdd Lloh30, Lloh31 + .loh AdrpAdd Lloh28, Lloh29 + .loh AdrpLdr Lloh26, Lloh27 + .loh AdrpAdd Lloh24, Lloh25 + .loh AdrpAdd Lloh22, Lloh23 + .loh AdrpLdr Lloh20, Lloh21 .loh AdrpAdd Lloh18, Lloh19 .loh AdrpAdd Lloh16, Lloh17 - .loh AdrpLdrGotLdr Lloh13, Lloh14, Lloh15 - .loh AdrpAdd Lloh11, Lloh12 - .loh AdrpAdd Lloh9, Lloh10 - .loh AdrpAdd Lloh7, Lloh8 - .loh AdrpAdd Lloh5, Lloh6 - .loh AdrpAdd Lloh57, Lloh58 - .loh AdrpAdd Lloh55, Lloh56 - .loh AdrpAdd Lloh53, Lloh54 - .loh AdrpLdr Lloh51, Lloh52 - .loh AdrpAdd Lloh61, Lloh62 - .loh AdrpAdd Lloh59, Lloh60 - .loh AdrpAdd Lloh65, Lloh66 - .loh AdrpAdd Lloh63, Lloh64 + .loh AdrpAdd Lloh14, Lloh15 + .loh AdrpLdr Lloh12, Lloh13 + .loh AdrpAdd Lloh44, Lloh45 + .loh AdrpAdd Lloh52, Lloh53 + .loh AdrpAdd Lloh50, Lloh51 + .loh AdrpAdd Lloh48, Lloh49 + .loh AdrpLdr Lloh46, Lloh47 + .loh AdrpAdd Lloh56, Lloh57 + .loh AdrpAdd Lloh54, Lloh55 + .loh AdrpAdd Lloh60, Lloh61 + .loh AdrpAdd Lloh58, Lloh59 .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 +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] - 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 _get_class - .p2align 2 -_get_class: - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 + ldrb w9, [x8] + strb wzr, [x8] + cbz w9, LBB5_5 +Lloh62: + adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE +Lloh63: + ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] +Lloh64: + ldr x2, [x8] +Lloh65: + adrp x0, l_anon.[ID].18@PAGE +Lloh66: + add x0, x0, l_anon.[ID].18@PAGEOFF + mov w1, #9 + bl SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) + cbz x0, LBB5_6 + str x0, [sp, #24] Lloh67: - adrp x8, SYM(::class::REGISTER_CLASS, 0)@PAGE + adrp x8, L_OBJC_SELECTOR_REFERENCES_dealloc@GOTPAGE Lloh68: - add x8, x8, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF - ldapr x8, [x8] - cmp x8, #3 - b.ne LBB3_3 + ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_dealloc@GOTPAGEOFF] Lloh69: - adrp x0, l_anon.[ID].11@PAGE + ldr x1, [x8] Lloh70: - add x0, x0, l_anon.[ID].11@PAGEOFF - mov w1, #15 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbz x0, LBB3_4 -LBB3_2: - ldp x29, x30, [sp, #16] - add sp, sp, #32 - ret -LBB3_3: - mov w8, #1 - strb w8, [sp, #7] - add x8, sp, #7 - str x8, [sp, #8] + adrp x19, l_anon.[ID].3@PAGE Lloh71: - adrp x0, SYM(::class::REGISTER_CLASS, 0)@PAGE + add x19, x19, l_anon.[ID].3@PAGEOFF Lloh72: - add x0, x0, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF + adrp x4, l_anon.[ID].5@PAGE Lloh73: - adrp x3, l_anon.[ID].0@PAGE + add x4, x4, l_anon.[ID].5@PAGEOFF Lloh74: - add x3, x3, l_anon.[ID].0@PAGEOFF + adrp x5, SYM(objc2[CRATE_ID]::__macro_helpers::declared_ivars::dealloc::, 0)@PAGE Lloh75: - adrp x4, l_anon.[ID].16@PAGE + add x5, x5, SYM(objc2[CRATE_ID]::__macro_helpers::declared_ivars::dealloc::, 0)@PAGEOFF + add x0, sp, #24 + mov x2, x19 + mov x3, #0 + bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) + ldr x8, [sp, #24] + str x8, [sp, #8] Lloh76: - add x4, x4, l_anon.[ID].16@PAGEOFF - add x2, sp, #8 - mov w1, #0 - bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + adrp x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGE Lloh77: - adrp x0, l_anon.[ID].11@PAGE + ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGEOFF] Lloh78: - add x0, x0, l_anon.[ID].11@PAGEOFF - mov w1, #15 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbnz x0, LBB3_2 -LBB3_4: + ldr x1, [x8] Lloh79: - adrp x0, l_anon.[ID].8@PAGE + adrp x4, l_anon.[ID].4@PAGE Lloh80: - add x0, x0, l_anon.[ID].8@PAGEOFF + add x4, x4, l_anon.[ID].4@PAGEOFF Lloh81: - adrp x2, l_anon.[ID].16@PAGE + adrp x5, _init_drop_ivars@PAGE Lloh82: - add x2, x2, l_anon.[ID].16@PAGEOFF - mov w1, #43 - bl SYM(core::panicking::panic::GENERATED_ID, 0) - .loh AdrpAdd Lloh67, Lloh68 - .loh AdrpAdd Lloh69, Lloh70 - .loh AdrpAdd Lloh77, Lloh78 - .loh AdrpAdd Lloh75, Lloh76 - .loh AdrpAdd Lloh73, Lloh74 - .loh AdrpAdd Lloh71, Lloh72 - .loh AdrpAdd Lloh81, Lloh82 - .loh AdrpAdd Lloh79, Lloh80 - - .globl _get_obj - .p2align 2 -_get_obj: - stp x29, x30, [sp, #-16]! - mov x29, sp - bl _get_class + 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 Lloh83: - adrp x8, L_OBJC_SELECTOR_REFERENCES_new@GOTPAGE + adrp x9, l_anon.[ID].15@PAGE Lloh84: - ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_new@GOTPAGEOFF] + add x9, x9, l_anon.[ID].15@PAGEOFF + stp x8, x9, [sp, #32] + mov w8, #27 + strb w8, [sp, #24] Lloh85: - ldr x1, [x8] - ldp x29, x30, [sp], #16 - b _objc_msgSend - .loh AdrpLdrGotLdr Lloh83, Lloh84, Lloh85 - - .globl _access_ivars - .p2align 2 -_access_ivars: - stp x22, x21, [sp, #-48]! - stp x20, x19, [sp, #16] - stp x29, x30, [sp, #32] - add x29, sp, #32 - bl _get_obj - mov x19, x0 - bl _object_getClass + adrp x20, l_anon.[ID].11@PAGE Lloh86: - adrp x1, l_anon.[ID].12@PAGE + 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) Lloh87: - add x1, x1, l_anon.[ID].12@PAGEOFF + adrp x1, l_anon.[ID].12@PAGE Lloh88: - adrp x3, l_anon.[ID].13@PAGE + add x1, x1, l_anon.[ID].12@PAGEOFF Lloh89: - add x3, x3, l_anon.[ID].13@PAGEOFF - mov w2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldrb w20, [x19, x0] - mov x0, x19 - bl _object_getClass + adrp x5, l_anon.[ID].13@PAGE Lloh90: - adrp x1, l_anon.[ID].14@PAGE + 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 Lloh91: - add x1, x1, l_anon.[ID].14@PAGEOFF + adrp x1, l_anon.[ID].12@PAGE Lloh92: - adrp x3, l_anon.[ID].2@PAGE -Lloh93: - add x3, x3, l_anon.[ID].2@PAGEOFF - mov w2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldr x21, [x19, x0] + add x1, x1, l_anon.[ID].12@PAGEOFF mov x0, x19 - bl _objc_release - mov x0, x20 - mov x1, x21 - ldp x29, x30, [sp, #32] - ldp x20, x19, [sp, #16] - ldp x22, x21, [sp], #48 - ret - .loh AdrpAdd Lloh92, Lloh93 - .loh AdrpAdd Lloh90, Lloh91 - .loh AdrpAdd Lloh88, Lloh89 - .loh AdrpAdd Lloh86, Lloh87 - - .globl SYM(::class, 0) - .p2align 2 -SYM(::class, 0): - sub sp, sp, #32 - stp x29, x30, [sp, #16] - add x29, sp, #16 + mov w2, #9 + bl SYM(objc2::runtime::AnyClass::instance_variable::GENERATED_ID, 0) + cbz x0, LBB5_8 + bl _ivar_getOffset +Lloh93: + adrp x8, __MergedGlobals@PAGE+32 + str x19, [x8, __MergedGlobals@PAGEOFF+32] Lloh94: - adrp x8, SYM(::class::REGISTER_CLASS, 0)@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] Lloh95: - add x8, x8, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF - ldapr x8, [x8] - cmp x8, #3 - b.ne LBB6_3 + 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: Lloh96: - adrp x0, l_anon.[ID].11@PAGE + adrp x0, l_anon.[ID].19@PAGE Lloh97: - add x0, x0, l_anon.[ID].11@PAGEOFF - mov w1, #15 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbz x0, LBB6_4 -LBB6_2: - ldp x29, x30, [sp, #16] - add sp, sp, #32 - ret -LBB6_3: - mov w8, #1 - strb w8, [sp, #7] - add x8, sp, #7 - str x8, [sp, #8] + add x0, x0, l_anon.[ID].19@PAGEOFF Lloh98: - adrp x0, SYM(::class::REGISTER_CLASS, 0)@PAGE + adrp x2, l_anon.[ID].21@PAGE Lloh99: - add x0, x0, SYM(::class::REGISTER_CLASS, 0)@PAGEOFF + add x2, x2, l_anon.[ID].21@PAGEOFF + mov w1, #43 + bl SYM(core::panicking::panic::GENERATED_ID, 0) +LBB5_6: Lloh100: - adrp x3, l_anon.[ID].0@PAGE + adrp x0, l_anon.[ID].18@PAGE Lloh101: - add x3, x3, l_anon.[ID].0@PAGEOFF + add x0, x0, l_anon.[ID].18@PAGEOFF Lloh102: - adrp x4, l_anon.[ID].16@PAGE + adrp x2, l_anon.[ID].27@PAGE Lloh103: - add x4, x4, l_anon.[ID].16@PAGEOFF - add x2, sp, #8 - mov w1, #0 - bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + add x2, x2, l_anon.[ID].27@PAGEOFF + mov w1, #9 + bl SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) +LBB5_7: + bl SYM(objc2::__macro_helpers::declared_ivars::register_with_ivars::get_ivar_failed::GENERATED_ID, 0) +LBB5_8: + bl SYM(objc2::__macro_helpers::declared_ivars::register_with_ivars::get_drop_flag_failed::GENERATED_ID, 0) + .loh AdrpAdd Lloh65, Lloh66 + .loh AdrpLdrGotLdr Lloh62, Lloh63, Lloh64 + .loh AdrpAdd Lloh89, Lloh90 + .loh AdrpAdd Lloh87, Lloh88 + .loh AdrpAdd Lloh85, Lloh86 + .loh AdrpAdd Lloh83, Lloh84 + .loh AdrpAdd Lloh81, Lloh82 + .loh AdrpAdd Lloh79, Lloh80 + .loh AdrpLdrGotLdr Lloh76, Lloh77, Lloh78 + .loh AdrpAdd Lloh74, Lloh75 + .loh AdrpAdd Lloh72, Lloh73 + .loh AdrpAdd Lloh70, Lloh71 + .loh AdrpLdrGotLdr Lloh67, Lloh68, Lloh69 + .loh AdrpAdd Lloh91, Lloh92 + .loh AdrpAdrp Lloh94, Lloh95 + .loh AdrpAdrp Lloh93, Lloh94 + .loh AdrpAdd Lloh98, Lloh99 + .loh AdrpAdd Lloh96, Lloh97 + .loh AdrpAdd Lloh102, Lloh103 + .loh AdrpAdd Lloh100, Lloh101 + + .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 Lloh104: - adrp x0, l_anon.[ID].11@PAGE + adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE Lloh105: - add x0, x0, l_anon.[ID].11@PAGEOFF - mov w1, #15 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cbnz x0, LBB6_2 -LBB6_4: + ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] Lloh106: - adrp x0, l_anon.[ID].8@PAGE + ldr x2, [x8] Lloh107: - add x0, x0, l_anon.[ID].8@PAGEOFF + adrp x0, l_anon.[ID].16@PAGE Lloh108: - adrp x2, l_anon.[ID].16@PAGE + add x0, x0, l_anon.[ID].16@PAGEOFF + mov w1, #15 + bl SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) + cbz x0, LBB6_5 + str x0, [sp, #8] Lloh109: - add x2, x2, l_anon.[ID].16@PAGEOFF - mov w1, #43 - bl SYM(core::panicking::panic::GENERATED_ID, 0) - .loh AdrpAdd Lloh94, Lloh95 - .loh AdrpAdd Lloh96, Lloh97 - .loh AdrpAdd Lloh104, Lloh105 - .loh AdrpAdd Lloh102, Lloh103 - .loh AdrpAdd Lloh100, Lloh101 - .loh AdrpAdd Lloh98, Lloh99 - .loh AdrpAdd Lloh108, Lloh109 - .loh AdrpAdd Lloh106, Lloh107 - - .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 - mov x20, x0 - bl _object_getClass + adrp x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGE Lloh110: - adrp x1, l_anon.[ID].14@PAGE + ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGEOFF] Lloh111: - add x1, x1, l_anon.[ID].14@PAGEOFF + ldr x1, [x8] Lloh112: - adrp x3, l_anon.[ID].2@PAGE + adrp x2, l_anon.[ID].3@PAGE Lloh113: - add x3, x3, l_anon.[ID].2@PAGEOFF - mov w2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldr x0, [x20, x0] - cbz x0, LBB7_2 - bl _objc_release -LBB7_2: + add x2, x2, l_anon.[ID].3@PAGEOFF Lloh114: - adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE + adrp x4, l_anon.[ID].4@PAGE Lloh115: - ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] + add x4, x4, l_anon.[ID].4@PAGEOFF Lloh116: - 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 Lloh112, Lloh113 - .loh AdrpAdd Lloh110, Lloh111 - .loh AdrpLdrGotLdr Lloh114, Lloh115, Lloh116 - - .globl _init - .p2align 2 -_init: - sub sp, sp, #48 - stp x20, x19, [sp, #16] - stp x29, x30, [sp, #32] - add x29, sp, #32 + adrp x5, _init_forgetable_ivars@PAGE Lloh117: - adrp x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGE + 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 Lloh118: - ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_init@GOTPAGEOFF] + adrp x9, l_anon.[ID].14@PAGE Lloh119: - ldr x1, [x8] + add x9, x9, l_anon.[ID].14@PAGEOFF + stp x8, x9, [sp, #32] + mov w8, #27 + strb w8, [sp, #24] Lloh120: - adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE + adrp x20, l_anon.[ID].11@PAGE Lloh121: - ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] -Lloh122: - ldr x8, [x8] - stp x0, x8, [sp] - mov x0, sp - bl _objc_msgSendSuper + 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 - cbz x0, LBB8_2 - mov x0, x19 - bl _object_getClass + mov x1, x20 + mov w2, #5 + bl SYM(objc2::runtime::AnyClass::instance_variable::GENERATED_ID, 0) + cbz x0, LBB6_6 + bl _ivar_getOffset +Lloh122: + adrp x8, __MergedGlobals@PAGE+16 + str x19, [x8, __MergedGlobals@PAGEOFF+16] Lloh123: - adrp x1, l_anon.[ID].12@PAGE + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 1)@PAGE + str x0, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 1)@PAGEOFF] + ldp x29, x30, [sp, #80] + ldp x20, x19, [sp, #64] + add sp, sp, #96 + ret +LBB6_4: Lloh124: - add x1, x1, l_anon.[ID].12@PAGEOFF + adrp x0, l_anon.[ID].19@PAGE Lloh125: - adrp x3, l_anon.[ID].13@PAGE + add x0, x0, l_anon.[ID].19@PAGEOFF Lloh126: - add x3, x3, l_anon.[ID].13@PAGEOFF - mov w2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov w8, #42 - strb w8, [x19, x0] - mov x0, x19 - bl _object_getClass + adrp x2, l_anon.[ID].21@PAGE Lloh127: - adrp x1, l_anon.[ID].14@PAGE + add x2, x2, l_anon.[ID].21@PAGEOFF + mov w1, #43 + bl SYM(core::panicking::panic::GENERATED_ID, 0) +LBB6_5: Lloh128: - add x1, x1, l_anon.[ID].14@PAGEOFF + adrp x0, l_anon.[ID].16@PAGE Lloh129: - adrp x3, l_anon.[ID].2@PAGE + add x0, x0, l_anon.[ID].16@PAGEOFF Lloh130: + adrp x2, l_anon.[ID].26@PAGE +Lloh131: + add x2, x2, l_anon.[ID].26@PAGEOFF + mov w1, #15 + bl SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) +LBB6_6: + bl SYM(objc2::__macro_helpers::declared_ivars::register_with_ivars::get_ivar_failed::GENERATED_ID, 0) + .loh AdrpAdd Lloh107, Lloh108 + .loh AdrpLdrGotLdr Lloh104, Lloh105, Lloh106 + .loh AdrpAdd Lloh120, Lloh121 + .loh AdrpAdd Lloh118, Lloh119 + .loh AdrpAdd Lloh116, Lloh117 + .loh AdrpAdd Lloh114, Lloh115 + .loh AdrpAdd Lloh112, Lloh113 + .loh AdrpLdrGotLdr Lloh109, Lloh110, Lloh111 + .loh AdrpAdrp Lloh122, Lloh123 + .loh AdrpAdd Lloh126, Lloh127 + .loh AdrpAdd Lloh124, Lloh125 + .loh AdrpAdd Lloh130, Lloh131 + .loh AdrpAdd Lloh128, Lloh129 + + .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: +Lloh132: + adrp x8, __MergedGlobals@PAGE+24 +Lloh133: + add x8, x8, __MergedGlobals@PAGEOFF+24 + ldapr x8, [x8] + cmp x8, #3 + b.ne LBB10_2 +Lloh134: + adrp x8, __MergedGlobals@PAGE+16 +Lloh135: + 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] +Lloh136: + adrp x0, __MergedGlobals@PAGE+24 +Lloh137: + add x0, x0, __MergedGlobals@PAGEOFF+24 +Lloh138: + adrp x3, l_anon.[ID].2@PAGE +Lloh139: add x3, x3, l_anon.[ID].2@PAGEOFF - mov w2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - str xzr, [x19, x0] -LBB8_2: - mov x0, x19 - ldp x29, x30, [sp, #32] - ldp x20, x19, [sp, #16] - add sp, sp, #48 +Lloh140: + adrp x4, l_anon.[ID].26@PAGE +Lloh141: + add x4, x4, l_anon.[ID].26@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 +Lloh142: + adrp x8, __MergedGlobals@PAGE+16 +Lloh143: + ldr x0, [x8, __MergedGlobals@PAGEOFF+16] + ret + .loh AdrpAdd Lloh132, Lloh133 + .loh AdrpLdr Lloh134, Lloh135 + .loh AdrpLdr Lloh142, Lloh143 + .loh AdrpAdd Lloh140, Lloh141 + .loh AdrpAdd Lloh138, Lloh139 + .loh AdrpAdd Lloh136, Lloh137 + + .globl _access_forgetable_ivars + .p2align 2 +_access_forgetable_ivars: +Lloh144: + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 1)@PAGE +Lloh145: + ldr x8, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 1)@PAGEOFF] + add x8, x0, x8 + ldr w1, [x8] + ldrb w0, [x8, #4] + ret + .loh AdrpLdr Lloh144, Lloh145 + + .globl SYM(::drop, 0) + .p2align 2 +SYM(::drop, 0): + ; InlineAsm Start + ; InlineAsm End + ret + + .globl _access_drop_ivars_class + .p2align 2 +_access_drop_ivars_class: +Lloh146: + adrp x8, __MergedGlobals@PAGE+40 +Lloh147: + add x8, x8, __MergedGlobals@PAGEOFF+40 + ldapr x8, [x8] + cmp x8, #3 + b.ne LBB13_2 +Lloh148: + adrp x8, __MergedGlobals@PAGE+32 +Lloh149: + ldr x0, [x8, __MergedGlobals@PAGEOFF+32] + ret +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] +Lloh150: + adrp x0, __MergedGlobals@PAGE+40 +Lloh151: + add x0, x0, __MergedGlobals@PAGEOFF+40 +Lloh152: + adrp x3, l_anon.[ID].1@PAGE +Lloh153: + add x3, x3, l_anon.[ID].1@PAGEOFF +Lloh154: + adrp x4, l_anon.[ID].27@PAGE +Lloh155: + 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) + ldp x29, x30, [sp, #16] + add sp, sp, #32 +Lloh156: + adrp x8, __MergedGlobals@PAGE+32 +Lloh157: + ldr x0, [x8, __MergedGlobals@PAGEOFF+32] + ret + .loh AdrpAdd Lloh146, Lloh147 + .loh AdrpLdr Lloh148, Lloh149 + .loh AdrpLdr Lloh156, Lloh157 + .loh AdrpAdd Lloh154, Lloh155 + .loh AdrpAdd Lloh152, Lloh153 + .loh AdrpAdd Lloh150, Lloh151 + + .globl _access_drop_ivars + .p2align 2 +_access_drop_ivars: +Lloh158: + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)@PAGE +Lloh159: + 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 Lloh158, Lloh159 + + .globl SYM(::class, 0) + .p2align 2 +SYM(::class, 0): +Lloh160: + adrp x8, __MergedGlobals@PAGE+8 +Lloh161: + add x8, x8, __MergedGlobals@PAGEOFF+8 + ldapr x8, [x8] + cmp x8, #3 + b.ne LBB15_2 +Lloh162: + adrp x8, __MergedGlobals@PAGE +Lloh163: + ldr x0, [x8, __MergedGlobals@PAGEOFF] + ret +LBB15_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] +Lloh164: + adrp x0, __MergedGlobals@PAGE+8 +Lloh165: + add x0, x0, __MergedGlobals@PAGEOFF+8 +Lloh166: + adrp x3, l_anon.[ID].0@PAGE +Lloh167: + add x3, x3, l_anon.[ID].0@PAGEOFF +Lloh168: + adrp x4, l_anon.[ID].23@PAGE +Lloh169: + add x4, x4, l_anon.[ID].23@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 +Lloh170: + adrp x8, __MergedGlobals@PAGE +Lloh171: + ldr x0, [x8, __MergedGlobals@PAGEOFF] ret - .loh AdrpLdrGotLdr Lloh120, Lloh121, Lloh122 - .loh AdrpLdrGotLdr Lloh117, Lloh118, Lloh119 - .loh AdrpAdd Lloh129, Lloh130 - .loh AdrpAdd Lloh127, Lloh128 - .loh AdrpAdd Lloh125, Lloh126 - .loh AdrpAdd Lloh123, Lloh124 - - .globl _class_method + .loh AdrpAdd Lloh160, Lloh161 + .loh AdrpLdr Lloh162, Lloh163 + .loh AdrpLdr Lloh170, Lloh171 + .loh AdrpAdd Lloh168, Lloh169 + .loh AdrpAdd Lloh166, Lloh167 + .loh AdrpAdd Lloh164, Lloh165 + + .globl _get_class .p2align 2 -_class_method: +_get_class: +Lloh172: + adrp x8, __MergedGlobals@PAGE+8 +Lloh173: + add x8, x8, __MergedGlobals@PAGEOFF+8 + ldapr x8, [x8] + cmp x8, #3 + b.ne LBB16_2 +Lloh174: + adrp x8, __MergedGlobals@PAGE +Lloh175: + 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] +Lloh176: + adrp x0, __MergedGlobals@PAGE+8 +Lloh177: + add x0, x0, __MergedGlobals@PAGEOFF+8 +Lloh178: + adrp x3, l_anon.[ID].0@PAGE +Lloh179: + add x3, x3, l_anon.[ID].0@PAGEOFF +Lloh180: + adrp x4, l_anon.[ID].23@PAGE +Lloh181: + add x4, x4, l_anon.[ID].23@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 +Lloh182: + adrp x8, __MergedGlobals@PAGE +Lloh183: + ldr x0, [x8, __MergedGlobals@PAGEOFF] ret + .loh AdrpAdd Lloh172, Lloh173 + .loh AdrpLdr Lloh174, Lloh175 + .loh AdrpLdr Lloh182, Lloh183 + .loh AdrpAdd Lloh180, Lloh181 + .loh AdrpAdd Lloh178, Lloh179 + .loh AdrpAdd Lloh176, Lloh177 - .globl _method + .globl _method_simple .p2align 2 -_method: +_method_simple: ret .globl _method_bool @@ -596,350 +861,627 @@ _method_bool: .globl _method_id .p2align 2 _method_id: - stp x20, x19, [sp, #-32]! + sub sp, sp, #32 stp x29, x30, [sp, #16] add x29, sp, #16 - mov x19, x0 - bl _object_getClass -Lloh131: - adrp x1, l_anon.[ID].14@PAGE -Lloh132: - add x1, x1, l_anon.[ID].14@PAGEOFF -Lloh133: - adrp x3, l_anon.[ID].2@PAGE -Lloh134: - add x3, x3, l_anon.[ID].2@PAGEOFF - mov w2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldr x0, [x19, x0] - cbz x0, LBB12_2 - bl _objc_retain -LBB12_2: +Lloh184: + adrp x8, __MergedGlobals@PAGE+8 +Lloh185: + add x8, x8, __MergedGlobals@PAGEOFF+8 + ldapr x8, [x8] + cmp x8, #3 + b.ne LBB19_2 +LBB19_1: +Lloh186: + adrp x8, __MergedGlobals@PAGE +Lloh187: + ldr x0, [x8, __MergedGlobals@PAGEOFF] +Lloh188: + adrp x8, L_OBJC_SELECTOR_REFERENCES_new@GOTPAGE +Lloh189: + ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_new@GOTPAGEOFF] +Lloh190: + ldr x1, [x8] + bl _objc_msgSend + bl _objc_autoreleaseReturnValue ldp x29, x30, [sp, #16] - ldp x20, x19, [sp], #32 - b _objc_autoreleaseReturnValue - .loh AdrpAdd Lloh133, Lloh134 - .loh AdrpAdd Lloh131, Lloh132 + add sp, sp, #32 + ret +LBB19_2: + mov w8, #1 + strb w8, [sp, #7] + add x8, sp, #7 + str x8, [sp, #8] +Lloh191: + adrp x0, __MergedGlobals@PAGE+8 +Lloh192: + add x0, x0, __MergedGlobals@PAGEOFF+8 +Lloh193: + adrp x3, l_anon.[ID].0@PAGE +Lloh194: + add x3, x3, l_anon.[ID].0@PAGEOFF +Lloh195: + adrp x4, l_anon.[ID].23@PAGE +Lloh196: + add x4, x4, l_anon.[ID].23@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 Lloh184, Lloh185 + .loh AdrpLdrGotLdr Lloh188, Lloh189, Lloh190 + .loh AdrpAdrp Lloh186, Lloh188 + .loh AdrpLdr Lloh186, Lloh187 + .loh AdrpAdd Lloh195, Lloh196 + .loh AdrpAdd Lloh193, Lloh194 + .loh AdrpAdd Lloh191, Lloh192 .globl _method_id_with_param .p2align 2 _method_id_with_param: - stp x22, x21, [sp, #-48]! - stp x20, x19, [sp, #16] - stp x29, x30, [sp, #32] - add x29, sp, #32 - mov x21, x2 - mov x20, x0 + stp x20, x19, [sp, #-32]! + stp x29, x30, [sp, #16] + add x29, sp, #16 + mov x20, x2 bl SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) mov x19, x0 - cbz w21, LBB13_5 - mov x0, x20 - bl _object_getClass -Lloh135: - adrp x1, l_anon.[ID].14@PAGE -Lloh136: - add x1, x1, l_anon.[ID].14@PAGEOFF -Lloh137: - adrp x3, l_anon.[ID].2@PAGE -Lloh138: - add x3, x3, l_anon.[ID].2@PAGEOFF - mov w2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldr x0, [x20, x0] - cbz x0, LBB13_3 - bl _objc_retain + tbz w20, #0, LBB20_2 + bl SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) mov x20, x0 - b LBB13_4 -LBB13_3: - mov x20, #0 -LBB13_4: mov x0, x19 bl _objc_release mov x19, x20 -LBB13_5: +LBB20_2: mov x0, x19 - ldp x29, x30, [sp, #32] - ldp x20, x19, [sp, #16] - ldp x22, x21, [sp], #48 + ldp x29, x30, [sp, #16] + ldp x20, x19, [sp], #32 b _objc_autoreleaseReturnValue - .loh AdrpAdd Lloh137, Lloh138 - .loh AdrpAdd Lloh135, Lloh136 .globl _copyWithZone .p2align 2 _copyWithZone: - stp x24, x23, [sp, #-64]! + sub sp, sp, #32 + stp x29, x30, [sp, #16] + add x29, sp, #16 +Lloh197: + adrp x8, __MergedGlobals@PAGE+8 +Lloh198: + add x8, x8, __MergedGlobals@PAGEOFF+8 + ldapr x8, [x8] + cmp x8, #3 + b.ne LBB21_2 +LBB21_1: +Lloh199: + adrp x8, __MergedGlobals@PAGE +Lloh200: + ldr x0, [x8, __MergedGlobals@PAGEOFF] +Lloh201: + adrp x8, L_OBJC_SELECTOR_REFERENCES_new@GOTPAGE +Lloh202: + ldr x8, [x8, L_OBJC_SELECTOR_REFERENCES_new@GOTPAGEOFF] +Lloh203: + ldr x1, [x8] + bl _objc_msgSend + ldp x29, x30, [sp, #16] + add sp, sp, #32 + ret +LBB21_2: + mov w8, #1 + strb w8, [sp, #7] + add x8, sp, #7 + str x8, [sp, #8] +Lloh204: + adrp x0, __MergedGlobals@PAGE+8 +Lloh205: + add x0, x0, __MergedGlobals@PAGEOFF+8 +Lloh206: + adrp x3, l_anon.[ID].0@PAGE +Lloh207: + add x3, x3, l_anon.[ID].0@PAGEOFF +Lloh208: + adrp x4, l_anon.[ID].23@PAGE +Lloh209: + add x4, x4, l_anon.[ID].23@PAGEOFF + add x2, sp, #8 + mov w1, #0 + bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + b LBB21_1 + .loh AdrpAdd Lloh197, Lloh198 + .loh AdrpLdrGotLdr Lloh201, Lloh202, Lloh203 + .loh AdrpAdrp Lloh199, Lloh201 + .loh AdrpLdr Lloh199, Lloh200 + .loh AdrpAdd Lloh208, Lloh209 + .loh AdrpAdd Lloh206, Lloh207 + .loh AdrpAdd Lloh204, Lloh205 + + .globl SYM(::class, 0) + .p2align 2 +SYM(::class, 0): +Lloh210: + adrp x8, __MergedGlobals@PAGE+24 +Lloh211: + add x8, x8, __MergedGlobals@PAGEOFF+24 + ldapr x8, [x8] + cmp x8, #3 + b.ne LBB22_2 +Lloh212: + adrp x8, __MergedGlobals@PAGE+16 +Lloh213: + ldr x0, [x8, __MergedGlobals@PAGEOFF+16] + ret +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] +Lloh214: + adrp x0, __MergedGlobals@PAGE+24 +Lloh215: + add x0, x0, __MergedGlobals@PAGEOFF+24 +Lloh216: + adrp x3, l_anon.[ID].2@PAGE +Lloh217: + add x3, x3, l_anon.[ID].2@PAGEOFF +Lloh218: + adrp x4, l_anon.[ID].26@PAGE +Lloh219: + add x4, x4, l_anon.[ID].26@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 +Lloh220: + adrp x8, __MergedGlobals@PAGE+16 +Lloh221: + ldr x0, [x8, __MergedGlobals@PAGEOFF+16] + ret + .loh AdrpAdd Lloh210, Lloh211 + .loh AdrpLdr Lloh212, Lloh213 + .loh AdrpLdr Lloh220, Lloh221 + .loh AdrpAdd Lloh218, Lloh219 + .loh AdrpAdd Lloh216, Lloh217 + .loh AdrpAdd Lloh214, Lloh215 + + .globl _init_forgetable_ivars + .p2align 2 +_init_forgetable_ivars: + cbz x0, LBB23_2 +Lloh222: + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 1)@PAGE +Lloh223: + ldr x8, [x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 1)@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 +Lloh224: + adrp x8, L_OBJC_SELECTOR_REFERENCES_af8966656b8b2b6c@PAGE +Lloh225: + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_af8966656b8b2b6c@PAGEOFF] +Lloh226: + adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE +Lloh227: + ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] +Lloh228: + ldr x8, [x8] + stp x0, x8, [sp] + mov x0, sp + bl _objc_msgSendSuper + ldp x29, x30, [sp, #16] + add sp, sp, #32 + ret + .loh AdrpLdr Lloh222, Lloh223 + .loh AdrpLdrGotLdr Lloh226, Lloh227, Lloh228 + .loh AdrpAdrp Lloh224, Lloh226 + .loh AdrpLdr Lloh224, Lloh225 + + .globl SYM(::class, 0) + .p2align 2 +SYM(::class, 0): +Lloh229: + adrp x8, __MergedGlobals@PAGE+40 +Lloh230: + add x8, x8, __MergedGlobals@PAGEOFF+40 + ldapr x8, [x8] + cmp x8, #3 + b.ne LBB24_2 +Lloh231: + adrp x8, __MergedGlobals@PAGE+32 +Lloh232: + ldr x0, [x8, __MergedGlobals@PAGEOFF+32] + ret +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] +Lloh233: + adrp x0, __MergedGlobals@PAGE+40 +Lloh234: + add x0, x0, __MergedGlobals@PAGEOFF+40 +Lloh235: + adrp x3, l_anon.[ID].1@PAGE +Lloh236: + add x3, x3, l_anon.[ID].1@PAGEOFF +Lloh237: + adrp x4, l_anon.[ID].27@PAGE +Lloh238: + 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) + ldp x29, x30, [sp, #16] + add sp, sp, #32 +Lloh239: + adrp x8, __MergedGlobals@PAGE+32 +Lloh240: + ldr x0, [x8, __MergedGlobals@PAGEOFF+32] + ret + .loh AdrpAdd Lloh229, Lloh230 + .loh AdrpLdr Lloh231, Lloh232 + .loh AdrpLdr Lloh239, Lloh240 + .loh AdrpAdd Lloh237, Lloh238 + .loh AdrpAdd Lloh235, Lloh236 + .loh AdrpAdd Lloh233, Lloh234 + + .globl _init_drop_ivars + .p2align 2 +_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 x20, x0 - bl _get_obj mov x19, x0 - cbz x0, LBB14_5 - mov x0, x20 - bl _object_getClass -Lloh139: - adrp x21, l_anon.[ID].12@PAGE -Lloh140: - add x21, x21, l_anon.[ID].12@PAGEOFF -Lloh141: - adrp x22, l_anon.[ID].13@PAGE -Lloh142: - add x22, x22, l_anon.[ID].13@PAGEOFF - mov x1, x21 - mov w2, #4 - mov x3, x22 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldrb w23, [x20, x0] - mov x0, x19 - bl _object_getClass - mov x1, x21 - mov w2, #4 - mov x3, x22 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - strb w23, [x19, x0] - mov x0, x20 - bl _object_getClass -Lloh143: - adrp x1, l_anon.[ID].14@PAGE -Lloh144: - add x1, x1, l_anon.[ID].14@PAGEOFF -Lloh145: - adrp x3, l_anon.[ID].2@PAGE -Lloh146: - add x3, x3, l_anon.[ID].2@PAGEOFF - mov w2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldr x0, [x20, x0] - cbz x0, LBB14_3 - bl _objc_retain + bl SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) mov x20, x0 - b LBB14_4 -LBB14_3: - mov x20, #0 -LBB14_4: - mov x0, x19 - bl _object_getClass -Lloh147: - adrp x1, l_anon.[ID].14@PAGE -Lloh148: - add x1, x1, l_anon.[ID].14@PAGEOFF -Lloh149: - adrp x3, l_anon.[ID].2@PAGE -Lloh150: - add x3, x3, l_anon.[ID].2@PAGEOFF - mov w2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - str x20, [x19, x0] -LBB14_5: - mov x0, x19 + 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 +Lloh241: + adrp x8, SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 0)@PAGE +Lloh242: + 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: + mov x0, x20 + bl _objc_release + mov x0, x21 + bl _objc_release +LBB25_3: +Lloh243: + adrp x8, L_OBJC_SELECTOR_REFERENCES_6edddcebbded8f32@PAGE +Lloh244: + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_6edddcebbded8f32@PAGEOFF] +Lloh245: + adrp x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGE +Lloh246: + ldr x8, [x8, L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPAGEOFF] +Lloh247: + ldr x8, [x8] + stp x19, x8, [sp] + mov x0, sp + bl _objc_msgSendSuper + 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] - ldp x24, x23, [sp], #64 + add sp, sp, #64 ret - .loh AdrpAdd Lloh145, Lloh146 - .loh AdrpAdd Lloh143, Lloh144 - .loh AdrpAdd Lloh141, Lloh142 - .loh AdrpAdd Lloh139, Lloh140 - .loh AdrpAdd Lloh149, Lloh150 - .loh AdrpAdd Lloh147, Lloh148 + .loh AdrpLdr Lloh241, Lloh242 + .loh AdrpLdrGotLdr Lloh245, Lloh246, Lloh247 + .loh AdrpAdrp Lloh243, Lloh245 + .loh AdrpLdr Lloh243, Lloh244 .section __DATA,__const .p2align 3, 0x0 l_anon.[ID].0: - .quad SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0) + .quad SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0) .asciz "\b\000\000\000\000\000\000\000\b\000\000\000\000\000\000" - .quad 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) - .quad SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) + .quad 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) + .quad SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) - .section __TEXT,__const .p2align 3, 0x0 l_anon.[ID].1: - .byte 0 + .quad SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0) + .asciz "\b\000\000\000\000\000\000\000\b\000\000\000\000\000\000" + .quad 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) + .quad SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) .p2align 3, 0x0 l_anon.[ID].2: + .quad SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0) + .asciz "\b\000\000\000\000\000\000\000\b\000\000\000\000\000\000" + .quad 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) + .quad SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) + + .section __TEXT,__const + .p2align 3, 0x0 +l_anon.[ID].3: + .byte 0 + + .p2align 3, 0x0 +l_anon.[ID].4: .byte 19 .space 39 .p2align 3, 0x0 -l_anon.[ID].3: +l_anon.[ID].5: .byte 17 .space 39 .p2align 3, 0x0 -l_anon.[ID].4: +l_anon.[ID].6: .byte 16 .space 39 -l_anon.[ID].5: +l_anon.[ID].7: .ascii "_NSZone" .section __DATA,__const .p2align 3, 0x0 -l_anon.[ID].6: +l_anon.[ID].8: .byte 28 .space 7 - .quad l_anon.[ID].5 + .quad l_anon.[ID].7 .asciz "\007\000\000\000\000\000\000" - .quad l_anon.[ID].1 + .quad l_anon.[ID].3 .space 8 .p2align 3, 0x0 -l_anon.[ID].7: +l_anon.[ID].9: .byte 25 .space 7 - .quad l_anon.[ID].6 + .quad l_anon.[ID].8 .space 24 .section __TEXT,__const -l_anon.[ID].8: - .ascii "called `Option::unwrap()` on a `None` value" - -l_anon.[ID].9: - .ascii "$RUSTC/library/std/src/sync/once.rs" - - .section __DATA,__const .p2align 3, 0x0 l_anon.[ID].10: - .quad l_anon.[ID].9 - .asciz "p\000\000\000\000\000\000\000\225\000\000\0002\000\000" + .byte 21 + .space 39 - .section __TEXT,__const l_anon.[ID].11: - .ascii "CustomClassName" + .ascii "ivars" - .section __TEXT,__literal4,4byte_literals l_anon.[ID].12: - .ascii "_foo" + .ascii "drop_flag" - .section __TEXT,__const .p2align 3, 0x0 l_anon.[ID].13: .byte 5 .space 39 - .section __TEXT,__literal4,4byte_literals + .p2align 3, 0x0 l_anon.[ID].14: - .ascii "_obj" + .byte 7 + .space 39 - .section __TEXT,__const + .p2align 3, 0x0 l_anon.[ID].15: + .byte 9 + .space 39 + +l_anon.[ID].16: + .ascii "ForgetableIvars" + +l_anon.[ID].17: + .ascii "NoIvars" + +l_anon.[ID].18: + .ascii "DropIvars" + +l_anon.[ID].19: + .ascii "called `Option::unwrap()` on a `None` value" + +l_anon.[ID].20: + .ascii "$RUSTC/library/std/src/sync/once.rs" + + .section __DATA,__const + .p2align 3, 0x0 +l_anon.[ID].21: + .quad l_anon.[ID].20 + .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, 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, 1) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 1),8,3 + .section __TEXT,__const +l_anon.[ID].22: .ascii "crates/$DIR/lib.rs" .section __DATA,__const .p2align 3, 0x0 -l_anon.[ID].16: - .quad l_anon.[ID].15 - .asciz "5\000\000\000\000\000\000\000\f\000\000\000\001\000\000" +l_anon.[ID].23: + .quad l_anon.[ID].22 + .asciz "5\000\000\000\000\000\000\000\016\000\000\000\001\000\000" + + .section __TEXT,__literal8,8byte_literals +l_anon.[ID].24: + .ascii "NSObject" -.zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),8,3 .section __TEXT,__const -l_anon.[ID].17: +l_anon.[ID].25: .ascii "NSCopying" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_d874ee9262978be2 -L_OBJC_METH_VAR_NAME_d874ee9262978be2: + .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_d874ee9262978be2 + .globl L_OBJC_SELECTOR_REFERENCES_03fd85b0462f54e9 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2: - .quad L_OBJC_METH_VAR_NAME_d874ee9262978be2 +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_d874ee9262978be2 + .globl L_OBJC_IMAGE_INFO_03fd85b0462f54e9 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_d874ee9262978be2: +L_OBJC_IMAGE_INFO_03fd85b0462f54e9: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc -L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc: + .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_4539fd1dbda0cddc + .globl L_OBJC_SELECTOR_REFERENCES_cf773331f3cfba54 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc: - .quad L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc +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_4539fd1dbda0cddc + .globl L_OBJC_IMAGE_INFO_cf773331f3cfba54 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_4539fd1dbda0cddc: +L_OBJC_IMAGE_INFO_cf773331f3cfba54: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 -L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5: + .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_2b1b3a94e0ece2e5 + .globl L_OBJC_SELECTOR_REFERENCES_abdcbb85641cd990 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5: - .quad L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 +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_2b1b3a94e0ece2e5 + .globl L_OBJC_IMAGE_INFO_abdcbb85641cd990 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5: +L_OBJC_IMAGE_INFO_abdcbb85641cd990: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f7f521670860b0ce -L_OBJC_METH_VAR_NAME_f7f521670860b0ce: + .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_f7f521670860b0ce + .globl L_OBJC_SELECTOR_REFERENCES_ef8de92414f2d9c8 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce: - .quad L_OBJC_METH_VAR_NAME_f7f521670860b0ce +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_f7f521670860b0ce + .globl L_OBJC_IMAGE_INFO_ef8de92414f2d9c8 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f7f521670860b0ce: +L_OBJC_IMAGE_INFO_ef8de92414f2d9c8: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_6addfcf634c6232f -L_OBJC_METH_VAR_NAME_6addfcf634c6232f: + .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_6addfcf634c6232f + .globl L_OBJC_SELECTOR_REFERENCES_4a611090161f3fae .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f: - .quad L_OBJC_METH_VAR_NAME_6addfcf634c6232f +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_6addfcf634c6232f + .globl L_OBJC_IMAGE_INFO_4a611090161f3fae .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_6addfcf634c6232f: +L_OBJC_IMAGE_INFO_4a611090161f3fae: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 -L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166: + .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_4a8c690dbc9d8166 + .globl L_OBJC_SELECTOR_REFERENCES_2837f061c311eb14 + .p2align 3, 0x0 +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_2837f061c311eb14 + .p2align 2, 0x0 +L_OBJC_IMAGE_INFO_2837f061c311eb14: + .asciz "\000\000\000\000@\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, 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].26: + .quad l_anon.[ID].22 + .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_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].22 + .asciz "5\000\000\000\000\000\000\000x\000\000\000\001\000\000" + + .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_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166: - .quad L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 +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_4a8c690dbc9d8166 + .globl L_OBJC_IMAGE_INFO_6edddcebbded8f32 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_4a8c690dbc9d8166: +L_OBJC_IMAGE_INFO_6edddcebbded8f32: .asciz "\000\000\000\000@\000\000" +.zerofill __DATA,__bss,__MergedGlobals,48,3 .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_declare_class/expected/apple-armv7s.s b/crates/test-assembly/crates/test_declare_class/expected/apple-armv7s.s index 55f165c2a..a6e83e1ef 100644 --- a/crates/test-assembly/crates/test_declare_class/expected/apple-armv7s.s +++ b/crates/test-assembly/crates/test_declare_class/expected/apple-armv7s.s @@ -1,882 +1,3 @@ .section __TEXT,__text,regular,pure_instructions .syntax unified - .p2align 2 - .code 32 -SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0): - bx lr - - .p2align 2 - .code 32 -SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0): - push {r4, r5, r7, lr} - add r7, sp, #8 - push {r8, r10, r11} - sub sp, sp, #12 - ldr r0, [r0] - ldrb r1, [r0] - mov r2, #0 - strb r2, [r0] - cmp r1, #0 - beq LBB1_5 - movw r0, :lower16:(LL_OBJC_CLASSLIST_REFERENCES_$_NSObject$non_lazy_ptr-(LPC1_0+8)) - movt r0, :upper16:(LL_OBJC_CLASSLIST_REFERENCES_$_NSObject$non_lazy_ptr-(LPC1_0+8)) -LPC1_0: - ldr r0, [pc, r0] - ldr r2, [r0] - movw r0, :lower16:(l_anon.[ID].11-(LPC1_1+8)) - movt r0, :upper16:(l_anon.[ID].11-(LPC1_1+8)) -LPC1_1: - add r0, pc, r0 - mov r1, #15 - bl SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) - cmp r0, #0 - beq LBB1_6 - str r0, [sp, #8] - movw r1, :lower16:(l_anon.[ID].13-(LPC1_2+8)) - movt r1, :upper16:(l_anon.[ID].13-(LPC1_2+8)) -LPC1_2: - add r1, pc, r1 - mov r0, #0 - strd r0, r1, [sp] - movw r1, :lower16:(L_anon.[ID].12-(LPC1_3+8)) - movt r1, :upper16:(L_anon.[ID].12-(LPC1_3+8)) -LPC1_3: - add r1, pc, r1 - add r4, sp, #8 - mov r0, r4 - mov r2, #4 - mov r3, #1 - bl SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) - movw r8, :lower16:(l_anon.[ID].2-(LPC1_4+8)) - movt r8, :upper16:(l_anon.[ID].2-(LPC1_4+8)) -LPC1_4: - add r8, pc, r8 - mov r0, #2 - stm sp, {r0, r8} - movw r1, :lower16:(L_anon.[ID].14-(LPC1_5+8)) - movt r1, :upper16:(L_anon.[ID].14-(LPC1_5+8)) -LPC1_5: - add r1, pc, r1 - mov r0, r4 - mov r2, #4 - mov r3, #4 - bl SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) - movw r0, :lower16:(LL_OBJC_SELECTOR_REFERENCES_dealloc$non_lazy_ptr-(LPC1_6+8)) - movt r0, :upper16:(LL_OBJC_SELECTOR_REFERENCES_dealloc$non_lazy_ptr-(LPC1_6+8)) -LPC1_6: - ldr r0, [pc, r0] - ldr r1, [r0] - movw r11, :lower16:(SYM(::class::{closure#0}::__objc2_dealloc, 0)-(LPC1_7+8)) - movt r11, :upper16:(SYM(::class::{closure#0}::__objc2_dealloc, 0)-(LPC1_7+8)) -LPC1_7: - add r11, pc, r11 - movw r10, :lower16:(l_anon.[ID].3-(LPC1_8+8)) - movt r10, :upper16:(l_anon.[ID].3-(LPC1_8+8)) -LPC1_8: - add r10, pc, r10 - strd r10, r11, [sp] - movw r5, :lower16:(l_anon.[ID].1-(LPC1_9+8)) - movt r5, :upper16:(l_anon.[ID].1-(LPC1_9+8)) -LPC1_9: - add r5, pc, r5 - mov r0, r4 - mov r2, r5 - mov r3, #0 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - movw r0, :lower16:(LL_OBJC_SELECTOR_REFERENCES_init$non_lazy_ptr-(LPC1_10+8)) - movt r0, :upper16:(LL_OBJC_SELECTOR_REFERENCES_init$non_lazy_ptr-(LPC1_10+8)) -LPC1_10: - ldr r0, [pc, r0] - ldr r1, [r0] - movw r9, :lower16:(_init-(LPC1_11+8)) - movt r9, :upper16:(_init-(LPC1_11+8)) -LPC1_11: - add r9, pc, r9 - strd r8, r9, [sp] - mov r0, r4 - mov r2, r5 - mov r3, #0 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2-(LPC1_12+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2-(LPC1_12+8)) -LPC1_12: - ldr r1, [pc, r1] - movw r11, :lower16:(_class_method-(LPC1_13+8)) - movt r11, :upper16:(_class_method-(LPC1_13+8)) -LPC1_13: - add r11, pc, r11 - strd r10, r11, [sp] - mov r0, r4 - mov r2, r5 - mov r3, #0 - bl SYM(objc2::declare::ClassBuilder::add_class_method_inner::GENERATED_ID, 0) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc-(LPC1_14+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc-(LPC1_14+8)) -LPC1_14: - ldr r1, [pc, r1] - movw r11, :lower16:(_method-(LPC1_15+8)) - movt r11, :upper16:(_method-(LPC1_15+8)) -LPC1_15: - add r11, pc, r11 - strd r10, r11, [sp] - mov r0, r4 - mov r2, r5 - mov r3, #0 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5-(LPC1_16+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5-(LPC1_16+8)) -LPC1_16: - ldr r1, [pc, r1] - movw r3, :lower16:(_method_bool-(LPC1_17+8)) - movt r3, :upper16:(_method_bool-(LPC1_17+8)) -LPC1_17: - add r3, pc, r3 - movw r10, :lower16:(l_anon.[ID].4-(LPC1_18+8)) - movt r10, :upper16:(l_anon.[ID].4-(LPC1_18+8)) -LPC1_18: - add r10, pc, r10 - str r10, [sp] - str r3, [sp, #4] - mov r0, r4 - mov r2, r10 - mov r3, #1 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce-(LPC1_19+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce-(LPC1_19+8)) -LPC1_19: - ldr r1, [pc, r1] - movw r9, :lower16:(_method_id-(LPC1_20+8)) - movt r9, :upper16:(_method_id-(LPC1_20+8)) -LPC1_20: - add r9, pc, r9 - strd r8, r9, [sp] - mov r0, r4 - mov r2, r5 - mov r3, #0 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f-(LPC1_21+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f-(LPC1_21+8)) -LPC1_21: - ldr r1, [pc, r1] - movw r9, :lower16:(_method_id_with_param-(LPC1_22+8)) - movt r9, :upper16:(_method_id_with_param-(LPC1_22+8)) -LPC1_22: - add r9, pc, r9 - strd r8, r9, [sp] - mov r0, r4 - mov r2, r10 - mov r3, #1 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - movw r0, :lower16:(l_anon.[ID].17-(LPC1_23+8)) - movt r0, :upper16:(l_anon.[ID].17-(LPC1_23+8)) -LPC1_23: - add r0, pc, r0 - mov r1, #9 - bl SYM(objc2::runtime::AnyProtocol::get::GENERATED_ID, 0) - cmp r0, #0 - beq LBB1_4 - mov r1, r0 - add r0, sp, #8 - bl SYM(objc2::declare::ClassBuilder::add_protocol::GENERATED_ID, 0) -LBB1_4: - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166-(LPC1_24+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166-(LPC1_24+8)) -LPC1_24: - ldr r1, [pc, r1] - movw r9, :lower16:(_copyWithZone-(LPC1_25+8)) - movt r9, :upper16:(_copyWithZone-(LPC1_25+8)) -LPC1_25: - add r9, pc, r9 - strd r8, r9, [sp] - movw r2, :lower16:(l_anon.[ID].7-(LPC1_26+8)) - movt r2, :upper16:(l_anon.[ID].7-(LPC1_26+8)) -LPC1_26: - add r2, pc, r2 - add r0, sp, #8 - mov r3, #1 - bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - ldr r0, [sp, #8] - bl SYM(objc2::declare::ClassBuilder::register::GENERATED_ID, 0) - sub sp, r7, #20 - pop {r8, r10, r11} - pop {r4, r5, r7, pc} -LBB1_5: - movw r0, :lower16:(l_anon.[ID].8-(LPC1_27+8)) - movt r0, :upper16:(l_anon.[ID].8-(LPC1_27+8)) -LPC1_27: - add r0, pc, r0 - movw r2, :lower16:(l_anon.[ID].10-(LPC1_28+8)) - movt r2, :upper16:(l_anon.[ID].10-(LPC1_28+8)) -LPC1_28: - add r2, pc, r2 - mov r1, #43 - mov lr, pc - b SYM(core::panicking::panic::GENERATED_ID, 0) -LBB1_6: - movw r0, :lower16:(l_anon.[ID].11-(LPC1_29+8)) - movt r0, :upper16:(l_anon.[ID].11-(LPC1_29+8)) -LPC1_29: - add r0, pc, r0 - movw r2, :lower16:(l_anon.[ID].16-(LPC1_30+8)) - movt r2, :upper16:(l_anon.[ID].16-(LPC1_30+8)) -LPC1_30: - add r2, pc, r2 - mov r1, #15 - mov lr, pc - b SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) - - .p2align 2 - .code 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): - push {r7, lr} - mov r7, sp - sub sp, sp, #4 - ldr r0, [r0] - str r0, [sp] - mov r0, sp - bl SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) - mov sp, r7 - pop {r7, pc} - - .globl _get_class - .p2align 2 - .code 32 -_get_class: - push {r7, lr} - mov r7, sp - sub sp, sp, #12 - movw r0, :lower16:(SYM(::class::REGISTER_CLASS, 0)-(LPC3_0+8)) - movt r0, :upper16:(SYM(::class::REGISTER_CLASS, 0)-(LPC3_0+8)) -LPC3_0: - add r0, pc, r0 - ldr r0, [r0] - dmb ish - cmp r0, #3 - bne LBB3_3 -LBB3_1: - movw r0, :lower16:(l_anon.[ID].11-(LPC3_4+8)) - movt r0, :upper16:(l_anon.[ID].11-(LPC3_4+8)) -LPC3_4: - add r0, pc, r0 - mov r1, #15 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cmp r0, #0 - movne sp, r7 - popne {r7, pc} -LBB3_2: - movw r0, :lower16:(l_anon.[ID].8-(LPC3_5+8)) - movt r0, :upper16:(l_anon.[ID].8-(LPC3_5+8)) -LPC3_5: - add r0, pc, r0 - movw r2, :lower16:(l_anon.[ID].16-(LPC3_6+8)) - movt r2, :upper16:(l_anon.[ID].16-(LPC3_6+8)) -LPC3_6: - add r2, pc, r2 - mov r1, #43 - mov lr, pc - b SYM(core::panicking::panic::GENERATED_ID, 0) -LBB3_3: - mov r0, #1 - strb r0, [r7, #-5] - sub r0, r7, #5 - str r0, [r7, #-4] - movw r1, :lower16:(l_anon.[ID].16-(LPC3_1+8)) - movt r1, :upper16:(l_anon.[ID].16-(LPC3_1+8)) -LPC3_1: - add r1, pc, r1 - movw r0, :lower16:(SYM(::class::REGISTER_CLASS, 0)-(LPC3_2+8)) - movt r0, :upper16:(SYM(::class::REGISTER_CLASS, 0)-(LPC3_2+8)) -LPC3_2: - add r0, pc, r0 - movw r3, :lower16:(l_anon.[ID].0-(LPC3_3+8)) - movt r3, :upper16:(l_anon.[ID].0-(LPC3_3+8)) -LPC3_3: - add r3, pc, r3 - str r1, [sp] - sub r2, r7, #4 - mov r1, #0 - bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - b LBB3_1 - - .globl _get_obj - .p2align 2 - .code 32 -_get_obj: - push {r7, lr} - mov r7, sp - bl _get_class - movw r1, :lower16:(LL_OBJC_SELECTOR_REFERENCES_new$non_lazy_ptr-(LPC4_0+8)) - movt r1, :upper16:(LL_OBJC_SELECTOR_REFERENCES_new$non_lazy_ptr-(LPC4_0+8)) -LPC4_0: - ldr r1, [pc, r1] - ldr r1, [r1] - pop {r7, lr} - b _objc_msgSend - - .globl _access_ivars - .p2align 2 - .code 32 -_access_ivars: - push {r4, r5, r6, r7, lr} - add r7, sp, #12 - bl _get_obj - mov r4, r0 - bl _object_getClass - movw r1, :lower16:(L_anon.[ID].12-(LPC5_0+8)) - movt r1, :upper16:(L_anon.[ID].12-(LPC5_0+8)) -LPC5_0: - add r1, pc, r1 - movw r3, :lower16:(l_anon.[ID].13-(LPC5_1+8)) - movt r3, :upper16:(l_anon.[ID].13-(LPC5_1+8)) -LPC5_1: - add r3, pc, r3 - mov r2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldrb r5, [r4, r0] - mov r0, r4 - bl _object_getClass - movw r1, :lower16:(L_anon.[ID].14-(LPC5_2+8)) - movt r1, :upper16:(L_anon.[ID].14-(LPC5_2+8)) -LPC5_2: - add r1, pc, r1 - movw r3, :lower16:(l_anon.[ID].2-(LPC5_3+8)) - movt r3, :upper16:(l_anon.[ID].2-(LPC5_3+8)) -LPC5_3: - add r3, pc, r3 - mov r2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldr r6, [r4, r0] - mov r0, r4 - bl _objc_release - mov r0, r5 - mov r1, r6 - pop {r4, r5, r6, r7, pc} - - .globl SYM(::class, 0) - .p2align 2 - .code 32 -SYM(::class, 0): - push {r7, lr} - mov r7, sp - sub sp, sp, #12 - movw r0, :lower16:(SYM(::class::REGISTER_CLASS, 0)-(LPC6_0+8)) - movt r0, :upper16:(SYM(::class::REGISTER_CLASS, 0)-(LPC6_0+8)) -LPC6_0: - add r0, pc, r0 - ldr r0, [r0] - dmb ish - cmp r0, #3 - bne LBB6_3 -LBB6_1: - movw r0, :lower16:(l_anon.[ID].11-(LPC6_4+8)) - movt r0, :upper16:(l_anon.[ID].11-(LPC6_4+8)) -LPC6_4: - add r0, pc, r0 - mov r1, #15 - bl SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - cmp r0, #0 - movne sp, r7 - popne {r7, pc} -LBB6_2: - movw r0, :lower16:(l_anon.[ID].8-(LPC6_5+8)) - movt r0, :upper16:(l_anon.[ID].8-(LPC6_5+8)) -LPC6_5: - add r0, pc, r0 - movw r2, :lower16:(l_anon.[ID].16-(LPC6_6+8)) - movt r2, :upper16:(l_anon.[ID].16-(LPC6_6+8)) -LPC6_6: - add r2, pc, r2 - mov r1, #43 - mov lr, pc - b SYM(core::panicking::panic::GENERATED_ID, 0) -LBB6_3: - mov r0, #1 - strb r0, [r7, #-5] - sub r0, r7, #5 - str r0, [r7, #-4] - movw r1, :lower16:(l_anon.[ID].16-(LPC6_1+8)) - movt r1, :upper16:(l_anon.[ID].16-(LPC6_1+8)) -LPC6_1: - add r1, pc, r1 - movw r0, :lower16:(SYM(::class::REGISTER_CLASS, 0)-(LPC6_2+8)) - movt r0, :upper16:(SYM(::class::REGISTER_CLASS, 0)-(LPC6_2+8)) -LPC6_2: - add r0, pc, r0 - movw r3, :lower16:(l_anon.[ID].0-(LPC6_3+8)) - movt r3, :upper16:(l_anon.[ID].0-(LPC6_3+8)) -LPC6_3: - add r3, pc, r3 - str r1, [sp] - sub r2, r7, #4 - mov r1, #0 - bl SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - b LBB6_1 - - .p2align 2 - .code 32 -SYM(::class::{closure#0}::__objc2_dealloc, 0): - push {r4, r5, r7, lr} - add r7, sp, #8 - sub sp, sp, #8 - mov r4, r1 - mov r5, r0 - bl _object_getClass - movw r1, :lower16:(L_anon.[ID].14-(LPC7_0+8)) - movt r1, :upper16:(L_anon.[ID].14-(LPC7_0+8)) -LPC7_0: - add r1, pc, r1 - movw r3, :lower16:(l_anon.[ID].2-(LPC7_1+8)) - movt r3, :upper16:(l_anon.[ID].2-(LPC7_1+8)) -LPC7_1: - add r3, pc, r3 - mov r2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldr r0, [r5, r0] - cmp r0, #0 - beq LBB7_2 - bl _objc_release -LBB7_2: - movw r0, :lower16:(LL_OBJC_CLASSLIST_REFERENCES_$_NSObject$non_lazy_ptr-(LPC7_2+8)) - movt r0, :upper16:(LL_OBJC_CLASSLIST_REFERENCES_$_NSObject$non_lazy_ptr-(LPC7_2+8)) -LPC7_2: - ldr r0, [pc, r0] - ldr r0, [r0] - str r5, [sp] - str r0, [sp, #4] - mov r0, sp - mov r1, r4 - bl _objc_msgSendSuper - sub sp, r7, #8 - pop {r4, r5, r7, pc} - - .globl _init - .p2align 2 - .code 32 -_init: - push {r4, r7, lr} - add r7, sp, #4 - sub sp, sp, #8 - movw r1, :lower16:(LL_OBJC_SELECTOR_REFERENCES_init$non_lazy_ptr-(LPC8_0+8)) - movt r1, :upper16:(LL_OBJC_SELECTOR_REFERENCES_init$non_lazy_ptr-(LPC8_0+8)) -LPC8_0: - ldr r1, [pc, r1] - ldr r1, [r1] - movw r2, :lower16:(LL_OBJC_CLASSLIST_REFERENCES_$_NSObject$non_lazy_ptr-(LPC8_1+8)) - movt r2, :upper16:(LL_OBJC_CLASSLIST_REFERENCES_$_NSObject$non_lazy_ptr-(LPC8_1+8)) -LPC8_1: - ldr r2, [pc, r2] - ldr r2, [r2] - stm sp, {r0, r2} - mov r0, sp - bl _objc_msgSendSuper - mov r4, r0 - cmp r0, #0 - beq LBB8_2 - mov r0, r4 - bl _object_getClass - movw r1, :lower16:(L_anon.[ID].12-(LPC8_2+8)) - movt r1, :upper16:(L_anon.[ID].12-(LPC8_2+8)) -LPC8_2: - add r1, pc, r1 - movw r3, :lower16:(l_anon.[ID].13-(LPC8_3+8)) - movt r3, :upper16:(l_anon.[ID].13-(LPC8_3+8)) -LPC8_3: - add r3, pc, r3 - mov r2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov r1, #42 - strb r1, [r4, r0] - mov r0, r4 - bl _object_getClass - movw r1, :lower16:(L_anon.[ID].14-(LPC8_4+8)) - movt r1, :upper16:(L_anon.[ID].14-(LPC8_4+8)) -LPC8_4: - add r1, pc, r1 - movw r3, :lower16:(l_anon.[ID].2-(LPC8_5+8)) - movt r3, :upper16:(l_anon.[ID].2-(LPC8_5+8)) -LPC8_5: - add r3, pc, r3 - mov r2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov r1, #0 - str r1, [r4, r0] -LBB8_2: - mov r0, r4 - sub sp, r7, #4 - pop {r4, r7, pc} - - .globl _class_method - .p2align 2 - .code 32 -_class_method: - bx lr - - .globl _method - .p2align 2 - .code 32 -_method: - bx lr - - .globl _method_bool - .p2align 2 - .code 32 -_method_bool: - clz r0, r2 - lsr r0, r0, #5 - bx lr - - .globl _method_id - .p2align 2 - .code 32 -_method_id: - push {r4, r7, lr} - add r7, sp, #4 - mov r4, r0 - bl _object_getClass - movw r1, :lower16:(L_anon.[ID].14-(LPC12_0+8)) - movt r1, :upper16:(L_anon.[ID].14-(LPC12_0+8)) -LPC12_0: - add r1, pc, r1 - movw r3, :lower16:(l_anon.[ID].2-(LPC12_1+8)) - movt r3, :upper16:(l_anon.[ID].2-(LPC12_1+8)) -LPC12_1: - add r3, pc, r3 - mov r2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldr r0, [r4, r0] - cmp r0, #0 - beq LBB12_2 - bl _objc_retain - pop {r4, r7, lr} - b _objc_autoreleaseReturnValue -LBB12_2: - mov r0, #0 - pop {r4, r7, lr} - b _objc_autoreleaseReturnValue - - .globl _method_id_with_param - .p2align 2 - .code 32 -_method_id_with_param: - push {r4, r5, r6, r7, lr} - add r7, sp, #12 - mov r6, r2 - mov r5, r0 - bl SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) - mov r4, r0 - cmp r6, #0 - beq LBB13_5 - mov r0, r5 - bl _object_getClass - movw r1, :lower16:(L_anon.[ID].14-(LPC13_0+8)) - movt r1, :upper16:(L_anon.[ID].14-(LPC13_0+8)) -LPC13_0: - add r1, pc, r1 - movw r3, :lower16:(l_anon.[ID].2-(LPC13_1+8)) - movt r3, :upper16:(l_anon.[ID].2-(LPC13_1+8)) -LPC13_1: - add r3, pc, r3 - mov r2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldr r0, [r5, r0] - cmp r0, #0 - beq LBB13_3 - bl _objc_retain - mov r5, r0 - b LBB13_4 -LBB13_3: - mov r5, #0 -LBB13_4: - mov r0, r4 - bl _objc_release - mov r4, r5 -LBB13_5: - mov r0, r4 - pop {r4, r5, r6, r7, lr} - b _objc_autoreleaseReturnValue - - .globl _copyWithZone - .p2align 2 - .code 32 -_copyWithZone: - push {r4, r5, r6, r7, lr} - add r7, sp, #12 - push {r8, r10} - mov r5, r0 - bl _get_obj - mov r4, r0 - cmp r0, #0 - beq LBB14_5 - mov r0, r5 - bl _object_getClass - movw r10, :lower16:(L_anon.[ID].12-(LPC14_0+8)) - movt r10, :upper16:(L_anon.[ID].12-(LPC14_0+8)) -LPC14_0: - add r10, pc, r10 - movw r8, :lower16:(l_anon.[ID].13-(LPC14_1+8)) - movt r8, :upper16:(l_anon.[ID].13-(LPC14_1+8)) -LPC14_1: - add r8, pc, r8 - mov r1, r10 - mov r2, #4 - mov r3, r8 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldrb r6, [r5, r0] - mov r0, r4 - bl _object_getClass - mov r1, r10 - mov r2, #4 - mov r3, r8 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - strb r6, [r4, r0] - mov r0, r5 - bl _object_getClass - movw r1, :lower16:(L_anon.[ID].14-(LPC14_2+8)) - movt r1, :upper16:(L_anon.[ID].14-(LPC14_2+8)) -LPC14_2: - add r1, pc, r1 - movw r3, :lower16:(l_anon.[ID].2-(LPC14_3+8)) - movt r3, :upper16:(l_anon.[ID].2-(LPC14_3+8)) -LPC14_3: - add r3, pc, r3 - mov r2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - ldr r0, [r5, r0] - cmp r0, #0 - beq LBB14_3 - bl _objc_retain - mov r5, r0 - b LBB14_4 -LBB14_3: - mov r5, #0 -LBB14_4: - mov r0, r4 - bl _object_getClass - movw r1, :lower16:(L_anon.[ID].14-(LPC14_4+8)) - movt r1, :upper16:(L_anon.[ID].14-(LPC14_4+8)) -LPC14_4: - add r1, pc, r1 - movw r3, :lower16:(l_anon.[ID].2-(LPC14_5+8)) - movt r3, :upper16:(l_anon.[ID].2-(LPC14_5+8)) -LPC14_5: - add r3, pc, r3 - mov r2, #4 - bl SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - str r5, [r4, r0] -LBB14_5: - mov r0, r4 - pop {r8, r10} - pop {r4, r5, r6, r7, pc} - - .section __DATA,__const - .p2align 2, 0x0 -l_anon.[ID].0: - .long SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0) - .asciz "\004\000\000\000\004\000\000" - .long 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) - .long SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) - - .section __TEXT,__const - .p2align 2, 0x0 -l_anon.[ID].1: - .byte 0 - - .p2align 2, 0x0 -l_anon.[ID].2: - .byte 19 - .space 19 - - .p2align 2, 0x0 -l_anon.[ID].3: - .byte 17 - .space 19 - - .p2align 2, 0x0 -l_anon.[ID].4: - .space 1 - .space 19 - -l_anon.[ID].5: - .ascii "_NSZone" - - .section __DATA,__const - .p2align 2, 0x0 -l_anon.[ID].6: - .byte 28 - .space 3 - .long l_anon.[ID].5 - .asciz "\007\000\000" - .long l_anon.[ID].1 - .space 4 - - .p2align 2, 0x0 -l_anon.[ID].7: - .byte 25 - .space 3 - .long l_anon.[ID].6 - .space 12 - - .section __TEXT,__const -l_anon.[ID].8: - .ascii "called `Option::unwrap()` on a `None` value" - -l_anon.[ID].9: - .ascii "$RUSTC/library/std/src/sync/once.rs" - - .section __DATA,__const - .p2align 2, 0x0 -l_anon.[ID].10: - .long l_anon.[ID].9 - .asciz "p\000\000\000\225\000\000\0002\000\000" - - .section __TEXT,__const -l_anon.[ID].11: - .ascii "CustomClassName" - - .section __TEXT,__literal4,4byte_literals -L_anon.[ID].12: - .ascii "_foo" - - .section __TEXT,__const - .p2align 2, 0x0 -l_anon.[ID].13: - .byte 5 - .space 19 - - .section __TEXT,__literal4,4byte_literals -L_anon.[ID].14: - .ascii "_obj" - - .section __TEXT,__const -l_anon.[ID].15: - .ascii "crates/$DIR/lib.rs" - - .section __DATA,__const - .p2align 2, 0x0 -l_anon.[ID].16: - .long l_anon.[ID].15 - .asciz "5\000\000\000\f\000\000\000\001\000\000" - -.zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),4,2 - .section __TEXT,__const -l_anon.[ID].17: - .ascii "NSCopying" - - .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_d874ee9262978be2 -L_OBJC_METH_VAR_NAME_d874ee9262978be2: - .asciz "classMethod" - - .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2 - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2: - .long L_OBJC_METH_VAR_NAME_d874ee9262978be2 - - .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_d874ee9262978be2 - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_d874ee9262978be2: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc -L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc: - .asciz "method" - - .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc: - .long L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc - - .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_4539fd1dbda0cddc - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_4539fd1dbda0cddc: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 -L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5: - .asciz "methodBool:" - - .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5 - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5: - .long L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 - - .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5 - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f7f521670860b0ce -L_OBJC_METH_VAR_NAME_f7f521670860b0ce: - .asciz "methodId" - - .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce: - .long L_OBJC_METH_VAR_NAME_f7f521670860b0ce - - .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_f7f521670860b0ce - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f7f521670860b0ce: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_6addfcf634c6232f -L_OBJC_METH_VAR_NAME_6addfcf634c6232f: - .asciz "methodIdWithParam:" - - .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f: - .long L_OBJC_METH_VAR_NAME_6addfcf634c6232f - - .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_6addfcf634c6232f - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_6addfcf634c6232f: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 -L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166: - .asciz "copyWithZone:" - - .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166 - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166: - .long L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 - - .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_4a8c690dbc9d8166 - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_4a8c690dbc9d8166: - .asciz "\000\000\000\000@\000\000" - - .section __DATA,__nl_symbol_ptr,non_lazy_symbol_pointers - .p2align 2, 0x0 -LL_OBJC_CLASSLIST_REFERENCES_$_NSObject$non_lazy_ptr: - .indirect_symbol L_OBJC_CLASSLIST_REFERENCES_$_NSObject - .long 0 -LL_OBJC_SELECTOR_REFERENCES_dealloc$non_lazy_ptr: - .indirect_symbol L_OBJC_SELECTOR_REFERENCES_dealloc - .long 0 -LL_OBJC_SELECTOR_REFERENCES_init$non_lazy_ptr: - .indirect_symbol L_OBJC_SELECTOR_REFERENCES_init - .long 0 -LL_OBJC_SELECTOR_REFERENCES_new$non_lazy_ptr: - .indirect_symbol L_OBJC_SELECTOR_REFERENCES_new - .long 0 - .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_declare_class/expected/apple-old-x86.s b/crates/test-assembly/crates/test_declare_class/expected/apple-old-x86.s index 396066212..d3cea7aab 100644 --- a/crates/test-assembly/crates/test_declare_class/expected/apple-old-x86.s +++ b/crates/test-assembly/crates/test_declare_class/expected/apple-old-x86.s @@ -1,911 +1,3 @@ .section __TEXT,__text,regular,pure_instructions .intel_syntax noprefix - .p2align 4, 0x90 -SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0): - push ebp - mov ebp, esp - pop ebp - ret - - .p2align 4, 0x90 -SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0): - push ebp - mov ebp, esp - push ebx - push edi - push esi - sub esp, 12 - call L1$pb -L1$pb: - pop esi - mov eax, dword ptr [ebp + 8] - mov eax, dword ptr [eax] - cmp byte ptr [eax], 0 - mov byte ptr [eax], 0 - je LBB1_5 - mov eax, dword ptr [esi + LL_OBJC_CLASS_REFERENCES_NSObject$non_lazy_ptr-L1$pb] - sub esp, 4 - lea edi, [esi + l_anon.[ID].11-L1$pb] - push dword ptr [eax] - push 15 - push edi - call SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) - add esp, 16 - test eax, eax - je LBB1_6 - mov dword ptr [ebp - 16], eax - sub esp, 8 - lea eax, [esi + l_anon.[ID].13-L1$pb] - lea ecx, [esi + L_anon.[ID].12-L1$pb] - lea ebx, [ebp - 16] - push eax - push 0 - push 1 - push 4 - push ecx - push ebx - call SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) - add esp, 24 - lea ecx, [esi + l_anon.[ID].2-L1$pb] - lea eax, [esi + L_anon.[ID].14-L1$pb] - push ecx - push 2 - push 4 - push 4 - push eax - push ebx - call SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) - add esp, 32 - mov eax, dword ptr [esi + LL_OBJC_SELECTOR_REFERENCES_dealloc$non_lazy_ptr-L1$pb] - sub esp, 8 - lea ecx, [esi + SYM(::class::{closure#0}::__objc2_dealloc, 0)-L1$pb] - lea edi, [esi + l_anon.[ID].3-L1$pb] - lea edx, [esi + l_anon.[ID].1-L1$pb] - push ecx - push edi - push 0 - push edx - mov edi, edx - push dword ptr [eax] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 32 - mov eax, dword ptr [esi + LL_OBJC_SELECTOR_REFERENCES_init$non_lazy_ptr-L1$pb] - sub esp, 8 - lea ecx, [esi + _init-L1$pb] - push ecx - lea ecx, [esi + l_anon.[ID].2-L1$pb] - push ecx - push 0 - push edi - push dword ptr [eax] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 24 - lea eax, [esi + _class_method-L1$pb] - push eax - lea eax, [esi + l_anon.[ID].3-L1$pb] - push eax - push 0 - push edi - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2-L1$pb] - push ebx - call SYM(objc2::declare::ClassBuilder::add_class_method_inner::GENERATED_ID, 0) - add esp, 24 - lea eax, [esi + _method-L1$pb] - push eax - lea eax, [esi + l_anon.[ID].3-L1$pb] - push eax - push 0 - push edi - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc-L1$pb] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 24 - lea eax, [esi + _method_bool-L1$pb] - lea ecx, [esi + l_anon.[ID].4-L1$pb] - push eax - push ecx - push 1 - push ecx - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5-L1$pb] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 24 - lea eax, [esi + _method_id-L1$pb] - push eax - lea edi, [esi + l_anon.[ID].2-L1$pb] - push edi - push 0 - lea eax, [esi + l_anon.[ID].1-L1$pb] - push eax - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce-L1$pb] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 24 - lea eax, [esi + _method_id_with_param-L1$pb] - push eax - push edi - push 1 - lea eax, [esi + l_anon.[ID].4-L1$pb] - push eax - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f-L1$pb] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 24 - lea eax, [esi + l_anon.[ID].17-L1$pb] - push 9 - push eax - call SYM(objc2::runtime::AnyProtocol::get::GENERATED_ID, 0) - add esp, 16 - test eax, eax - je LBB1_4 - sub esp, 8 - push eax - push ebx - call SYM(objc2::declare::ClassBuilder::add_protocol::GENERATED_ID, 0) - add esp, 16 -LBB1_4: - sub esp, 8 - lea eax, [esi + _copyWithZone-L1$pb] - lea ecx, [esi + l_anon.[ID].7-L1$pb] - push eax - lea eax, [esi + l_anon.[ID].2-L1$pb] - push eax - push 1 - push ecx - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166-L1$pb] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 20 - push dword ptr [ebp - 16] - call SYM(objc2::declare::ClassBuilder::register::GENERATED_ID, 0) - add esp, 28 - pop esi - pop edi - pop ebx - pop ebp - ret -LBB1_5: - sub esp, 4 - lea eax, [esi + l_anon.[ID].10-L1$pb] - lea ecx, [esi + l_anon.[ID].8-L1$pb] - push eax - push 43 - push ecx - call SYM(core::panicking::panic::GENERATED_ID, 0) -LBB1_6: - sub esp, 4 - lea eax, [esi + l_anon.[ID].16-L1$pb] - push eax - push 15 - push edi - call SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::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): - push ebp - mov ebp, esp - sub esp, 24 - mov eax, dword ptr [ebp + 8] - mov eax, dword ptr [eax] - mov dword ptr [ebp - 4], eax - lea eax, [ebp - 4] - mov dword ptr [esp], eax - call SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) - add esp, 24 - pop ebp - ret - - .globl _get_class - .p2align 4, 0x90 -_get_class: - push ebp - mov ebp, esp - push edi - push esi - sub esp, 16 - call L3$pb -L3$pb: - pop esi - mov eax, dword ptr [esi + SYM(::class::REGISTER_CLASS, 0)-L3$pb] - cmp eax, 3 - jne LBB3_1 -LBB3_2: - sub esp, 8 - lea eax, [esi + l_anon.[ID].11-L3$pb] - push 15 - push eax - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - add esp, 16 - test eax, eax - je LBB3_4 - add esp, 16 - pop esi - pop edi - pop ebp - ret -LBB3_1: - mov byte ptr [ebp - 9], 1 - lea eax, [ebp - 9] - mov dword ptr [ebp - 16], eax - sub esp, 12 - lea eax, [esi + l_anon.[ID].16-L3$pb] - lea ecx, [esi + l_anon.[ID].0-L3$pb] - lea edx, [ebp - 16] - lea edi, [esi + SYM(::class::REGISTER_CLASS, 0)-L3$pb] - push eax - push ecx - push edx - push 0 - push edi - call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - add esp, 32 - jmp LBB3_2 -LBB3_4: - sub esp, 4 - lea eax, [esi + l_anon.[ID].16-L3$pb] - lea ecx, [esi + l_anon.[ID].8-L3$pb] - push eax - push 43 - push ecx - call SYM(core::panicking::panic::GENERATED_ID, 0) - - .globl _get_obj - .p2align 4, 0x90 -_get_obj: - push ebp - mov ebp, esp - push esi - push eax - call L4$pb -L4$pb: - pop esi - call _get_class - mov ecx, dword ptr [esi + LL_OBJC_SELECTOR_REFERENCES_new$non_lazy_ptr-L4$pb] - sub esp, 8 - push dword ptr [ecx] - push eax - call _objc_msgSend - add esp, 20 - pop esi - pop ebp - ret - - .globl _access_ivars - .p2align 4, 0x90 -_access_ivars: - push ebp - mov ebp, esp - push ebx - push edi - push esi - sub esp, 12 - call L5$pb -L5$pb: - pop edi - call _get_obj - mov esi, eax - sub esp, 12 - push eax - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].13-L5$pb] - lea edx, [edi + L_anon.[ID].12-L5$pb] - push ecx - push 4 - push edx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - movzx ebx, byte ptr [esi + eax] - sub esp, 12 - push esi - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].2-L5$pb] - lea edx, [edi + L_anon.[ID].14-L5$pb] - push ecx - push 4 - push edx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov edi, dword ptr [esi + eax] - sub esp, 12 - push esi - call _objc_release - add esp, 16 - mov eax, ebx - mov edx, edi - add esp, 12 - pop esi - pop edi - pop ebx - pop ebp - ret - - .globl SYM(::class, 0) - .p2align 4, 0x90 -SYM(::class, 0): - push ebp - mov ebp, esp - push edi - push esi - sub esp, 16 - call L6$pb -L6$pb: - pop esi - mov eax, dword ptr [esi + SYM(::class::REGISTER_CLASS, 0)-L6$pb] - cmp eax, 3 - jne LBB6_1 -LBB6_2: - sub esp, 8 - lea eax, [esi + l_anon.[ID].11-L6$pb] - push 15 - push eax - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - add esp, 16 - test eax, eax - je LBB6_4 - add esp, 16 - pop esi - pop edi - pop ebp - ret -LBB6_1: - mov byte ptr [ebp - 9], 1 - lea eax, [ebp - 9] - mov dword ptr [ebp - 16], eax - sub esp, 12 - lea eax, [esi + l_anon.[ID].16-L6$pb] - lea ecx, [esi + l_anon.[ID].0-L6$pb] - lea edx, [ebp - 16] - lea edi, [esi + SYM(::class::REGISTER_CLASS, 0)-L6$pb] - push eax - push ecx - push edx - push 0 - push edi - call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - add esp, 32 - jmp LBB6_2 -LBB6_4: - sub esp, 4 - lea eax, [esi + l_anon.[ID].16-L6$pb] - lea ecx, [esi + l_anon.[ID].8-L6$pb] - push eax - push 43 - push ecx - call SYM(core::panicking::panic::GENERATED_ID, 0) - - .p2align 4, 0x90 -SYM(::class::{closure#0}::__objc2_dealloc, 0): - push ebp - mov ebp, esp - push ebx - push edi - push esi - sub esp, 12 - call L7$pb -L7$pb: - pop ebx - mov esi, dword ptr [ebp + 12] - mov edi, dword ptr [ebp + 8] - sub esp, 12 - push edi - call _object_getClass - add esp, 16 - lea ecx, [ebx + l_anon.[ID].2-L7$pb] - lea edx, [ebx + L_anon.[ID].14-L7$pb] - push ecx - push 4 - push edx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov eax, dword ptr [edi + eax] - test eax, eax - je LBB7_2 - sub esp, 12 - push eax - call _objc_release - add esp, 16 -LBB7_2: - mov eax, dword ptr [ebx + LL_OBJC_CLASS_REFERENCES_NSObject$non_lazy_ptr-L7$pb] - mov eax, dword ptr [eax] - mov dword ptr [ebp - 20], edi - mov dword ptr [ebp - 16], eax - sub esp, 8 - lea eax, [ebp - 20] - push esi - push eax - call _objc_msgSendSuper - add esp, 28 - pop esi - pop edi - pop ebx - pop ebp - ret - - .globl _init - .p2align 4, 0x90 -_init: - push ebp - mov ebp, esp - push edi - push esi - sub esp, 16 - call L8$pb -L8$pb: - pop edi - mov eax, dword ptr [ebp + 8] - mov ecx, dword ptr [edi + LL_OBJC_SELECTOR_REFERENCES_init$non_lazy_ptr-L8$pb] - mov ecx, dword ptr [ecx] - mov edx, dword ptr [edi + LL_OBJC_CLASS_REFERENCES_NSObject$non_lazy_ptr-L8$pb] - mov edx, dword ptr [edx] - mov dword ptr [ebp - 16], eax - mov dword ptr [ebp - 12], edx - sub esp, 8 - lea eax, [ebp - 16] - push ecx - push eax - call _objc_msgSendSuper - add esp, 16 - mov esi, eax - test eax, eax - je LBB8_2 - sub esp, 12 - push esi - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].13-L8$pb] - lea edx, [edi + L_anon.[ID].12-L8$pb] - push ecx - push 4 - push edx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov byte ptr [esi + eax], 42 - sub esp, 12 - push esi - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].2-L8$pb] - lea edx, [edi + L_anon.[ID].14-L8$pb] - push ecx - push 4 - push edx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov dword ptr [esi + eax], 0 -LBB8_2: - mov eax, esi - add esp, 16 - pop esi - pop edi - pop ebp - ret - - .globl _class_method - .p2align 4, 0x90 -_class_method: - push ebp - mov ebp, esp - pop ebp - ret - - .globl _method - .p2align 4, 0x90 -_method: - push ebp - mov ebp, esp - pop ebp - ret - - .globl _method_bool - .p2align 4, 0x90 -_method_bool: - push ebp - mov ebp, esp - xor eax, eax - cmp byte ptr [ebp + 16], 0 - sete al - pop ebp - ret - - .globl _method_id - .p2align 4, 0x90 -_method_id: - push ebp - mov ebp, esp - push edi - push esi - call L12$pb -L12$pb: - pop edi - mov esi, dword ptr [ebp + 8] - sub esp, 12 - push esi - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].2-L12$pb] - lea edx, [edi + L_anon.[ID].14-L12$pb] - push ecx - push 4 - push edx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov eax, dword ptr [esi + eax] - test eax, eax - je LBB12_1 - sub esp, 12 - push eax - call _objc_retain - add esp, 16 - jmp LBB12_3 -LBB12_1: - xor eax, eax -LBB12_3: - sub esp, 12 - push eax - call _objc_autoreleaseReturnValue - add esp, 16 - pop esi - pop edi - pop ebp - ret - - .globl _method_id_with_param - .p2align 4, 0x90 -_method_id_with_param: - push ebp - mov ebp, esp - push ebx - push edi - push esi - sub esp, 28 - call L13$pb -L13$pb: - pop edi - call SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) - mov esi, eax - cmp byte ptr [ebp + 16], 0 - je LBB13_5 - mov ebx, dword ptr [ebp + 8] - mov dword ptr [esp], ebx - call _object_getClass - lea ecx, [edi + l_anon.[ID].2-L13$pb] - mov dword ptr [esp + 12], ecx - lea ecx, [edi + L_anon.[ID].14-L13$pb] - mov dword ptr [esp + 4], ecx - mov dword ptr [esp], eax - mov dword ptr [esp + 8], 4 - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov eax, dword ptr [ebx + eax] - test eax, eax - je LBB13_2 - mov dword ptr [esp], eax - call _objc_retain - mov edi, eax - jmp LBB13_4 -LBB13_2: - xor edi, edi -LBB13_4: - mov dword ptr [esp], esi - call _objc_release - mov esi, edi -LBB13_5: - mov dword ptr [esp], esi - call _objc_autoreleaseReturnValue - add esp, 28 - pop esi - pop edi - pop ebx - pop ebp - ret - - .globl _copyWithZone - .p2align 4, 0x90 -_copyWithZone: - push ebp - mov ebp, esp - push ebx - push edi - push esi - sub esp, 12 - call L14$pb -L14$pb: - pop edi - call _get_obj - mov esi, eax - test eax, eax - je LBB14_5 - mov eax, dword ptr [ebp + 8] - sub esp, 12 - push eax - mov ebx, eax - call _object_getClass - add esp, 16 - lea edx, [edi + l_anon.[ID].13-L14$pb] - lea ecx, [edi + L_anon.[ID].12-L14$pb] - push edx - push 4 - push ecx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - movzx ebx, byte ptr [ebx + eax] - sub esp, 12 - push esi - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].13-L14$pb] - push ecx - push 4 - lea ecx, [edi + L_anon.[ID].12-L14$pb] - push ecx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov byte ptr [esi + eax], bl - sub esp, 12 - push dword ptr [ebp + 8] - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].2-L14$pb] - lea ebx, [edi + L_anon.[ID].14-L14$pb] - mov dword ptr [ebp - 16], ecx - push ecx - push 4 - push ebx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov ecx, dword ptr [ebp + 8] - mov eax, dword ptr [ecx + eax] - test eax, eax - je LBB14_2 - sub esp, 12 - push eax - call _objc_retain - add esp, 16 - mov edi, eax - jmp LBB14_4 -LBB14_2: - xor edi, edi -LBB14_4: - sub esp, 12 - push esi - call _object_getClass - add esp, 16 - push dword ptr [ebp - 16] - push 4 - push ebx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov dword ptr [esi + eax], edi -LBB14_5: - mov eax, esi - add esp, 12 - pop esi - pop edi - pop ebx - pop ebp - ret - - .section __DATA,__const - .p2align 2, 0x0 -l_anon.[ID].0: - .long SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0) - .asciz "\004\000\000\000\004\000\000" - .long 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) - .long SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) - - .section __TEXT,__const - .p2align 2, 0x0 -l_anon.[ID].1: - .byte 0 - - .p2align 2, 0x0 -l_anon.[ID].2: - .byte 19 - .space 19 - - .p2align 2, 0x0 -l_anon.[ID].3: - .byte 17 - .space 19 - - .p2align 2, 0x0 -l_anon.[ID].4: - .space 1 - .space 19 - -l_anon.[ID].5: - .ascii "_NSZone" - - .section __DATA,__const - .p2align 2, 0x0 -l_anon.[ID].6: - .byte 28 - .space 3 - .long l_anon.[ID].5 - .asciz "\007\000\000" - .long l_anon.[ID].1 - .space 4 - - .p2align 2, 0x0 -l_anon.[ID].7: - .byte 25 - .space 3 - .long l_anon.[ID].6 - .space 12 - - .section __TEXT,__const -l_anon.[ID].8: - .ascii "called `Option::unwrap()` on a `None` value" - -l_anon.[ID].9: - .ascii "$RUSTC/library/std/src/sync/once.rs" - - .section __DATA,__const - .p2align 2, 0x0 -l_anon.[ID].10: - .long l_anon.[ID].9 - .asciz "p\000\000\000\225\000\000\0002\000\000" - - .section __TEXT,__const -l_anon.[ID].11: - .ascii "CustomClassName" - - .section __TEXT,__literal4,4byte_literals -L_anon.[ID].12: - .ascii "_foo" - - .section __TEXT,__const - .p2align 2, 0x0 -l_anon.[ID].13: - .byte 5 - .space 19 - - .section __TEXT,__literal4,4byte_literals -L_anon.[ID].14: - .ascii "_obj" - - .section __TEXT,__const -l_anon.[ID].15: - .ascii "crates/$DIR/lib.rs" - - .section __DATA,__const - .p2align 2, 0x0 -l_anon.[ID].16: - .long l_anon.[ID].15 - .asciz "5\000\000\000\f\000\000\000\001\000\000" - -.zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),4,2 - .section __TEXT,__const -l_anon.[ID].17: - .ascii "NSCopying" - - .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_d874ee9262978be2 -L_OBJC_METH_VAR_NAME_d874ee9262978be2: - .asciz "classMethod" - - .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2 - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2: - .long L_OBJC_METH_VAR_NAME_d874ee9262978be2 - - .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_d874ee9262978be2 - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_d874ee9262978be2: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc -L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc: - .asciz "method" - - .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc: - .long L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc - - .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_4539fd1dbda0cddc - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_4539fd1dbda0cddc: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 -L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5: - .asciz "methodBool:" - - .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5 - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5: - .long L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 - - .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5 - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f7f521670860b0ce -L_OBJC_METH_VAR_NAME_f7f521670860b0ce: - .asciz "methodId" - - .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce: - .long L_OBJC_METH_VAR_NAME_f7f521670860b0ce - - .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_f7f521670860b0ce - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f7f521670860b0ce: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_6addfcf634c6232f -L_OBJC_METH_VAR_NAME_6addfcf634c6232f: - .asciz "methodIdWithParam:" - - .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f: - .long L_OBJC_METH_VAR_NAME_6addfcf634c6232f - - .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_6addfcf634c6232f - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_6addfcf634c6232f: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 -L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166: - .asciz "copyWithZone:" - - .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166 - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166: - .long L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 - - .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_4a8c690dbc9d8166 - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_4a8c690dbc9d8166: - .asciz "\000\000\000\000@\000\000" - - .section __IMPORT,__pointers,non_lazy_symbol_pointers -LL_OBJC_CLASS_REFERENCES_NSObject$non_lazy_ptr: - .indirect_symbol L_OBJC_CLASS_REFERENCES_NSObject - .long 0 -LL_OBJC_SELECTOR_REFERENCES_dealloc$non_lazy_ptr: - .indirect_symbol L_OBJC_SELECTOR_REFERENCES_dealloc - .long 0 -LL_OBJC_SELECTOR_REFERENCES_init$non_lazy_ptr: - .indirect_symbol L_OBJC_SELECTOR_REFERENCES_init - .long 0 -LL_OBJC_SELECTOR_REFERENCES_new$non_lazy_ptr: - .indirect_symbol L_OBJC_SELECTOR_REFERENCES_new - .long 0 - .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_declare_class/expected/apple-x86.s b/crates/test-assembly/crates/test_declare_class/expected/apple-x86.s index 340c9a0e3..d3cea7aab 100644 --- a/crates/test-assembly/crates/test_declare_class/expected/apple-x86.s +++ b/crates/test-assembly/crates/test_declare_class/expected/apple-x86.s @@ -1,911 +1,3 @@ .section __TEXT,__text,regular,pure_instructions .intel_syntax noprefix - .p2align 4, 0x90 -SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0): - push ebp - mov ebp, esp - pop ebp - ret - - .p2align 4, 0x90 -SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0): - push ebp - mov ebp, esp - push ebx - push edi - push esi - sub esp, 12 - call L1$pb -L1$pb: - pop esi - mov eax, dword ptr [ebp + 8] - mov eax, dword ptr [eax] - cmp byte ptr [eax], 0 - mov byte ptr [eax], 0 - je LBB1_5 - mov eax, dword ptr [esi + LL_OBJC_CLASSLIST_REFERENCES_$_NSObject$non_lazy_ptr-L1$pb] - sub esp, 4 - lea edi, [esi + l_anon.[ID].11-L1$pb] - push dword ptr [eax] - push 15 - push edi - call SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) - add esp, 16 - test eax, eax - je LBB1_6 - mov dword ptr [ebp - 16], eax - sub esp, 8 - lea eax, [esi + l_anon.[ID].13-L1$pb] - lea ecx, [esi + L_anon.[ID].12-L1$pb] - lea ebx, [ebp - 16] - push eax - push 0 - push 1 - push 4 - push ecx - push ebx - call SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) - add esp, 24 - lea ecx, [esi + l_anon.[ID].2-L1$pb] - lea eax, [esi + L_anon.[ID].14-L1$pb] - push ecx - push 2 - push 4 - push 4 - push eax - push ebx - call SYM(objc2::declare::ClassBuilder::add_ivar_inner_mono::GENERATED_ID, 0) - add esp, 32 - mov eax, dword ptr [esi + LL_OBJC_SELECTOR_REFERENCES_dealloc$non_lazy_ptr-L1$pb] - sub esp, 8 - lea ecx, [esi + SYM(::class::{closure#0}::__objc2_dealloc, 0)-L1$pb] - lea edi, [esi + l_anon.[ID].3-L1$pb] - lea edx, [esi + l_anon.[ID].1-L1$pb] - push ecx - push edi - push 0 - push edx - mov edi, edx - push dword ptr [eax] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 32 - mov eax, dword ptr [esi + LL_OBJC_SELECTOR_REFERENCES_init$non_lazy_ptr-L1$pb] - sub esp, 8 - lea ecx, [esi + _init-L1$pb] - push ecx - lea ecx, [esi + l_anon.[ID].2-L1$pb] - push ecx - push 0 - push edi - push dword ptr [eax] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 24 - lea eax, [esi + _class_method-L1$pb] - push eax - lea eax, [esi + l_anon.[ID].3-L1$pb] - push eax - push 0 - push edi - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2-L1$pb] - push ebx - call SYM(objc2::declare::ClassBuilder::add_class_method_inner::GENERATED_ID, 0) - add esp, 24 - lea eax, [esi + _method-L1$pb] - push eax - lea eax, [esi + l_anon.[ID].3-L1$pb] - push eax - push 0 - push edi - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc-L1$pb] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 24 - lea eax, [esi + _method_bool-L1$pb] - lea ecx, [esi + l_anon.[ID].4-L1$pb] - push eax - push ecx - push 1 - push ecx - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5-L1$pb] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 24 - lea eax, [esi + _method_id-L1$pb] - push eax - lea edi, [esi + l_anon.[ID].2-L1$pb] - push edi - push 0 - lea eax, [esi + l_anon.[ID].1-L1$pb] - push eax - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce-L1$pb] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 24 - lea eax, [esi + _method_id_with_param-L1$pb] - push eax - push edi - push 1 - lea eax, [esi + l_anon.[ID].4-L1$pb] - push eax - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f-L1$pb] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 24 - lea eax, [esi + l_anon.[ID].17-L1$pb] - push 9 - push eax - call SYM(objc2::runtime::AnyProtocol::get::GENERATED_ID, 0) - add esp, 16 - test eax, eax - je LBB1_4 - sub esp, 8 - push eax - push ebx - call SYM(objc2::declare::ClassBuilder::add_protocol::GENERATED_ID, 0) - add esp, 16 -LBB1_4: - sub esp, 8 - lea eax, [esi + _copyWithZone-L1$pb] - lea ecx, [esi + l_anon.[ID].7-L1$pb] - push eax - lea eax, [esi + l_anon.[ID].2-L1$pb] - push eax - push 1 - push ecx - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166-L1$pb] - push ebx - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - add esp, 20 - push dword ptr [ebp - 16] - call SYM(objc2::declare::ClassBuilder::register::GENERATED_ID, 0) - add esp, 28 - pop esi - pop edi - pop ebx - pop ebp - ret -LBB1_5: - sub esp, 4 - lea eax, [esi + l_anon.[ID].10-L1$pb] - lea ecx, [esi + l_anon.[ID].8-L1$pb] - push eax - push 43 - push ecx - call SYM(core::panicking::panic::GENERATED_ID, 0) -LBB1_6: - sub esp, 4 - lea eax, [esi + l_anon.[ID].16-L1$pb] - push eax - push 15 - push edi - call SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::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): - push ebp - mov ebp, esp - sub esp, 24 - mov eax, dword ptr [ebp + 8] - mov eax, dword ptr [eax] - mov dword ptr [ebp - 4], eax - lea eax, [ebp - 4] - mov dword ptr [esp], eax - call SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) - add esp, 24 - pop ebp - ret - - .globl _get_class - .p2align 4, 0x90 -_get_class: - push ebp - mov ebp, esp - push edi - push esi - sub esp, 16 - call L3$pb -L3$pb: - pop esi - mov eax, dword ptr [esi + SYM(::class::REGISTER_CLASS, 0)-L3$pb] - cmp eax, 3 - jne LBB3_1 -LBB3_2: - sub esp, 8 - lea eax, [esi + l_anon.[ID].11-L3$pb] - push 15 - push eax - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - add esp, 16 - test eax, eax - je LBB3_4 - add esp, 16 - pop esi - pop edi - pop ebp - ret -LBB3_1: - mov byte ptr [ebp - 9], 1 - lea eax, [ebp - 9] - mov dword ptr [ebp - 16], eax - sub esp, 12 - lea eax, [esi + l_anon.[ID].16-L3$pb] - lea ecx, [esi + l_anon.[ID].0-L3$pb] - lea edx, [ebp - 16] - lea edi, [esi + SYM(::class::REGISTER_CLASS, 0)-L3$pb] - push eax - push ecx - push edx - push 0 - push edi - call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - add esp, 32 - jmp LBB3_2 -LBB3_4: - sub esp, 4 - lea eax, [esi + l_anon.[ID].16-L3$pb] - lea ecx, [esi + l_anon.[ID].8-L3$pb] - push eax - push 43 - push ecx - call SYM(core::panicking::panic::GENERATED_ID, 0) - - .globl _get_obj - .p2align 4, 0x90 -_get_obj: - push ebp - mov ebp, esp - push esi - push eax - call L4$pb -L4$pb: - pop esi - call _get_class - mov ecx, dword ptr [esi + LL_OBJC_SELECTOR_REFERENCES_new$non_lazy_ptr-L4$pb] - sub esp, 8 - push dword ptr [ecx] - push eax - call _objc_msgSend - add esp, 20 - pop esi - pop ebp - ret - - .globl _access_ivars - .p2align 4, 0x90 -_access_ivars: - push ebp - mov ebp, esp - push ebx - push edi - push esi - sub esp, 12 - call L5$pb -L5$pb: - pop edi - call _get_obj - mov esi, eax - sub esp, 12 - push eax - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].13-L5$pb] - lea edx, [edi + L_anon.[ID].12-L5$pb] - push ecx - push 4 - push edx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - movzx ebx, byte ptr [esi + eax] - sub esp, 12 - push esi - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].2-L5$pb] - lea edx, [edi + L_anon.[ID].14-L5$pb] - push ecx - push 4 - push edx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov edi, dword ptr [esi + eax] - sub esp, 12 - push esi - call _objc_release - add esp, 16 - mov eax, ebx - mov edx, edi - add esp, 12 - pop esi - pop edi - pop ebx - pop ebp - ret - - .globl SYM(::class, 0) - .p2align 4, 0x90 -SYM(::class, 0): - push ebp - mov ebp, esp - push edi - push esi - sub esp, 16 - call L6$pb -L6$pb: - pop esi - mov eax, dword ptr [esi + SYM(::class::REGISTER_CLASS, 0)-L6$pb] - cmp eax, 3 - jne LBB6_1 -LBB6_2: - sub esp, 8 - lea eax, [esi + l_anon.[ID].11-L6$pb] - push 15 - push eax - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - add esp, 16 - test eax, eax - je LBB6_4 - add esp, 16 - pop esi - pop edi - pop ebp - ret -LBB6_1: - mov byte ptr [ebp - 9], 1 - lea eax, [ebp - 9] - mov dword ptr [ebp - 16], eax - sub esp, 12 - lea eax, [esi + l_anon.[ID].16-L6$pb] - lea ecx, [esi + l_anon.[ID].0-L6$pb] - lea edx, [ebp - 16] - lea edi, [esi + SYM(::class::REGISTER_CLASS, 0)-L6$pb] - push eax - push ecx - push edx - push 0 - push edi - call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - add esp, 32 - jmp LBB6_2 -LBB6_4: - sub esp, 4 - lea eax, [esi + l_anon.[ID].16-L6$pb] - lea ecx, [esi + l_anon.[ID].8-L6$pb] - push eax - push 43 - push ecx - call SYM(core::panicking::panic::GENERATED_ID, 0) - - .p2align 4, 0x90 -SYM(::class::{closure#0}::__objc2_dealloc, 0): - push ebp - mov ebp, esp - push ebx - push edi - push esi - sub esp, 12 - call L7$pb -L7$pb: - pop ebx - mov esi, dword ptr [ebp + 12] - mov edi, dword ptr [ebp + 8] - sub esp, 12 - push edi - call _object_getClass - add esp, 16 - lea ecx, [ebx + l_anon.[ID].2-L7$pb] - lea edx, [ebx + L_anon.[ID].14-L7$pb] - push ecx - push 4 - push edx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov eax, dword ptr [edi + eax] - test eax, eax - je LBB7_2 - sub esp, 12 - push eax - call _objc_release - add esp, 16 -LBB7_2: - mov eax, dword ptr [ebx + LL_OBJC_CLASSLIST_REFERENCES_$_NSObject$non_lazy_ptr-L7$pb] - mov eax, dword ptr [eax] - mov dword ptr [ebp - 20], edi - mov dword ptr [ebp - 16], eax - sub esp, 8 - lea eax, [ebp - 20] - push esi - push eax - call _objc_msgSendSuper - add esp, 28 - pop esi - pop edi - pop ebx - pop ebp - ret - - .globl _init - .p2align 4, 0x90 -_init: - push ebp - mov ebp, esp - push edi - push esi - sub esp, 16 - call L8$pb -L8$pb: - pop edi - mov eax, dword ptr [ebp + 8] - mov ecx, dword ptr [edi + LL_OBJC_SELECTOR_REFERENCES_init$non_lazy_ptr-L8$pb] - mov ecx, dword ptr [ecx] - mov edx, dword ptr [edi + LL_OBJC_CLASSLIST_REFERENCES_$_NSObject$non_lazy_ptr-L8$pb] - mov edx, dword ptr [edx] - mov dword ptr [ebp - 16], eax - mov dword ptr [ebp - 12], edx - sub esp, 8 - lea eax, [ebp - 16] - push ecx - push eax - call _objc_msgSendSuper - add esp, 16 - mov esi, eax - test eax, eax - je LBB8_2 - sub esp, 12 - push esi - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].13-L8$pb] - lea edx, [edi + L_anon.[ID].12-L8$pb] - push ecx - push 4 - push edx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov byte ptr [esi + eax], 42 - sub esp, 12 - push esi - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].2-L8$pb] - lea edx, [edi + L_anon.[ID].14-L8$pb] - push ecx - push 4 - push edx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov dword ptr [esi + eax], 0 -LBB8_2: - mov eax, esi - add esp, 16 - pop esi - pop edi - pop ebp - ret - - .globl _class_method - .p2align 4, 0x90 -_class_method: - push ebp - mov ebp, esp - pop ebp - ret - - .globl _method - .p2align 4, 0x90 -_method: - push ebp - mov ebp, esp - pop ebp - ret - - .globl _method_bool - .p2align 4, 0x90 -_method_bool: - push ebp - mov ebp, esp - xor eax, eax - cmp byte ptr [ebp + 16], 0 - sete al - pop ebp - ret - - .globl _method_id - .p2align 4, 0x90 -_method_id: - push ebp - mov ebp, esp - push edi - push esi - call L12$pb -L12$pb: - pop edi - mov esi, dword ptr [ebp + 8] - sub esp, 12 - push esi - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].2-L12$pb] - lea edx, [edi + L_anon.[ID].14-L12$pb] - push ecx - push 4 - push edx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov eax, dword ptr [esi + eax] - test eax, eax - je LBB12_1 - sub esp, 12 - push eax - call _objc_retain - add esp, 16 - jmp LBB12_3 -LBB12_1: - xor eax, eax -LBB12_3: - sub esp, 12 - push eax - call _objc_autoreleaseReturnValue - add esp, 16 - pop esi - pop edi - pop ebp - ret - - .globl _method_id_with_param - .p2align 4, 0x90 -_method_id_with_param: - push ebp - mov ebp, esp - push ebx - push edi - push esi - sub esp, 28 - call L13$pb -L13$pb: - pop edi - call SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) - mov esi, eax - cmp byte ptr [ebp + 16], 0 - je LBB13_5 - mov ebx, dword ptr [ebp + 8] - mov dword ptr [esp], ebx - call _object_getClass - lea ecx, [edi + l_anon.[ID].2-L13$pb] - mov dword ptr [esp + 12], ecx - lea ecx, [edi + L_anon.[ID].14-L13$pb] - mov dword ptr [esp + 4], ecx - mov dword ptr [esp], eax - mov dword ptr [esp + 8], 4 - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov eax, dword ptr [ebx + eax] - test eax, eax - je LBB13_2 - mov dword ptr [esp], eax - call _objc_retain - mov edi, eax - jmp LBB13_4 -LBB13_2: - xor edi, edi -LBB13_4: - mov dword ptr [esp], esi - call _objc_release - mov esi, edi -LBB13_5: - mov dword ptr [esp], esi - call _objc_autoreleaseReturnValue - add esp, 28 - pop esi - pop edi - pop ebx - pop ebp - ret - - .globl _copyWithZone - .p2align 4, 0x90 -_copyWithZone: - push ebp - mov ebp, esp - push ebx - push edi - push esi - sub esp, 12 - call L14$pb -L14$pb: - pop edi - call _get_obj - mov esi, eax - test eax, eax - je LBB14_5 - mov eax, dword ptr [ebp + 8] - sub esp, 12 - push eax - mov ebx, eax - call _object_getClass - add esp, 16 - lea edx, [edi + l_anon.[ID].13-L14$pb] - lea ecx, [edi + L_anon.[ID].12-L14$pb] - push edx - push 4 - push ecx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - movzx ebx, byte ptr [ebx + eax] - sub esp, 12 - push esi - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].13-L14$pb] - push ecx - push 4 - lea ecx, [edi + L_anon.[ID].12-L14$pb] - push ecx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov byte ptr [esi + eax], bl - sub esp, 12 - push dword ptr [ebp + 8] - call _object_getClass - add esp, 16 - lea ecx, [edi + l_anon.[ID].2-L14$pb] - lea ebx, [edi + L_anon.[ID].14-L14$pb] - mov dword ptr [ebp - 16], ecx - push ecx - push 4 - push ebx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov ecx, dword ptr [ebp + 8] - mov eax, dword ptr [ecx + eax] - test eax, eax - je LBB14_2 - sub esp, 12 - push eax - call _objc_retain - add esp, 16 - mov edi, eax - jmp LBB14_4 -LBB14_2: - xor edi, edi -LBB14_4: - sub esp, 12 - push esi - call _object_getClass - add esp, 16 - push dword ptr [ebp - 16] - push 4 - push ebx - push eax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - add esp, 16 - mov dword ptr [esi + eax], edi -LBB14_5: - mov eax, esi - add esp, 12 - pop esi - pop edi - pop ebx - pop ebp - ret - - .section __DATA,__const - .p2align 2, 0x0 -l_anon.[ID].0: - .long SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0) - .asciz "\004\000\000\000\004\000\000" - .long 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) - .long SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) - - .section __TEXT,__const - .p2align 2, 0x0 -l_anon.[ID].1: - .byte 0 - - .p2align 2, 0x0 -l_anon.[ID].2: - .byte 19 - .space 19 - - .p2align 2, 0x0 -l_anon.[ID].3: - .byte 17 - .space 19 - - .p2align 2, 0x0 -l_anon.[ID].4: - .space 1 - .space 19 - -l_anon.[ID].5: - .ascii "_NSZone" - - .section __DATA,__const - .p2align 2, 0x0 -l_anon.[ID].6: - .byte 28 - .space 3 - .long l_anon.[ID].5 - .asciz "\007\000\000" - .long l_anon.[ID].1 - .space 4 - - .p2align 2, 0x0 -l_anon.[ID].7: - .byte 25 - .space 3 - .long l_anon.[ID].6 - .space 12 - - .section __TEXT,__const -l_anon.[ID].8: - .ascii "called `Option::unwrap()` on a `None` value" - -l_anon.[ID].9: - .ascii "$RUSTC/library/std/src/sync/once.rs" - - .section __DATA,__const - .p2align 2, 0x0 -l_anon.[ID].10: - .long l_anon.[ID].9 - .asciz "p\000\000\000\225\000\000\0002\000\000" - - .section __TEXT,__const -l_anon.[ID].11: - .ascii "CustomClassName" - - .section __TEXT,__literal4,4byte_literals -L_anon.[ID].12: - .ascii "_foo" - - .section __TEXT,__const - .p2align 2, 0x0 -l_anon.[ID].13: - .byte 5 - .space 19 - - .section __TEXT,__literal4,4byte_literals -L_anon.[ID].14: - .ascii "_obj" - - .section __TEXT,__const -l_anon.[ID].15: - .ascii "crates/$DIR/lib.rs" - - .section __DATA,__const - .p2align 2, 0x0 -l_anon.[ID].16: - .long l_anon.[ID].15 - .asciz "5\000\000\000\f\000\000\000\001\000\000" - -.zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),4,2 - .section __TEXT,__const -l_anon.[ID].17: - .ascii "NSCopying" - - .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_d874ee9262978be2 -L_OBJC_METH_VAR_NAME_d874ee9262978be2: - .asciz "classMethod" - - .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2 - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2: - .long L_OBJC_METH_VAR_NAME_d874ee9262978be2 - - .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_d874ee9262978be2 - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_d874ee9262978be2: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc -L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc: - .asciz "method" - - .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc: - .long L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc - - .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_4539fd1dbda0cddc - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_4539fd1dbda0cddc: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 -L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5: - .asciz "methodBool:" - - .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5 - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5: - .long L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 - - .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5 - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f7f521670860b0ce -L_OBJC_METH_VAR_NAME_f7f521670860b0ce: - .asciz "methodId" - - .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce: - .long L_OBJC_METH_VAR_NAME_f7f521670860b0ce - - .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_f7f521670860b0ce - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f7f521670860b0ce: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_6addfcf634c6232f -L_OBJC_METH_VAR_NAME_6addfcf634c6232f: - .asciz "methodIdWithParam:" - - .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f: - .long L_OBJC_METH_VAR_NAME_6addfcf634c6232f - - .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_6addfcf634c6232f - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_6addfcf634c6232f: - .asciz "\000\000\000\000@\000\000" - - .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 -L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166: - .asciz "copyWithZone:" - - .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166 - .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166: - .long L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 - - .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_4a8c690dbc9d8166 - .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_4a8c690dbc9d8166: - .asciz "\000\000\000\000@\000\000" - - .section __IMPORT,__pointers,non_lazy_symbol_pointers -LL_OBJC_CLASSLIST_REFERENCES_$_NSObject$non_lazy_ptr: - .indirect_symbol L_OBJC_CLASSLIST_REFERENCES_$_NSObject - .long 0 -LL_OBJC_SELECTOR_REFERENCES_dealloc$non_lazy_ptr: - .indirect_symbol L_OBJC_SELECTOR_REFERENCES_dealloc - .long 0 -LL_OBJC_SELECTOR_REFERENCES_init$non_lazy_ptr: - .indirect_symbol L_OBJC_SELECTOR_REFERENCES_init - .long 0 -LL_OBJC_SELECTOR_REFERENCES_new$non_lazy_ptr: - .indirect_symbol L_OBJC_SELECTOR_REFERENCES_new - .long 0 - .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 bc4820489..20519bd6d 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 @@ -1,14 +1,69 @@ .section __TEXT,__text,regular,pure_instructions .intel_syntax noprefix .p2align 4, 0x90 -SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0): +SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0): push rbp mov rbp, rsp pop rbp ret .p2align 4, 0x90 -SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0): +SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0): + push rbp + mov rbp, rsp + pop rbp + ret + + .p2align 4, 0x90 +SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0): + push rbp + mov rbp, rsp + 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 mov rbp, rsp push r15 @@ -19,103 +74,82 @@ SYM(::call_once::<::class::{closure#0}::__objc2_dealloc, 0)] - mov rdi, rbx - mov rdx, r15 + mov rdx, r14 xor ecx, ecx - mov r8, r12 - 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 r9, [rip + _init] - mov rdi, rbx - mov rdx, r15 - xor ecx, ecx - mov r8, r14 - call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2] - lea r9, [rip + _class_method] - mov rdi, rbx - mov rdx, r15 - xor ecx, ecx - mov r8, r12 call SYM(objc2::declare::ClassBuilder::add_class_method_inner::GENERATED_ID, 0) - mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc] - lea r9, [rip + _method] + mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_cf773331f3cfba54] + lea r8, [rip + l_anon.[ID].5] + lea r9, [rip + _method_simple] mov rdi, rbx - mov rdx, r15 + mov rdx, r14 xor ecx, ecx - mov r8, r12 call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5] - lea r12, [rip + l_anon.[ID].4] + mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_abdcbb85641cd990] + lea r15, [rip + l_anon.[ID].6] lea r9, [rip + _method_bool] mov ecx, 1 mov rdi, rbx - mov rdx, r12 - mov r8, r12 + mov rdx, r15 + mov r8, r15 call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce] + mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_ef8de92414f2d9c8] + lea r12, [rip + l_anon.[ID].4] lea r9, [rip + _method_id] mov rdi, rbx - mov rdx, r15 + mov rdx, r14 xor ecx, ecx - mov r8, r14 + mov r8, r12 call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f] + mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_4a611090161f3fae] lea r9, [rip + _method_id_with_param] mov ecx, 1 mov rdi, rbx - mov rdx, r12 - mov r8, r14 + mov rdx, r15 + mov r8, r12 call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - lea rdi, [rip + l_anon.[ID].17] + lea rdi, [rip + L_anon.[ID].24] + mov esi, 8 + call SYM(objc2::runtime::AnyProtocol::get::GENERATED_ID, 0) + test rax, rax + je LBB4_4 + lea rdi, [rbp - 40] + mov rsi, rax + call SYM(objc2::declare::ClassBuilder::add_protocol::GENERATED_ID, 0) +LBB4_4: + lea rdi, [rip + l_anon.[ID].25] mov esi, 9 call SYM(objc2::runtime::AnyProtocol::get::GENERATED_ID, 0) test rax, rax - je LBB1_4 + je LBB4_6 lea rdi, [rbp - 40] mov rsi, rax call SYM(objc2::declare::ClassBuilder::add_protocol::GENERATED_ID, 0) -LBB1_4: - mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166] - lea rdx, [rip + l_anon.[ID].7] - lea r8, [rip + l_anon.[ID].2] +LBB4_6: + mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_2837f061c311eb14] + lea rdx, [rip + l_anon.[ID].9] + lea r8, [rip + l_anon.[ID].4] lea r9, [rip + _copyWithZone] lea rdi, [rbp - 40] mov ecx, 1 call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) mov rdi, qword ptr [rbp - 40] call SYM(objc2::declare::ClassBuilder::register::GENERATED_ID, 0) + mov qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 0).0], rax add rsp, 16 pop rbx pop r12 @@ -123,235 +157,370 @@ LBB1_4: pop r15 pop rbp ret -LBB1_5: - lea rdi, [rip + l_anon.[ID].8] - lea rdx, [rip + l_anon.[ID].10] +LBB4_7: + lea rdi, [rip + l_anon.[ID].19] + lea rdx, [rip + l_anon.[ID].21] mov esi, 43 call SYM(core::panicking::panic::GENERATED_ID, 0) -LBB1_6: - lea rdi, [rip + l_anon.[ID].11] - lea rdx, [rip + l_anon.[ID].16] +LBB4_8: + lea rdi, [rip + l_anon.[ID].17] + lea rdx, [rip + l_anon.[ID].23] + mov esi, 7 + call SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) + + .p2align 4, 0x90 +SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0): + push rbp + mov rbp, rsp + push r14 + push rbx + sub rsp, 64 + mov rax, qword ptr [rdi] + cmp byte ptr [rax], 0 + mov byte ptr [rax], 0 + 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].18] + mov esi, 9 + call SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) + test rax, rax + 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 rbx, [rip + l_anon.[ID].3] + lea r8, [rip + l_anon.[ID].5] + 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] + lea rdi, [rbp - 24] + mov rdx, rbx + xor ecx, ecx + 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) + 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 rbp + ret +LBB5_5: + lea rdi, [rip + l_anon.[ID].19] + lea rdx, [rip + l_anon.[ID].21] + mov esi, 43 + call SYM(core::panicking::panic::GENERATED_ID, 0) +LBB5_6: + lea rdi, [rip + l_anon.[ID].18] + lea rdx, [rip + l_anon.[ID].27] + mov esi, 9 + call SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) +LBB5_7: + call SYM(objc2::__macro_helpers::declared_ivars::register_with_ivars::get_ivar_failed::GENERATED_ID, 0) +LBB5_8: + call SYM(objc2::__macro_helpers::declared_ivars::register_with_ivars::get_drop_flag_failed::GENERATED_ID, 0) + + .p2align 4, 0x90 +SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0): + push rbp + mov rbp, rsp + push r14 + push rbx + sub rsp, 64 + mov rax, qword ptr [rdi] + cmp byte ptr [rax], 0 + mov byte ptr [rax], 0 + je LBB6_4 + mov rax, qword ptr [rip + L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPCREL] + mov rdx, qword ptr [rax] + lea rdi, [rip + l_anon.[ID].16] + mov esi, 15 + call SYM(objc2::declare::ClassBuilder::new::GENERATED_ID, 0) + test rax, rax + je LBB6_5 + mov qword ptr [rbp - 24], rax + 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] + lea rdi, [rbp - 24] + xor ecx, ecx + 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], 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) + 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, 1)], rax + add rsp, 64 + pop rbx + pop r14 + pop rbp + ret +LBB6_4: + lea rdi, [rip + l_anon.[ID].19] + lea rdx, [rip + l_anon.[ID].21] + mov esi, 43 + call SYM(core::panicking::panic::GENERATED_ID, 0) +LBB6_5: + lea rdi, [rip + l_anon.[ID].16] + lea rdx, [rip + l_anon.[ID].26] mov esi, 15 call SYM(objc2::__macro_helpers::declare_class::failed_declaring_class::GENERATED_ID, 0) +LBB6_6: + call SYM(objc2::__macro_helpers::declared_ivars::register_with_ivars::get_ivar_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): +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): push rbp mov rbp, rsp sub rsp, 16 mov rax, qword ptr [rdi] mov qword ptr [rbp - 8], rax lea rdi, [rbp - 8] - call SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) + call SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) add rsp, 16 pop rbp ret - .globl _get_class .p2align 4, 0x90 -_get_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): push rbp mov rbp, rsp sub rsp, 16 - mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] - cmp rax, 3 - jne LBB3_1 -LBB3_2: - lea rdi, [rip + l_anon.[ID].11] - mov esi, 15 - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - test rax, rax - je LBB3_4 + mov rax, qword ptr [rdi] + mov qword ptr [rbp - 8], rax + lea rdi, [rbp - 8] + call SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) add rsp, 16 pop rbp ret -LBB3_1: + + .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): + push rbp + mov rbp, rsp + sub rsp, 16 + mov rax, qword ptr [rdi] + mov qword ptr [rbp - 8], rax + lea rdi, [rbp - 8] + call SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) + add rsp, 16 + pop rbp + ret + + .globl _access_forgetable_ivars_class + .p2align 4, 0x90 +_access_forgetable_ivars_class: + mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] + cmp rax, 3 + jne LBB10_1 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 2).0] + ret +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].0] - lea r8, [rip + l_anon.[ID].16] + lea rdi, [rip + SYM(::class::REGISTER_CLASS, 0)] + lea rcx, [rip + l_anon.[ID].2] + lea r8, [rip + l_anon.[ID].26] lea rdx, [rbp - 16] xor esi, esi call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - jmp LBB3_2 -LBB3_4: - lea rdi, [rip + l_anon.[ID].8] - lea rdx, [rip + l_anon.[ID].16] - 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 _get_obj + .globl _access_forgetable_ivars .p2align 4, 0x90 -_get_obj: +_access_forgetable_ivars: push rbp mov rbp, rsp - call _get_class - mov rcx, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_new@GOTPCREL] - mov rsi, qword ptr [rcx] - mov rdi, rax + mov rcx, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_IVAR_OFFSET, 1)] + movzx eax, byte ptr [rdi + rcx + 4] + mov edx, dword ptr [rdi + rcx] pop rbp - jmp _objc_msgSend + ret - .globl _access_ivars + .globl SYM(::drop, 0) .p2align 4, 0x90 -_access_ivars: +SYM(::drop, 0): push rbp mov rbp, rsp - push r15 - push r14 - push rbx - push rax - call _get_obj - mov rbx, rax - mov rdi, rax - call _object_getClass - lea rsi, [rip + L_anon.[ID].12] - lea rcx, [rip + l_anon.[ID].13] - mov edx, 4 - mov rdi, rax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - movzx r14d, byte ptr [rbx + rax] - mov rdi, rbx - call _object_getClass - lea rsi, [rip + L_anon.[ID].14] - lea rcx, [rip + l_anon.[ID].2] - mov edx, 4 - mov rdi, rax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov r15, qword ptr [rbx + rax] - mov rdi, rbx - call _objc_release - mov eax, r14d - mov rdx, r15 - add rsp, 8 - pop rbx - pop r14 - pop r15 + ## InlineAsm Start + ## InlineAsm End pop rbp ret - .globl SYM(::class, 0) + .globl _access_drop_ivars_class .p2align 4, 0x90 -SYM(::class, 0): +_access_drop_ivars_class: + mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] + cmp rax, 3 + jne LBB13_1 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 1).0] + ret +LBB13_1: push rbp mov rbp, rsp sub rsp, 16 - mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] - cmp rax, 3 - jne LBB6_1 -LBB6_2: - lea rdi, [rip + l_anon.[ID].11] - mov esi, 15 - call SYM(objc2::runtime::AnyClass::get::GENERATED_ID, 0) - test rax, rax - je LBB6_4 - add rsp, 16 - pop rbp - ret -LBB6_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].16] + lea rdi, [rip + SYM(::class::REGISTER_CLASS, 0)] + lea rcx, [rip + l_anon.[ID].1] + lea r8, [rip + l_anon.[ID].27] lea rdx, [rbp - 16] xor esi, esi call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) - jmp LBB6_2 -LBB6_4: - lea rdi, [rip + l_anon.[ID].8] - lea rdx, [rip + l_anon.[ID].16] - 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 -SYM(::class::{closure#0}::__objc2_dealloc, 0): +_access_drop_ivars: push rbp mov rbp, rsp - push r14 - push rbx - sub rsp, 16 - mov rbx, rsi - mov r14, rdi - call _object_getClass - lea rsi, [rip + L_anon.[ID].14] - lea rcx, [rip + l_anon.[ID].2] - mov edx, 4 - mov rdi, rax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov rdi, qword ptr [r14 + rax] - test rdi, rdi - je LBB7_2 - call _objc_release -LBB7_2: - 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 + 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 _init + .globl SYM(::class, 0) .p2align 4, 0x90 -_init: +SYM(::class, 0): + mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] + cmp rax, 3 + jne LBB15_1 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 0).0] + ret +LBB15_1: 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] - mov rax, qword ptr [rip + L_OBJC_CLASSLIST_REFERENCES_$_NSObject@GOTPCREL] - mov rax, qword ptr [rax] - mov qword ptr [rbp - 24], rdi + sub rsp, 16 + mov byte ptr [rbp - 1], 1 + lea rax, [rbp - 1] mov qword ptr [rbp - 16], rax - lea rdi, [rbp - 24] - call _objc_msgSendSuper - mov rbx, rax - test rax, rax - je LBB8_2 - mov rdi, rbx - call _object_getClass - lea rsi, [rip + L_anon.[ID].12] - lea rcx, [rip + l_anon.[ID].13] - mov edx, 4 - mov rdi, rax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov byte ptr [rbx + rax], 42 - mov rdi, rbx - call _object_getClass - lea rsi, [rip + L_anon.[ID].14] - lea rcx, [rip + l_anon.[ID].2] - mov edx, 4 - mov rdi, rax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov qword ptr [rbx + rax], 0 -LBB8_2: - mov rax, rbx - add rsp, 24 - pop rbx + lea rdi, [rip + SYM(::class::REGISTER_CLASS, 0)] + lea rcx, [rip + l_anon.[ID].0] + lea r8, [rip + l_anon.[ID].23] + lea rdx, [rbp - 16] + xor esi, esi + call SYM(std::sys_common::once::queue::Once::call::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 _class_method + .globl _get_class .p2align 4, 0x90 -_class_method: +_get_class: + mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] + cmp rax, 3 + jne LBB16_1 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 0).0] + ret +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].23] + lea rdx, [rbp - 16] + xor esi, esi + call SYM(std::sys_common::once::queue::Once::call::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 + .globl _method_simple .p2align 4, 0x90 -_method: +_method_simple: push rbp mov rbp, rsp pop rbp @@ -373,138 +542,202 @@ _method_bool: _method_id: push rbp mov rbp, rsp - push rbx - push rax - mov rbx, rdi - call _object_getClass - lea rsi, [rip + L_anon.[ID].14] - lea rcx, [rip + l_anon.[ID].2] - mov edx, 4 - mov rdi, rax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov rdi, qword ptr [rbx + rax] - test rdi, rdi - je LBB12_1 - call _objc_retain + sub rsp, 16 + mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] + cmp rax, 3 + 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 - add rsp, 8 - pop rbx - pop rbp - jmp _objc_autoreleaseReturnValue -LBB12_1: - xor edi, edi - add rsp, 8 - pop rbx + call _objc_autoreleaseReturnValue + add rsp, 16 pop rbp - jmp _objc_autoreleaseReturnValue + ret +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].23] + lea rdx, [rbp - 16] + xor esi, esi + call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + jmp LBB19_2 .globl _method_id_with_param .p2align 4, 0x90 _method_id_with_param: push rbp mov rbp, rsp - push r15 push r14 push rbx - push rax - mov r15d, edx - mov r14, rdi + mov r14d, edx call SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) mov rbx, rax - test r15b, r15b - je LBB13_5 - mov rdi, r14 - call _object_getClass - lea rsi, [rip + L_anon.[ID].14] - lea rcx, [rip + l_anon.[ID].2] - mov edx, 4 - mov rdi, rax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov rdi, qword ptr [r14 + rax] - test rdi, rdi - je LBB13_2 - call _objc_retain + test r14b, r14b + je LBB20_2 + call SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) mov r14, rax - jmp LBB13_4 -LBB13_2: - xor r14d, r14d -LBB13_4: mov rdi, rbx call _objc_release mov rbx, r14 -LBB13_5: +LBB20_2: mov rdi, rbx - add rsp, 8 pop rbx pop r14 - pop r15 pop rbp jmp _objc_autoreleaseReturnValue .globl _copyWithZone .p2align 4, 0x90 _copyWithZone: + 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: + 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 +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].23] + lea rdx, [rbp - 16] + xor esi, esi + call SYM(std::sys_common::once::queue::Once::call::GENERATED_ID, 0) + jmp LBB21_2 + + .globl SYM(::class, 0) + .p2align 4, 0x90 +SYM(::class, 0): + mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] + cmp rax, 3 + jne LBB22_1 + mov rax, qword ptr [rip + SYM(test_declare_class[CRATE_ID]::_::__OBJC2_CLASS, 2).0] + ret +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].26] + lea rdx, [rbp - 16] + xor esi, esi + call SYM(std::sys_common::once::queue::Once::call::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 _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, 1)] + mov dword ptr [rdi + rax], 43 + mov byte ptr [rdi + rax + 4], 42 +LBB23_2: + push rbp + mov rbp, rsp + 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 - 16], rdi + mov qword ptr [rbp - 8], rax + lea rdi, [rbp - 16] + call _objc_msgSendSuper + add rsp, 16 + pop rbp + ret + + .globl SYM(::class, 0) + .p2align 4, 0x90 +SYM(::class, 0): + mov rax, qword ptr [rip + SYM(::class::REGISTER_CLASS, 0)] + cmp rax, 3 + jne LBB24_1 + 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].27] + lea rdx, [rbp - 16] + xor esi, esi + call SYM(std::sys_common::once::queue::Once::call::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 _init_drop_ivars + .p2align 4, 0x90 +_init_drop_ivars: push rbp mov rbp, rsp push r15 push r14 - push r13 - push r12 push rbx - push rax - mov r14, rdi - call _get_obj - mov rbx, rax - test rax, rax - je LBB14_5 - mov rdi, r14 - call _object_getClass - lea r15, [rip + L_anon.[ID].12] - lea r12, [rip + l_anon.[ID].13] - mov edx, 4 - mov rdi, rax - mov rsi, r15 - mov rcx, r12 - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - movzx r13d, byte ptr [r14 + rax] - mov rdi, rbx - call _object_getClass - mov edx, 4 - mov rdi, rax - mov rsi, r15 - mov rcx, r12 - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov byte ptr [rbx + rax], r13b - mov rdi, r14 - call _object_getClass - lea rsi, [rip + L_anon.[ID].14] - lea rcx, [rip + l_anon.[ID].2] - mov edx, 4 - mov rdi, rax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) - mov rdi, qword ptr [r14 + rax] - test rdi, rdi - je LBB14_2 - call _objc_retain + sub rsp, 24 + mov rbx, rdi + call SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) mov r14, rax - jmp LBB14_4 -LBB14_2: - xor r14d, r14d -LBB14_4: - mov rdi, rbx - call _object_getClass - lea rsi, [rip + L_anon.[ID].14] - lea rcx, [rip + l_anon.[ID].2] - mov edx, 4 - mov rdi, rax - call SYM(objc2::runtime::ivar_offset::GENERATED_ID, 0) + call SYM(objc2::runtime::nsobject::NSObject::new::GENERATED_ID, 0) + 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 -LBB14_5: - mov rax, rbx - add rsp, 8 + 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 r12 - pop r13 pop r14 pop r15 pop rbp @@ -513,197 +746,295 @@ LBB14_5: .section __DATA,__const .p2align 3, 0x0 l_anon.[ID].0: - .quad SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0) + .quad SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0) .asciz "\b\000\000\000\000\000\000\000\b\000\000\000\000\000\000" - .quad 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) - .quad SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) + .quad 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) + .quad SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) - .section __TEXT,__const .p2align 3, 0x0 l_anon.[ID].1: - .byte 0 + .quad SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0) + .asciz "\b\000\000\000\000\000\000\000\b\000\000\000\000\000\000" + .quad 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) + .quad SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) .p2align 3, 0x0 l_anon.[ID].2: + .quad SYM(core[CRATE_ID]::ptr::drop_in_place::<::call_once<::class::{closure#0}>::{closure#0}>, 0) + .asciz "\b\000\000\000\000\000\000\000\b\000\000\000\000\000\000" + .quad 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) + .quad SYM(::call_once::<::class::{closure#0}>::{closure#0}, 0) + + .section __TEXT,__const + .p2align 3, 0x0 +l_anon.[ID].3: + .byte 0 + + .p2align 3, 0x0 +l_anon.[ID].4: .byte 19 .space 39 .p2align 3, 0x0 -l_anon.[ID].3: +l_anon.[ID].5: .byte 17 .space 39 .p2align 3, 0x0 -l_anon.[ID].4: +l_anon.[ID].6: .space 1 .space 39 -l_anon.[ID].5: +l_anon.[ID].7: .ascii "_NSZone" .section __DATA,__const .p2align 3, 0x0 -l_anon.[ID].6: +l_anon.[ID].8: .byte 28 .space 7 - .quad l_anon.[ID].5 + .quad l_anon.[ID].7 .asciz "\007\000\000\000\000\000\000" - .quad l_anon.[ID].1 + .quad l_anon.[ID].3 .space 8 .p2align 3, 0x0 -l_anon.[ID].7: +l_anon.[ID].9: .byte 25 .space 7 - .quad l_anon.[ID].6 + .quad l_anon.[ID].8 .space 24 .section __TEXT,__const -l_anon.[ID].8: - .ascii "called `Option::unwrap()` on a `None` value" - -l_anon.[ID].9: - .ascii "$RUSTC/library/std/src/sync/once.rs" - - .section __DATA,__const .p2align 3, 0x0 l_anon.[ID].10: - .quad l_anon.[ID].9 - .asciz "p\000\000\000\000\000\000\000\225\000\000\0002\000\000" + .byte 21 + .space 39 - .section __TEXT,__const l_anon.[ID].11: - .ascii "CustomClassName" + .ascii "ivars" - .section __TEXT,__literal4,4byte_literals -L_anon.[ID].12: - .ascii "_foo" +l_anon.[ID].12: + .ascii "drop_flag" - .section __TEXT,__const .p2align 3, 0x0 l_anon.[ID].13: .byte 5 .space 39 - .section __TEXT,__literal4,4byte_literals -L_anon.[ID].14: - .ascii "_obj" + .p2align 3, 0x0 +l_anon.[ID].14: + .byte 7 + .space 39 - .section __TEXT,__const + .p2align 3, 0x0 l_anon.[ID].15: + .byte 9 + .space 39 + +l_anon.[ID].16: + .ascii "ForgetableIvars" + +l_anon.[ID].17: + .ascii "NoIvars" + +l_anon.[ID].18: + .ascii "DropIvars" + +l_anon.[ID].19: + .ascii "called `Option::unwrap()` on a `None` value" + +l_anon.[ID].20: + .ascii "$RUSTC/library/std/src/sync/once.rs" + + .section __DATA,__const + .p2align 3, 0x0 +l_anon.[ID].21: + .quad l_anon.[ID].20 + .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, 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, 1) +.zerofill __DATA,__common,SYM(test_declare_class[CRATE_ID]::_::__OBJC2_DROP_FLAG_OFFSET, 1),8,3 + .section __TEXT,__const +l_anon.[ID].22: .ascii "crates/$DIR/lib.rs" .section __DATA,__const .p2align 3, 0x0 -l_anon.[ID].16: - .quad l_anon.[ID].15 - .asciz "5\000\000\000\000\000\000\000\f\000\000\000\001\000\000" +l_anon.[ID].23: + .quad l_anon.[ID].22 + .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].24: + .ascii "NSObject" -.zerofill __DATA,__bss,SYM(::class::REGISTER_CLASS, 0),8,3 .section __TEXT,__const -l_anon.[ID].17: +l_anon.[ID].25: .ascii "NSCopying" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_d874ee9262978be2 -L_OBJC_METH_VAR_NAME_d874ee9262978be2: + .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_d874ee9262978be2 + .globl L_OBJC_SELECTOR_REFERENCES_03fd85b0462f54e9 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2: - .quad L_OBJC_METH_VAR_NAME_d874ee9262978be2 +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_d874ee9262978be2 + .globl L_OBJC_IMAGE_INFO_03fd85b0462f54e9 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_d874ee9262978be2: +L_OBJC_IMAGE_INFO_03fd85b0462f54e9: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc -L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc: + .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_4539fd1dbda0cddc + .globl L_OBJC_SELECTOR_REFERENCES_cf773331f3cfba54 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc: - .quad L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc +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_4539fd1dbda0cddc + .globl L_OBJC_IMAGE_INFO_cf773331f3cfba54 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_4539fd1dbda0cddc: +L_OBJC_IMAGE_INFO_cf773331f3cfba54: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 -L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5: + .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_2b1b3a94e0ece2e5 + .globl L_OBJC_SELECTOR_REFERENCES_abdcbb85641cd990 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5: - .quad L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 +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_2b1b3a94e0ece2e5 + .globl L_OBJC_IMAGE_INFO_abdcbb85641cd990 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5: +L_OBJC_IMAGE_INFO_abdcbb85641cd990: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f7f521670860b0ce -L_OBJC_METH_VAR_NAME_f7f521670860b0ce: + .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_f7f521670860b0ce + .globl L_OBJC_SELECTOR_REFERENCES_ef8de92414f2d9c8 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce: - .quad L_OBJC_METH_VAR_NAME_f7f521670860b0ce +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_f7f521670860b0ce + .globl L_OBJC_IMAGE_INFO_ef8de92414f2d9c8 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f7f521670860b0ce: +L_OBJC_IMAGE_INFO_ef8de92414f2d9c8: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_6addfcf634c6232f -L_OBJC_METH_VAR_NAME_6addfcf634c6232f: + .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_6addfcf634c6232f + .globl L_OBJC_SELECTOR_REFERENCES_4a611090161f3fae .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f: - .quad L_OBJC_METH_VAR_NAME_6addfcf634c6232f +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_6addfcf634c6232f + .globl L_OBJC_IMAGE_INFO_4a611090161f3fae .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_6addfcf634c6232f: +L_OBJC_IMAGE_INFO_4a611090161f3fae: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 -L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166: + .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_4a8c690dbc9d8166 + .globl L_OBJC_SELECTOR_REFERENCES_2837f061c311eb14 + .p2align 3, 0x0 +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_2837f061c311eb14 + .p2align 2, 0x0 +L_OBJC_IMAGE_INFO_2837f061c311eb14: + .asciz "\000\000\000\000@\000\000" + +.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, 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, 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].26: + .quad l_anon.[ID].22 + .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_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].22 + .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_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166: - .quad L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 +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_4a8c690dbc9d8166 + .globl L_OBJC_IMAGE_INFO_6edddcebbded8f32 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_4a8c690dbc9d8166: +L_OBJC_IMAGE_INFO_6edddcebbded8f32: .asciz "\000\000\000\000@\000\000" .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 ea181ca05..e65c67d18 100644 --- a/crates/test-assembly/crates/test_declare_class/lib.rs +++ b/crates/test-assembly/crates/test_declare_class/lib.rs @@ -1,51 +1,38 @@ //! Test assembly output of `declare_class!`. #![deny(unsafe_op_in_unsafe_fn)] +// Limit to Apple targets only, since we don't particularly care about GNUStep code-size for now. #![cfg(feature = "apple")] -use core::ptr::{self}; +// Limit to 64-bit since we don't do anything special on other targets, and the assembly files are _huge_. +#![cfg(target_pointer_width = "64")] +use core::ptr; -use icrate::Foundation::{NSCopying, NSObject}; -use objc2::declare::{Ivar, IvarDrop, IvarEncode}; -use objc2::rc::Id; -use objc2::runtime::{AnyClass, NSZone}; -use objc2::{declare_class, msg_send, msg_send_id, mutability, ClassType}; +use icrate::Foundation::{NSCopying, NSObject, NSObjectProtocol, NSZone}; +use objc2::rc::{Allocated, Id}; +use objc2::runtime::AnyClass; +use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass}; declare_class!( #[no_mangle] - pub struct Custom { - foo: IvarEncode, - obj: IvarDrop>, "_obj">, - } - - mod ivars; + pub struct NoIvars; - unsafe impl ClassType for Custom { + unsafe impl ClassType for NoIvars { type Super = NSObject; type Mutability = mutability::InteriorMutable; - const NAME: &'static str = "CustomClassName"; + const NAME: &'static str = "NoIvars"; } - unsafe impl Custom { - #[no_mangle] - #[method(init)] - unsafe fn init(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.obj, None); - let this: *mut Self = this; - this - }) - .unwrap_or_else(ptr::null_mut) - } + impl DeclaredClass for NoIvars {} + unsafe impl NoIvars { #[no_mangle] #[method(classMethod)] - fn class_method() {} + fn get_class() -> &'static AnyClass { + Self::class() + } #[no_mangle] #[method(method)] - fn method(&self) {} + fn method_simple(&self) {} #[no_mangle] #[method(methodBool:)] @@ -56,60 +43,129 @@ declare_class!( #[no_mangle] #[method_id(methodId)] fn method_id(&self) -> Option> { - self.obj.clone() + unsafe { msg_send_id![Self::class(), new] } } // Test that `objc_autoreleaseReturnValue` is tail-called #[no_mangle] #[method_id(methodIdWithParam:)] fn method_id_with_param(&self, param: bool) -> Option> { - // Explicitly create outside condition + // Intentionally create this outside condition let obj = NSObject::new(); if param { - self.obj.clone() + Some(NSObject::new()) } else { Some(obj) } } } - unsafe impl NSCopying for Custom { + unsafe impl NSObjectProtocol for NoIvars {} + + unsafe impl NSCopying for NoIvars { #[no_mangle] #[method_id(copyWithZone:)] fn copyWithZone(&self, _zone: *const NSZone) -> Option> { - get_obj().map(|new| { - let hack = Id::as_ptr(&new) as *mut Self; - let hack = unsafe { &mut *hack }; - - Ivar::write(&mut hack.foo, *self.foo); - Ivar::write(&mut hack.obj, self.obj.clone()); - new - }) + unsafe { msg_send_id![Self::class(), new] } } } ); -#[no_mangle] -#[inline(never)] -pub fn get_class() -> &'static AnyClass { - Custom::class() +pub struct ForgetableIvarsIvars { + foo: u8, + bar: u32, } -#[no_mangle] -#[inline(never)] -pub fn get_obj() -> Option> { - unsafe { msg_send_id![get_class(), new] } +declare_class!( + #[no_mangle] + pub struct ForgetableIvars; + + unsafe impl ClassType for ForgetableIvars { + type Super = NSObject; + type Mutability = mutability::InteriorMutable; + const NAME: &'static str = "ForgetableIvars"; + } + + impl DeclaredClass for ForgetableIvars { + type Ivars = ForgetableIvarsIvars; + } + + unsafe impl ForgetableIvars { + #[no_mangle] + #[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] } + } + } +); + +impl ForgetableIvars { + #[no_mangle] + pub fn access_forgetable_ivars_class() -> &'static AnyClass { + Self::class() + } + + #[no_mangle] + pub fn access_forgetable_ivars(&self) -> (u8, u32) { + (self.ivars().foo, self.ivars().bar) + } } -#[no_mangle] -#[inline(never)] -pub fn access_ivars() -> (u8, *const NSObject) { - let obj = unsafe { get_obj().unwrap_unchecked() }; - ( - *obj.foo, - (*obj.obj) - .as_ref() - .map(|obj| Id::as_ptr(&obj)) - .unwrap_or_else(ptr::null), - ) +pub struct DropIvarsIvars { + obj: Id, + obj_option: Option>, +} + +declare_class!( + #[no_mangle] + pub struct DropIvars; + + unsafe impl ClassType for DropIvars { + type Super = NSObject; + type Mutability = mutability::InteriorMutable; + const NAME: &'static str = "DropIvars"; + } + + impl DeclaredClass for DropIvars { + type Ivars = DropIvarsIvars; + } + + unsafe impl DropIvars { + #[no_mangle] + #[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] } + } + } +); + +impl Drop for DropIvars { + #[inline(never)] + fn drop(&mut self) { + std::hint::black_box(()); + } +} + +impl DropIvars { + #[no_mangle] + pub fn access_drop_ivars_class() -> &'static AnyClass { + Self::class() + } + + #[no_mangle] + 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-assembly/src/lib.rs b/crates/test-assembly/src/lib.rs index 23464ceaa..f5acef128 100644 --- a/crates/test-assembly/src/lib.rs +++ b/crates/test-assembly/src/lib.rs @@ -90,6 +90,7 @@ pub fn read_assembly>(path: P, package_path: &Path) -> io::Result let s = s.replace(".asciz\t\"}\\000", ".asciz\t\"p\\000"); let s = s.replace(".asciz\t\"L\\000", ".asciz\t\"p\\000"); let s = s.replace(".asciz\t\"t\\000", ".asciz\t\"p\\000"); + let s = s.replace(".asciz\t\"T\\000", ".asciz\t\"P\\000"); // HACK: Replace Objective-C image info for simulator targets let s = s.replace( 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 { diff --git a/crates/tests/src/test_object.rs b/crates/tests/src/test_object.rs index abb5fde52..45e80605f 100644 --- a/crates/tests/src/test_object.rs +++ b/crates/tests/src/test_object.rs @@ -138,11 +138,13 @@ impl MyTestObject { } fn var1_ivar(&self) -> &c_int { - unsafe { self.inner.ivar("var1") } + let ivar = Self::class().instance_variable("var1").unwrap(); + unsafe { ivar.load(&self.inner) } } fn var1_ivar_mut(&mut self) -> &mut c_int { - unsafe { self.inner.ivar_mut("var1") } + let ivar = Self::class().instance_variable("var1").unwrap(); + unsafe { ivar.load_mut(&mut self.inner) } } fn add_to_ivar1(&mut self, number: c_int) { @@ -154,11 +156,13 @@ impl MyTestObject { } fn var2_ivar(&self) -> &Bool { - unsafe { self.inner.ivar("var2") } + let ivar = Self::class().instance_variable("var2").unwrap(); + unsafe { ivar.load(&self.inner) } } fn var2_ivar_mut(&mut self) -> &mut Bool { - unsafe { self.inner.ivar_mut("var2") } + let ivar = Self::class().instance_variable("var2").unwrap(); + unsafe { ivar.load_mut(&mut self.inner) } } fn var3(&self) -> *mut AnyObject { @@ -170,11 +174,13 @@ impl MyTestObject { } fn var3_ivar(&self) -> &*mut AnyObject { - unsafe { self.inner.ivar("var3") } + let ivar = Self::class().instance_variable("var3").unwrap(); + unsafe { ivar.load(&self.inner) } } fn var3_ivar_mut(&mut self) -> &mut *mut AnyObject { - unsafe { self.inner.ivar_mut("var3") } + let ivar = Self::class().instance_variable("var3").unwrap(); + unsafe { ivar.load_mut(&mut self.inner) } } }