From d7410d66edea16ec90e77ccc6ccf0c10a9031695 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 28 Jan 2023 07:47:56 +0100 Subject: [PATCH] Add API for associated objects --- crates/objc2/src/macros/associated_object.rs | 24 +++++ crates/objc2/src/macros/mod.rs | 1 + crates/objc2/src/runtime/associated_object.rs | 90 +++++++++++++++++++ crates/objc2/src/runtime/mod.rs | 4 - 4 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 crates/objc2/src/macros/associated_object.rs create mode 100644 crates/objc2/src/runtime/associated_object.rs diff --git a/crates/objc2/src/macros/associated_object.rs b/crates/objc2/src/macros/associated_object.rs new file mode 100644 index 000000000..24d400f42 --- /dev/null +++ b/crates/objc2/src/macros/associated_object.rs @@ -0,0 +1,24 @@ +macro_rules! associated_object { + ( + $(#[$m:meta])* + impl $name:ident { + $v_getter:vis fn $getter_name:ident(&self) -> $getter_ty:ty; + $v_setter:vis fn $setter_name:ident(&self, $setter_param:ident: $setter_ty:ty); + } + ) => { + const _: () = { + static mut __KEY: u8 = 0; + + $(#[$m])* + impl $name { + $v_getter fn $getter_name(&self) -> $getter_ty { + + } + + $v_setter fn $setter_name(&self, $setter_param: $setter_ty) { + + } + } + } + }; +} diff --git a/crates/objc2/src/macros/mod.rs b/crates/objc2/src/macros/mod.rs index e9f235c9f..4c5fb3559 100644 --- a/crates/objc2/src/macros/mod.rs +++ b/crates/objc2/src/macros/mod.rs @@ -6,6 +6,7 @@ mod __rewrite_self_arg; mod declare_class; mod extern_class; mod extern_methods; +mod associated_object; mod extern_protocol; /// Gets a reference to an [`AnyClass`] from the given name. diff --git a/crates/objc2/src/runtime/associated_object.rs b/crates/objc2/src/runtime/associated_object.rs new file mode 100644 index 000000000..8ca3a79f7 --- /dev/null +++ b/crates/objc2/src/runtime/associated_object.rs @@ -0,0 +1,90 @@ +use core::ptr; + +use super::Object; +use crate::rc::{Id, Shared}; +use crate::ffi; + +/// Associated object support. +/// +/// These are associated functions, since they are very rarely used, and will +/// mostly just clutter up the `Deref` chain and documentation of all other +/// classes in `icrate`. +/// +/// See [Apple's documentation](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAssociativeReferences.html). +impl Object { + /// + /// + /// # Panics + /// + /// This may panic or abort the process if the specified object does not + /// support associated objects. + unsafe fn set_associated_ptr(this: &Self, key: &K, value: *const ()) { + let key: *const K = key; + // SAFETY: The object and key is non-null + // + // Caller ensures that the key is uniquely used for the expected + // operation. + unsafe { + ffi::objc_setAssociatedObject(this.as_ptr(), key.cast(), value as *mut _, ffi::OBJC_ASSOCIATION_ASSIGN) + } + } + + unsafe fn set_associated_id(this: &Self, key: &K, value: Option<&Id>) { + let key: *const K = key; + let ptr: *const T = value.map(|value| Id::as_ptr(value)).unwrap_or(ptr::null()); + // SAFETY: The object and key is non-null, and the value came from + // a shared `Id`, so it is safe to retain. + // + // Caller ensures that the key is uniquely used for the expected + // operation. + unsafe { + ffi::objc_setAssociatedObject( + this.as_ptr(), + key.cast(), + ptr.cast(), + ffi::OBJC_ASSOCIATION_RETAIN, + ) + } + } + + unsafe fn get_associated_ptr(this: &Self, key: &K) -> *const () { + let key: *const K = key; + // SAFETY: + unsafe { ffi::objc_getAssociatedObject(this.as_ptr(), key.cast()).cast() } + } + + unsafe fn get_associated_id(this: &Self, key: &K) -> Id { + let ptr = this.get_associated_ptr(key) as *mut T; + // SAFETY: Caller upholds that the associated object stores an `Id`, + // and that the `Id` was originally `Shared`. + unsafe { Id::retain_autoreleased(ptr) } + } + + unsafe fn remove_associated(this: &Self, key: &K) { + let key: *const K = key; + // SAFETY: The object and key is non-null, and the associated is being + // broken, so the policy doesn't matter. + // + // Caller ensures that the key is uniquely used for the expected + // operation. + unsafe { + ffi::objc_setAssociatedObject( + this.as_ptr(), + key.cast(), + ptr::null(), + ffi::OBJC_ASSOCIATION_ASSIGN, + ) + } + } + + fn remove_all_associated(this: &Self) { + // SAFETY: + unsafe { ffi::objc_removeAssociatedObjects(this.as_ptr()) } + } + + // objc_setAssociatedObject + // objc_getAssociatedObject + // objc_removeAssociatedObjects + + // https://nshipster.com/associated-objects/ +} diff --git a/crates/objc2/src/runtime/mod.rs b/crates/objc2/src/runtime/mod.rs index 5e6a75d9a..8f1d81226 100644 --- a/crates/objc2/src/runtime/mod.rs +++ b/crates/objc2/src/runtime/mod.rs @@ -1265,10 +1265,6 @@ impl AnyObject { // SAFETY: Invariants upheld by caller unsafe { *self.ivar_mut::(name) = value }; } - - // objc_setAssociatedObject - // objc_getAssociatedObject - // objc_removeAssociatedObjects } impl fmt::Debug for AnyObject {