@@ -625,19 +625,45 @@ impl Object {
625
625
unsafe { ptr. as_ref ( ) . unwrap_unchecked ( ) }
626
626
}
627
627
628
- /// 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
+ ///
629
646
///
630
647
/// # Panics
631
648
///
632
- /// Panics if the object has no ivar with the given name, or the type
633
- /// 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
+ ///
634
656
///
635
657
/// # Safety
636
658
///
637
- /// 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.
638
662
///
639
- /// Library implementors should expose a safe interface to the ivar.
640
- 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 {
641
667
let offset = ivar_offset :: < T > ( self . class ( ) , name) ;
642
668
let ptr: * const Self = self ;
643
669
@@ -646,32 +672,56 @@ impl Object {
646
672
let ptr = unsafe { ptr. offset ( offset) } ;
647
673
let ptr: * const T = ptr. cast ( ) ;
648
674
649
- unsafe { ptr. as_ref ( ) . unwrap_unchecked ( ) }
675
+ // Safe as *mut T because `self` is `UnsafeCell`
676
+ ptr as * mut T
677
+ }
678
+
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
+ ///
684
+ ///
685
+ /// # Safety
686
+ ///
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 ( ) }
650
697
}
651
698
652
- /// Use [`ivar`][`Self::ivar`] instead.
699
+ /// Use [`Object::ivar`] instead.
700
+ ///
653
701
///
654
702
/// # Safety
655
703
///
656
- /// Same as [`ivar`][`Self ::ivar`].
704
+ /// See [`Object ::ivar`].
657
705
#[ deprecated = "Use `Object::ivar` instead." ]
658
706
pub unsafe fn get_ivar < T : Encode > ( & self , name : & str ) -> & T {
659
707
// SAFETY: Upheld by caller
660
- unsafe { self . ivar ( name) }
708
+ unsafe { self . ivar :: < T > ( name) }
661
709
}
662
710
663
711
/// Returns a mutable reference to the ivar with the given name.
664
712
///
665
- /// # Panics
713
+ /// See [`Object::ivar_ptr`] for more information, including on when this
714
+ /// panics.
666
715
///
667
- /// Panics if the object has no ivar with the given name, or the type
668
- /// encoding of the ivar differs from the type encoding of `T`.
669
716
///
670
717
/// # Safety
671
718
///
672
- /// 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`.
673
721
///
674
- /// 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.
675
725
pub unsafe fn ivar_mut < T : Encode > ( & mut self , name : & str ) -> & mut T {
676
726
let offset = ivar_offset :: < T > ( self . class ( ) , name) ;
677
727
let ptr: * mut Self = self ;
@@ -684,29 +734,27 @@ impl Object {
684
734
unsafe { ptr. as_mut ( ) . unwrap_unchecked ( ) }
685
735
}
686
736
687
- /// Use [`ivar_mut`](`Self::ivar_mut`) instead.
737
+ /// Use [`Object::ivar_mut`] instead.
738
+ ///
688
739
///
689
740
/// # Safety
690
741
///
691
- /// Same as [`ivar_mut`][`Self ::ivar_mut`].
742
+ /// Same as [`Object ::ivar_mut`].
692
743
#[ deprecated = "Use `Object::ivar_mut` instead." ]
693
744
pub unsafe fn get_mut_ivar < T : Encode > ( & mut self , name : & str ) -> & mut T {
694
745
// SAFETY: Upheld by caller
695
- unsafe { self . ivar_mut ( name) }
746
+ unsafe { self . ivar_mut :: < T > ( name) }
696
747
}
697
748
698
749
/// Sets the value of the ivar with the given name.
699
750
///
700
- /// # Panics
751
+ /// This is just a helpful shorthand for [`Object::ivar_mut`], see that
752
+ /// for more information.
701
753
///
702
- /// Panics if the object has no ivar with the given name, or the type
703
- /// encoding of the ivar differs from the type encoding of `T`.
704
754
///
705
755
/// # Safety
706
756
///
707
- /// The caller must ensure that the ivar is actually of type `T`.
708
- ///
709
- /// Library implementors should expose a safe interface to the ivar.
757
+ /// Same as [`Object::ivar_mut`].
710
758
pub unsafe fn set_ivar < T : Encode > ( & mut self , name : & str , value : T ) {
711
759
// SAFETY: Invariants upheld by caller
712
760
unsafe { * self . ivar_mut :: < T > ( name) = value } ;
@@ -858,13 +906,28 @@ mod tests {
858
906
fn test_object ( ) {
859
907
let mut obj = test_utils:: custom_object ( ) ;
860
908
assert_eq ! ( obj. class( ) , test_utils:: custom_class( ) ) ;
861
- let result: u32 = unsafe {
862
- obj. set_ivar ( "_foo" , 4u32 ) ;
863
- * obj. ivar ( "_foo" )
909
+
910
+ let result = unsafe {
911
+ obj. set_ivar :: < u32 > ( "_foo" , 4 ) ;
912
+ * obj. ivar :: < u32 > ( "_foo" )
864
913
} ;
865
914
assert_eq ! ( result, 4 ) ;
866
915
}
867
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
+
868
931
#[ test]
869
932
fn test_encode ( ) {
870
933
fn assert_enc < T : Encode > ( expected : & str ) {
0 commit comments