objc2/runtime/
protocol_object.rs1use core::fmt;
2use core::hash;
3use core::marker::PhantomData;
4use core::ptr::NonNull;
5
6use crate::encode::{Encoding, RefEncode};
7use crate::rc::{autoreleasepool_leaking, Retained};
8use crate::runtime::__nsstring::nsstring_to_str;
9use crate::runtime::{AnyObject, NSObjectProtocol};
10use crate::Message;
11
12pub unsafe trait ImplementedBy<T: ?Sized + Message> {
22 #[doc(hidden)]
23 const __INNER: ();
24}
25
26#[doc(alias = "id")]
59#[repr(C)]
60pub struct ProtocolObject<P: ?Sized> {
61 inner: AnyObject,
62 p: PhantomData<P>,
63}
64
65unsafe impl<P: ?Sized + Send> Send for ProtocolObject<P> {}
69
70unsafe impl<P: ?Sized + Sync> Sync for ProtocolObject<P> {}
74
75unsafe impl<P: ?Sized> RefEncode for ProtocolObject<P> {
77 const ENCODING_REF: Encoding = Encoding::Object;
78}
79
80unsafe impl<P: ?Sized> Message for ProtocolObject<P> {}
83
84impl<P: ?Sized> ProtocolObject<P> {
85 #[inline]
88 pub fn from_ref<T: ?Sized + Message>(obj: &T) -> &Self
89 where
90 P: ImplementedBy<T>,
91 {
92 let ptr: NonNull<T> = NonNull::from(obj);
93 let ptr: NonNull<Self> = ptr.cast();
94 unsafe { ptr.as_ref() }
97 }
98
99 #[deprecated = "use `ProtocolObject::from_retained` instead"]
101 #[inline]
102 pub fn from_id<T>(obj: Retained<T>) -> Retained<Self>
103 where
104 P: ImplementedBy<T> + 'static,
105 T: Message + 'static,
106 {
107 Self::from_retained(obj)
108 }
109
110 #[inline]
112 pub fn from_retained<T>(obj: Retained<T>) -> Retained<Self>
113 where
114 P: ImplementedBy<T> + 'static,
115 T: Message + 'static,
116 {
117 unsafe { Retained::cast_unchecked::<Self>(obj) }
122 }
123}
124
125impl<P: ?Sized + NSObjectProtocol> PartialEq for ProtocolObject<P> {
126 #[inline]
127 #[doc(alias = "isEqual:")]
128 fn eq(&self, other: &Self) -> bool {
129 self.isEqual(Some(&other.inner))
130 }
131}
132
133impl<P: ?Sized + NSObjectProtocol> Eq for ProtocolObject<P> {}
134
135impl<P: ?Sized + NSObjectProtocol> hash::Hash for ProtocolObject<P> {
136 #[inline]
137 fn hash<H: hash::Hasher>(&self, state: &mut H) {
138 <Self as NSObjectProtocol>::hash(self).hash(state);
139 }
140}
141
142impl<P: ?Sized + NSObjectProtocol> fmt::Debug for ProtocolObject<P> {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 let description = self.description();
145
146 autoreleasepool_leaking(|pool| {
165 let s = unsafe { nsstring_to_str(&description, pool) };
173 fmt::Display::fmt(s, f)
174 })
175 }
176}
177
178impl<P: ?Sized, T> AsRef<ProtocolObject<T>> for ProtocolObject<P>
179where
180 T: ?Sized + ImplementedBy<ProtocolObject<P>>,
181{
182 #[inline]
183 fn as_ref(&self) -> &ProtocolObject<T> {
184 ProtocolObject::from_ref(self)
185 }
186}
187
188impl<P: ?Sized + 'static> AsRef<AnyObject> for ProtocolObject<P> {
191 #[inline]
192 fn as_ref(&self) -> &AnyObject {
193 let ptr: NonNull<ProtocolObject<P>> = NonNull::from(self);
194 let ptr: NonNull<AnyObject> = ptr.cast();
195 unsafe { ptr.as_ref() }
197 }
198}
199
200#[cfg(test)]
201#[allow(clippy::missing_safety_doc)]
202#[allow(dead_code)]
203mod tests {
204 use alloc::format;
205 use core::ffi::CStr;
206
207 use static_assertions::{assert_impl_all, assert_not_impl_any};
208
209 use super::*;
210 use crate::runtime::{ClassBuilder, NSObject};
211 use crate::{define_class, extern_methods, extern_protocol, msg_send, ClassType};
212
213 extern_protocol!(
214 unsafe trait Foo {
215 #[unsafe(method(foo))]
216 fn foo_class();
217
218 #[unsafe(method(foo))]
219 fn foo_instance(&self);
220 }
221 );
222
223 extern_protocol!(
224 unsafe trait Bar: NSObjectProtocol {
225 #[unsafe(method(bar))]
226 fn bar_class();
227
228 #[unsafe(method(bar))]
229 fn bar_instance(&self);
230 }
231 );
232
233 extern_protocol!(
234 unsafe trait FooBar: Foo + Bar {
235 #[unsafe(method(foobar))]
236 fn foobar_class();
237
238 #[unsafe(method(foobar))]
239 fn foobar_instance(&self);
240 }
241 );
242
243 extern_protocol!(
244 unsafe trait FooFooBar: Foo + FooBar {
245 #[unsafe(method(foofoobar))]
246 fn foofoobar_class();
247
248 #[unsafe(method(foofoobar))]
249 fn foofoobar_instance(&self);
250 }
251 );
252
253 define_class!(
254 #[unsafe(super(NSObject))]
255 #[derive(Debug, PartialEq, Eq, Hash)]
256 struct DummyClass;
257
258 unsafe impl NSObjectProtocol for DummyClass {}
259 );
260
261 unsafe impl Foo for DummyClass {}
262 unsafe impl Bar for DummyClass {}
263 unsafe impl FooBar for DummyClass {}
264 impl DummyClass {
267 extern_methods!(
268 #[unsafe(method(new))]
269 fn new() -> Retained<Self>;
270 );
271 }
272
273 #[test]
274 fn impl_traits() {
275 assert_impl_all!(NSObject: NSObjectProtocol);
276 assert_impl_all!(ProtocolObject<dyn NSObjectProtocol>: NSObjectProtocol);
277 assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: Send, Sync);
278 assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Send>: NSObjectProtocol, Send);
279 assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol + Send>: Sync);
280 assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Sync>: NSObjectProtocol, Sync);
281 assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol + Sync>: Send);
282 assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Send + Sync>: NSObjectProtocol, Send, Sync);
283 assert_not_impl_any!(ProtocolObject<dyn Foo>: NSObjectProtocol);
284 assert_impl_all!(ProtocolObject<dyn Bar>: NSObjectProtocol);
285 assert_impl_all!(ProtocolObject<dyn FooBar>: NSObjectProtocol);
286 assert_impl_all!(ProtocolObject<dyn FooFooBar>: NSObjectProtocol);
287 assert_impl_all!(DummyClass: NSObjectProtocol);
288
289 assert_not_impl_any!(NSObject: Foo);
290 assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: Foo);
291 assert_impl_all!(ProtocolObject<dyn Foo>: Foo);
292 assert_not_impl_any!(ProtocolObject<dyn Bar>: Foo);
293 assert_impl_all!(ProtocolObject<dyn FooBar>: Foo);
294 assert_impl_all!(ProtocolObject<dyn FooFooBar>: Foo);
295 assert_impl_all!(DummyClass: Foo);
296
297 assert_not_impl_any!(NSObject: Bar);
298 assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: Bar);
299 assert_not_impl_any!(ProtocolObject<dyn Foo>: Bar);
300 assert_impl_all!(ProtocolObject<dyn Bar>: Bar);
301 assert_impl_all!(ProtocolObject<dyn FooBar>: Bar);
302 assert_impl_all!(ProtocolObject<dyn FooFooBar>: Bar);
303 assert_impl_all!(DummyClass: Bar);
304
305 assert_not_impl_any!(NSObject: FooBar);
306 assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: FooBar);
307 assert_not_impl_any!(ProtocolObject<dyn Foo>: FooBar);
308 assert_not_impl_any!(ProtocolObject<dyn Bar>: FooBar);
309 assert_impl_all!(ProtocolObject<dyn FooBar>: FooBar);
310 assert_impl_all!(ProtocolObject<dyn FooFooBar>: FooBar);
311 assert_impl_all!(DummyClass: FooBar);
312
313 assert_not_impl_any!(NSObject: FooFooBar);
314 assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: FooFooBar);
315 assert_not_impl_any!(ProtocolObject<dyn Foo>: FooFooBar);
316 assert_not_impl_any!(ProtocolObject<dyn Bar>: FooFooBar);
317 assert_not_impl_any!(ProtocolObject<dyn FooBar>: FooFooBar);
318 assert_impl_all!(ProtocolObject<dyn FooFooBar>: FooFooBar);
319 assert_not_impl_any!(DummyClass: FooFooBar);
320 }
321
322 #[test]
323 fn convertible() {
324 let obj = DummyClass::new();
325 let foobar: &ProtocolObject<dyn FooBar> = ProtocolObject::from_ref(&*obj);
326 let foobar: &ProtocolObject<dyn FooBar> = ProtocolObject::from_ref(foobar);
327
328 let _bar: &ProtocolObject<dyn Bar> = ProtocolObject::from_ref(foobar);
329 let bar: &ProtocolObject<dyn Bar> = ProtocolObject::from_ref(&*obj);
330 let bar: &ProtocolObject<dyn Bar> = ProtocolObject::from_ref(bar);
331
332 let _foo: &ProtocolObject<dyn Foo> = ProtocolObject::from_ref(foobar);
333 let foo: &ProtocolObject<dyn Foo> = ProtocolObject::from_ref(&*obj);
334 let _foo: &ProtocolObject<dyn Foo> = ProtocolObject::from_ref(foo);
335
336 let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(foobar);
337 let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(bar);
338 let nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(&*obj);
339 let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(nsobject);
340 let _: &ProtocolObject<dyn NSObjectProtocol + Send> = ProtocolObject::from_ref(&*obj);
341 let _: &ProtocolObject<dyn NSObjectProtocol + Sync> = ProtocolObject::from_ref(&*obj);
342 let _: &ProtocolObject<dyn NSObjectProtocol + Send + Sync> =
343 ProtocolObject::from_ref(&*obj);
344
345 let _foobar: Retained<ProtocolObject<dyn FooBar>> = ProtocolObject::from_retained(obj);
346 }
347
348 #[test]
349 fn convert_to_anyobj() {
350 let obj = NSObject::new();
351 let obj: Retained<ProtocolObject<dyn NSObjectProtocol>> =
352 ProtocolObject::from_retained(obj);
353 let _obj: &AnyObject = obj.as_ref();
354 }
355
356 #[test]
357 fn test_traits() {
358 use core::hash::Hasher;
359 use std::collections::hash_map::DefaultHasher;
360 use std::hash::Hash;
361
362 let obj = DummyClass::new();
363 let obj2 = DummyClass::new();
364
365 let foobar: &ProtocolObject<dyn FooBar> = ProtocolObject::from_ref(&*obj);
366 let foobar2: &ProtocolObject<dyn FooBar> = ProtocolObject::from_ref(&*obj2);
367
368 assert_eq!(
369 format!("{obj:?}"),
370 format!("DummyClass {{ super: {foobar:?}, ivars: () }}"),
371 );
372 assert_eq!(obj == obj2, foobar == foobar2);
373
374 let mut hashstate_a = DefaultHasher::new();
375 let mut hashstate_b = DefaultHasher::new();
376
377 obj.hash(&mut hashstate_a);
378 <_ as Hash>::hash(foobar, &mut hashstate_b);
379
380 assert_eq!(hashstate_a.finish(), hashstate_b.finish());
381 }
382
383 extern_protocol!(
386 #[cfg(debug_assertions)]
387 unsafe trait CfgTest {}
388 );
389
390 #[test]
391 #[cfg(debug_assertions)]
392 fn test_meta() {
393 if false {
394 let _protocol = <dyn CfgTest as crate::ProtocolType>::protocol();
395 }
396 }
397
398 #[test]
399 #[cfg_attr(
400 feature = "gnustep-1-7",
401 ignore = "depends on the platform's NSString unicode handling"
402 )]
403 fn debug_non_utf8_classname() {
404 let s = CStr::from_bytes_with_nul(b"My\xF0\x90\x80Class\0").unwrap();
406
407 let cls = ClassBuilder::new(s, NSObject::class()).unwrap().register();
408 let obj: Retained<NSObject> = unsafe { msg_send![cls, new] };
409
410 let expected = format!("<My\u{f8ff}êÄClass: {:p}>", &*obj);
411 assert_eq!(format!("{obj:?}"), expected);
412 }
413}