Skip to content

Commit 1f91a8d

Browse files
committed
Test reference-counting properties of msg_send_id!
1 parent aed5cdd commit 1f91a8d

File tree

3 files changed

+104
-41
lines changed

3 files changed

+104
-41
lines changed

objc2/src/__macro_helpers.rs

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -151,59 +151,84 @@ mod tests {
151151

152152
use core::ptr;
153153

154-
use crate::rc::{Owned, Shared};
154+
use crate::rc::{Owned, RcTestObject, Shared, ThreadTestData, _NSZone};
155155
use crate::runtime::Object;
156-
use crate::{Encoding, RefEncode};
157-
158-
#[repr(C)]
159-
struct _NSZone {
160-
_inner: [u8; 0],
161-
}
162-
163-
unsafe impl RefEncode for _NSZone {
164-
const ENCODING_REF: Encoding<'static> =
165-
Encoding::Pointer(&Encoding::Struct("_NSZone", &[]));
166-
}
167156

168157
#[test]
169158
fn test_macro_alloc() {
170-
let cls = class!(NSObject);
159+
let mut expected = ThreadTestData::current();
160+
let cls = RcTestObject::class();
161+
162+
let obj: Id<RcTestObject, Shared> = unsafe { msg_send_id![cls, alloc].unwrap() };
163+
expected.alloc += 1;
164+
expected.assert_current();
171165

172-
let _obj: Option<Id<Object, Shared>> = unsafe { msg_send_id![cls, alloc] };
166+
drop(obj);
167+
expected.release += 1;
168+
expected.dealloc += 1;
169+
expected.assert_current();
173170

174171
let zone: *const _NSZone = ptr::null();
175-
let _obj: Option<Id<Object, Owned>> = unsafe { msg_send_id![cls, allocWithZone: zone] };
172+
let _obj: Id<RcTestObject, Owned> =
173+
unsafe { msg_send_id![cls, allocWithZone: zone].unwrap() };
174+
expected.alloc += 1;
175+
expected.assert_current();
176176
}
177177

178178
#[test]
179179
fn test_macro_init() {
180-
let cls = class!(NSObject);
180+
let mut expected = ThreadTestData::current();
181+
let cls = RcTestObject::class();
181182

182-
let obj: Option<Id<Object, Shared>> = unsafe { msg_send_id![cls, alloc] };
183+
let obj: Option<Id<RcTestObject, Shared>> = unsafe { msg_send_id![cls, alloc] };
184+
expected.alloc += 1;
183185
// Don't check allocation error
184-
let _obj: Id<Object, Shared> = unsafe { msg_send_id![obj, init].unwrap() };
186+
let _obj: Id<RcTestObject, Shared> = unsafe { msg_send_id![obj, init].unwrap() };
187+
expected.init += 1;
188+
expected.assert_current();
185189

186-
let obj: Option<Id<Object, Shared>> = unsafe { msg_send_id![cls, alloc] };
190+
let obj: Option<Id<RcTestObject, Shared>> = unsafe { msg_send_id![cls, alloc] };
191+
expected.alloc += 1;
187192
// Check allocation error before init
188193
let obj = obj.unwrap();
189-
let _obj: Id<Object, Shared> = unsafe { msg_send_id![obj, init].unwrap() };
194+
let _obj: Id<RcTestObject, Shared> = unsafe { msg_send_id![obj, init].unwrap() };
195+
expected.init += 1;
196+
expected.assert_current();
190197
}
191198

192199
#[test]
193200
fn test_macro() {
194-
let cls = class!(NSObject);
195-
196-
let _obj: Id<Object, Owned> = unsafe { msg_send_id![cls, new].unwrap() };
197-
198-
let obj = unsafe { msg_send_id![cls, alloc] };
199-
200-
let obj: Id<Object, Owned> = unsafe { msg_send_id![obj, init].unwrap() };
201-
202-
// TODO:
203-
// let copy: Id<Object, Shared> = unsafe { msg_send_id![&obj, copy].unwrap() };
204-
// let mutable_copy: Id<Object, Shared> = unsafe { msg_send_id![&obj, mutableCopy].unwrap() };
205-
206-
let _desc: Option<Id<Object, Shared>> = unsafe { msg_send_id![&obj, description] };
201+
let mut expected = ThreadTestData::current();
202+
let cls = RcTestObject::class();
203+
crate::rc::autoreleasepool(|_| {
204+
let _obj: Id<RcTestObject, Owned> = unsafe { msg_send_id![cls, new].unwrap() };
205+
expected.alloc += 1;
206+
expected.init += 1;
207+
expected.assert_current();
208+
209+
let obj = unsafe { msg_send_id![cls, alloc] };
210+
expected.alloc += 1;
211+
expected.assert_current();
212+
213+
let obj: Id<RcTestObject, Owned> = unsafe { msg_send_id![obj, init].unwrap() };
214+
expected.init += 1;
215+
expected.assert_current();
216+
217+
// TODO:
218+
// let copy: Id<RcTestObject, Shared> = unsafe { msg_send_id![&obj, copy].unwrap() };
219+
// let mutable_copy: Id<RcTestObject, Shared> = unsafe { msg_send_id![&obj, mutableCopy].unwrap() };
220+
221+
let _self: Id<RcTestObject, Shared> = unsafe { msg_send_id![&obj, self].unwrap() };
222+
expected.retain += 1;
223+
expected.assert_current();
224+
225+
let _desc: Option<Id<RcTestObject, Shared>> =
226+
unsafe { msg_send_id![&obj, description] };
227+
expected.assert_current();
228+
});
229+
expected.release += 3;
230+
expected.dealloc += 2;
231+
expected.assert_current();
207232
}
208233

209234
#[test]

objc2/src/rc/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ pub use self::ownership::{Owned, Ownership, Shared};
7272
pub use self::weak_id::WeakId;
7373

7474
#[cfg(test)]
75-
pub(crate) use self::test_object::{RcTestObject, ThreadTestData};
75+
pub(crate) use self::test_object::{RcTestObject, ThreadTestData, _NSZone};
7676

7777
#[cfg(test)]
7878
mod tests {

objc2/src/rc/test_object.rs

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,40 @@ use std::sync::Once;
55
use super::{Id, Owned};
66
use crate::declare::ClassBuilder;
77
use crate::runtime::{Bool, Class, Object, Sel};
8-
use crate::{msg_send, msg_send_bool, msg_send_id};
8+
use crate::{msg_send, msg_send_bool};
99
use crate::{Encoding, Message, RefEncode};
1010

11+
#[repr(C)]
12+
pub(crate) struct _NSZone {
13+
_inner: [u8; 0],
14+
}
15+
16+
#[cfg(feature = "gnustep-1-7")]
17+
const INNER_NSZONE_ENCODING: &[Encoding<'static>] = {
18+
use crate::{ffi, Encode};
19+
let uptr = Encoding::Pointer(&Encoding::Unknown);
20+
&[
21+
uptr,
22+
uptr,
23+
uptr,
24+
uptr,
25+
uptr,
26+
uptr,
27+
uptr,
28+
ffi::NSUInteger::ENCODING,
29+
Encoding::Object,
30+
Encoding::Pointer(&Encoding::Struct("_NSZone", &[])),
31+
]
32+
};
33+
34+
#[cfg(feature = "apple")]
35+
const INNER_NSZONE_ENCODING: &[Encoding<'static>] = &[];
36+
37+
unsafe impl RefEncode for _NSZone {
38+
const ENCODING_REF: Encoding<'static> =
39+
Encoding::Pointer(&Encoding::Struct("_NSZone", INNER_NSZONE_ENCODING));
40+
}
41+
1142
#[derive(Debug, Clone, Default, PartialEq)]
1243
pub(crate) struct ThreadTestData {
1344
pub(crate) alloc: usize,
@@ -77,14 +108,19 @@ impl DerefMut for RcTestObject {
77108
}
78109

79110
impl RcTestObject {
80-
fn class() -> &'static Class {
111+
pub(crate) fn class() -> &'static Class {
81112
static REGISTER_CLASS: Once = Once::new();
82113

83114
REGISTER_CLASS.call_once(|| {
84-
extern "C" fn alloc(cls: &Class, _cmd: Sel) -> *mut RcTestObject {
115+
// `+[NSObject alloc]` delegates to `+[NSObject allocWithZone:]`
116+
extern "C" fn alloc_with_zone(
117+
cls: &Class,
118+
_cmd: Sel,
119+
zone: *mut _NSZone,
120+
) -> *mut RcTestObject {
85121
TEST_DATA.with(|data| data.borrow_mut().alloc += 1);
86122
let superclass = class!(NSObject).metaclass();
87-
unsafe { msg_send![super(cls, superclass), alloc] }
123+
unsafe { msg_send![super(cls, superclass), allocWithZone: zone] }
88124
}
89125
extern "C" fn init(this: &mut RcTestObject, _cmd: Sel) -> *mut RcTestObject {
90126
TEST_DATA.with(|data| data.borrow_mut().init += 1);
@@ -119,8 +155,9 @@ impl RcTestObject {
119155
let mut builder = ClassBuilder::new("RcTestObject", class!(NSObject)).unwrap();
120156
unsafe {
121157
builder.add_class_method(
122-
sel!(alloc),
123-
alloc as extern "C" fn(&Class, Sel) -> *mut RcTestObject,
158+
sel!(allocWithZone:),
159+
alloc_with_zone
160+
as extern "C" fn(&Class, Sel, *mut _NSZone) -> *mut RcTestObject,
124161
);
125162
builder.add_method(
126163
sel!(init),
@@ -152,6 +189,7 @@ impl RcTestObject {
152189
}
153190

154191
pub(crate) fn new() -> Id<Self, Owned> {
155-
unsafe { msg_send_id![Self::class(), new] }.unwrap()
192+
// Use msg_send! to test that; msg_send_id! is tested elsewhere!
193+
unsafe { Id::new(msg_send![Self::class(), new]) }.unwrap()
156194
}
157195
}

0 commit comments

Comments
 (0)