3
3
//! For more information on foreign functions, see Apple's documentation:
4
4
//! <https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html>
5
5
6
+ #[ cfg( doc) ]
7
+ use core:: cell:: UnsafeCell ;
6
8
use core:: fmt;
7
9
use core:: hash;
8
10
use core:: panic:: { RefUnwindSafe , UnwindSafe } ;
@@ -593,6 +595,11 @@ fn ivar_offset<T: Encode>(cls: &Class, name: &str) -> isize {
593
595
///
594
596
/// `Id<Object, _>` is equivalent to Objective-C's `id`.
595
597
///
598
+ /// This contains [`UnsafeCell`], and is similar to that in that one can
599
+ /// safely access and perform interior mutability on this (both via.
600
+ /// [`msg_send!`] and through ivars), so long as Rust's mutability rules are
601
+ /// upheld, and that data races are avoided.
602
+ ///
596
603
/// Note: This is intentionally neither [`Sync`], [`Send`], [`UnwindSafe`],
597
604
/// [`RefUnwindSafe`] nor [`Unpin`], since that is something that may change
598
605
/// depending on the specific subclass. For example, `NSAutoreleasePool` is
@@ -618,19 +625,45 @@ impl Object {
618
625
unsafe { ptr. as_ref ( ) . unwrap_unchecked ( ) }
619
626
}
620
627
621
- /// Returns a shared reference to the ivar with the given name.
628
+ /// Returns a pointer to the instance variable / ivar with the given name.
629
+ ///
630
+ /// This is similar to [`UnsafeCell::get`], see that for more information
631
+ /// on what is and isn't safe to do.
632
+ ///
633
+ /// Usually you will have defined the instance variable yourself with
634
+ /// [`ClassBuilder::add_ivar`], the type of the ivar `T` must match the
635
+ /// type used in that.
636
+ ///
637
+ /// Attempting to access or modify private implementation details of a
638
+ /// class that you do no control using this is not supported, and may
639
+ /// invoke undefined behaviour.
640
+ ///
641
+ /// Library implementors are strongly encouraged to expose a safe
642
+ /// interface to the ivar.
643
+ ///
644
+ /// [`ClassBuilder::add_ivar`]: crate::declare::ClassBuilder::add_ivar
645
+ ///
622
646
///
623
647
/// # Panics
624
648
///
625
- /// Panics if the object has no ivar with the given name, or the type
626
- /// encoding of the ivar differs from the type encoding of `T`.
649
+ /// May panic if the object has no ivar with the given name. May also
650
+ /// panic if the type encoding of the ivar differs from the type encoding
651
+ /// of `T`.
652
+ ///
653
+ /// This should purely seen as help while debugging and is not guaranteed
654
+ /// (e.g. it may be disabled when `debug_assertions` are off).
655
+ ///
627
656
///
628
657
/// # Safety
629
658
///
630
- /// The caller must ensure that the ivar is actually of type `T`.
659
+ /// The object must have an instance variable with the given name, and it
660
+ /// must be of type `T`. Any invariants that the object have assumed about
661
+ /// the value of the instance variable must not be violated.
631
662
///
632
- /// Library implementors should expose a safe interface to the ivar.
633
- pub unsafe fn ivar < T : Encode > ( & self , name : & str ) -> & T {
663
+ /// No thread syncronization is done on accesses to the variable, so you
664
+ /// must ensure that any access to the returned pointer do not cause data
665
+ /// races, and that Rust's mutability rules are not otherwise violated.
666
+ pub unsafe fn ivar_ptr < T : Encode > ( & self , name : & str ) -> * mut T {
634
667
let offset = ivar_offset :: < T > ( self . class ( ) , name) ;
635
668
let ptr: * const Self = self ;
636
669
@@ -639,32 +672,56 @@ impl Object {
639
672
let ptr = unsafe { ptr. offset ( offset) } ;
640
673
let ptr: * const T = ptr. cast ( ) ;
641
674
642
- unsafe { ptr. as_ref ( ) . unwrap_unchecked ( ) }
675
+ // Safe as *mut T because `self` is `UnsafeCell`
676
+ ptr as * mut T
643
677
}
644
678
645
- /// Use [`ivar`][`Self::ivar`] instead.
679
+ /// Returns a reference to the instance variable with the given name.
680
+ ///
681
+ /// See [`Object::ivar_ptr`] for more information, including on when this
682
+ /// panics.
683
+ ///
646
684
///
647
685
/// # Safety
648
686
///
649
- /// Same as [`ivar`][`Self::ivar`].
687
+ /// The object must have an instance variable with the given name, and it
688
+ /// must be of type `T`.
689
+ ///
690
+ /// No thread syncronization is done, so you must ensure that no other
691
+ /// thread is concurrently mutating the variable. This requirement can be
692
+ /// considered upheld if all mutation happens through [`Object::ivar_mut`]
693
+ /// (since that takes `&mut self`).
694
+ pub unsafe fn ivar < T : Encode > ( & self , name : & str ) -> & T {
695
+ // SAFETY: Upheld by caller.
696
+ unsafe { self . ivar_ptr :: < T > ( name) . as_ref ( ) . unwrap_unchecked ( ) }
697
+ }
698
+
699
+ /// Use [`Object::ivar`] instead.
700
+ ///
701
+ ///
702
+ /// # Safety
703
+ ///
704
+ /// See [`Object::ivar`].
650
705
#[ deprecated = "Use `Object::ivar` instead." ]
651
706
pub unsafe fn get_ivar < T : Encode > ( & self , name : & str ) -> & T {
652
707
// SAFETY: Upheld by caller
653
- unsafe { self . ivar ( name) }
708
+ unsafe { self . ivar :: < T > ( name) }
654
709
}
655
710
656
711
/// Returns a mutable reference to the ivar with the given name.
657
712
///
658
- /// # Panics
713
+ /// See [`Object::ivar_ptr`] for more information, including on when this
714
+ /// panics.
659
715
///
660
- /// Panics if the object has no ivar with the given name, or the type
661
- /// encoding of the ivar differs from the type encoding of `T`.
662
716
///
663
717
/// # Safety
664
718
///
665
- /// The caller must ensure that the ivar is actually of type `T`.
719
+ /// The object must have an instance variable with the given name, and it
720
+ /// must be of type `T`.
666
721
///
667
- /// Library implementors should expose a safe interface to the ivar.
722
+ /// This access happens through `&mut self`, which means we know it to be
723
+ /// the only reference, hence you do not need to do any work to ensure
724
+ /// that data races do not happen.
668
725
pub unsafe fn ivar_mut < T : Encode > ( & mut self , name : & str ) -> & mut T {
669
726
let offset = ivar_offset :: < T > ( self . class ( ) , name) ;
670
727
let ptr: * mut Self = self ;
@@ -677,29 +734,27 @@ impl Object {
677
734
unsafe { ptr. as_mut ( ) . unwrap_unchecked ( ) }
678
735
}
679
736
680
- /// Use [`ivar_mut`](`Self::ivar_mut`) instead.
737
+ /// Use [`Object::ivar_mut`] instead.
738
+ ///
681
739
///
682
740
/// # Safety
683
741
///
684
- /// Same as [`ivar_mut`][`Self ::ivar_mut`].
742
+ /// Same as [`Object ::ivar_mut`].
685
743
#[ deprecated = "Use `Object::ivar_mut` instead." ]
686
744
pub unsafe fn get_mut_ivar < T : Encode > ( & mut self , name : & str ) -> & mut T {
687
745
// SAFETY: Upheld by caller
688
- unsafe { self . ivar_mut ( name) }
746
+ unsafe { self . ivar_mut :: < T > ( name) }
689
747
}
690
748
691
749
/// Sets the value of the ivar with the given name.
692
750
///
693
- /// # Panics
751
+ /// This is just a helpful shorthand for [`Object::ivar_mut`], see that
752
+ /// for more information.
694
753
///
695
- /// Panics if the object has no ivar with the given name, or the type
696
- /// encoding of the ivar differs from the type encoding of `T`.
697
754
///
698
755
/// # Safety
699
756
///
700
- /// The caller must ensure that the ivar is actually of type `T`.
701
- ///
702
- /// Library implementors should expose a safe interface to the ivar.
757
+ /// Same as [`Object::ivar_mut`].
703
758
pub unsafe fn set_ivar < T : Encode > ( & mut self , name : & str , value : T ) {
704
759
// SAFETY: Invariants upheld by caller
705
760
unsafe { * self . ivar_mut :: < T > ( name) = value } ;
@@ -851,13 +906,28 @@ mod tests {
851
906
fn test_object ( ) {
852
907
let mut obj = test_utils:: custom_object ( ) ;
853
908
assert_eq ! ( obj. class( ) , test_utils:: custom_class( ) ) ;
854
- let result: u32 = unsafe {
855
- obj. set_ivar ( "_foo" , 4u32 ) ;
856
- * obj. ivar ( "_foo" )
909
+
910
+ let result = unsafe {
911
+ obj. set_ivar :: < u32 > ( "_foo" , 4 ) ;
912
+ * obj. ivar :: < u32 > ( "_foo" )
857
913
} ;
858
914
assert_eq ! ( result, 4 ) ;
859
915
}
860
916
917
+ #[ test]
918
+ #[ should_panic = "Ivar unknown not found on class CustomObject" ]
919
+ fn test_object_ivar_unknown ( ) {
920
+ let obj = test_utils:: custom_object ( ) ;
921
+ let _ = unsafe { * obj. ivar :: < u32 > ( "unknown" ) } ;
922
+ }
923
+
924
+ #[ test]
925
+ #[ should_panic = "assertion failed: T::ENCODING.equivalent_to_str(ivar.type_encoding())" ]
926
+ fn test_object_ivar_wrong_type ( ) {
927
+ let obj = test_utils:: custom_object ( ) ;
928
+ let _ = unsafe { * obj. ivar :: < u8 > ( "_foo" ) } ;
929
+ }
930
+
861
931
#[ test]
862
932
fn test_encode ( ) {
863
933
fn assert_enc < T : Encode > ( expected : & str ) {
0 commit comments