Skip to content

Commit c75691e

Browse files
authored
Merge pull request #151 from madsmtm/test-runtime-access
Test runtime introspection with a class created by Objective-C
2 parents dd3c422 + 1045636 commit c75691e

File tree

5 files changed

+265
-4
lines changed

5 files changed

+265
-4
lines changed

tests/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ gnustep-2-0 = ["gnustep-1-9", "block2/gnustep-2-0", "objc2-foundation/gnustep-2-
3131
gnustep-2-1 = ["gnustep-2-0", "block2/gnustep-2-1", "objc2-foundation/gnustep-2-1"]
3232
winobjc = ["gnustep-1-8", "block2/winobjc", "objc2-foundation/winobjc"]
3333

34+
malloc = ["objc2/malloc"]
35+
3436
[dependencies]
3537
block2 = { path = "../block2", default-features = false }
3638
block-sys = { path = "../block-sys", default-features = false }

tests/build.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::env;
22

33
fn main() {
4-
println!("cargo:rerun-if-changed=extern/block_utils.c");
5-
println!("cargo:rerun-if-changed=extern/encode_utils.m");
4+
println!("cargo:rerun-if-changed=build.rs");
65

76
let mut builder = cc::Build::new();
87
builder.compiler("clang");
98
builder.file("extern/block_utils.c");
9+
println!("cargo:rerun-if-changed=extern/block_utils.c");
1010

1111
for flag in env::var("DEP_BLOCK_0_0_CC_ARGS").unwrap().split(' ') {
1212
builder.flag(flag);
@@ -17,13 +17,16 @@ fn main() {
1717
let mut builder = cc::Build::new();
1818
builder.compiler("clang");
1919
builder.file("extern/encode_utils.m");
20+
builder.file("extern/test_object.m");
21+
println!("cargo:rerun-if-changed=extern/encode_utils.m");
22+
println!("cargo:rerun-if-changed=extern/test_object.m");
2023

2124
for flag in env::var("DEP_OBJC_0_2_CC_ARGS").unwrap().split(' ') {
2225
builder.flag(flag);
2326
}
2427

28+
builder.compile("libobjc_utils.a");
29+
2530
// For assembly tests
2631
println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap());
27-
28-
builder.compile("libencode_utils.a");
2932
}

tests/extern/test_object.m

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include <Foundation/NSObject.h>
2+
3+
@interface MyTestObject: NSObject <NSObject> {
4+
int var1;
5+
BOOL var2;
6+
id var3;
7+
}
8+
9+
+ (instancetype) getAutoreleasedInstance;
10+
+ (int) add: (int) a and: (int) b;
11+
12+
- (int) var1;
13+
- (void) addToVar1: (int) number;
14+
15+
- (BOOL) var2;
16+
17+
- (id) var3;
18+
- (void) setVar3: (id) obj;
19+
@end
20+
21+
22+
@implementation MyTestObject
23+
- (id)init {
24+
self = [super init];
25+
if (self) {
26+
var1 = 42;
27+
var2 = YES;
28+
// var3 = nil;
29+
}
30+
return self;
31+
}
32+
33+
+ (instancetype) getAutoreleasedInstance {
34+
return [[MyTestObject alloc] init];
35+
}
36+
37+
+ (int) add: (int) a and: (int) b {
38+
return a + b;
39+
}
40+
41+
- (int) var1 {
42+
return var1;
43+
}
44+
45+
- (void) addToVar1: (int) number {
46+
var1 += number;
47+
}
48+
49+
- (BOOL) var2 {
50+
return var2;
51+
}
52+
53+
- (id) var3 {
54+
return var3;
55+
}
56+
57+
- (void) setVar3: (id) obj {
58+
var3 = obj;
59+
}
60+
@end

tests/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ extern crate std;
1010
pub mod ffi;
1111
#[cfg(test)]
1212
mod test_encode_utils;
13+
#[cfg(test)]
14+
mod test_object;
15+
1316
use crate::ffi::LargeStruct;
1417

1518
pub fn get_int_block_with(i: i32) -> RcBlock<(), i32> {

tests/src/test_object.rs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
use core::mem::{size_of, ManuallyDrop};
2+
use std::os::raw::c_int;
3+
4+
use objc2::rc::{autoreleasepool, AutoreleasePool, Id, Owned};
5+
use objc2::runtime::{Bool, Class, Object, Protocol};
6+
#[cfg(feature = "malloc")]
7+
use objc2::sel;
8+
use objc2::{class, msg_send, msg_send_bool};
9+
use objc2_foundation::NSObject;
10+
11+
#[repr(C)]
12+
struct MyTestObject {
13+
inner: NSObject,
14+
}
15+
16+
unsafe impl ::objc2::Message for MyTestObject {}
17+
18+
unsafe impl ::objc2::RefEncode for MyTestObject {
19+
const ENCODING_REF: ::objc2::Encoding<'static> = ::objc2::Encoding::Object;
20+
}
21+
22+
impl MyTestObject {
23+
fn class() -> &'static Class {
24+
class!(MyTestObject)
25+
}
26+
27+
fn new() -> Id<Self, Owned> {
28+
let cls = Self::class();
29+
unsafe { Id::new(msg_send![cls, new]).unwrap() }
30+
}
31+
32+
fn new_autoreleased<'p>(pool: &'p AutoreleasePool) -> &'p Self {
33+
let cls = Self::class();
34+
let ptr: *const Self = unsafe { msg_send![cls, getAutoreleasedInstance] };
35+
unsafe { pool.ptr_as_ref(ptr) }
36+
}
37+
38+
fn add_numbers(a: c_int, b: c_int) -> c_int {
39+
let cls = Self::class();
40+
unsafe { msg_send![cls, add: a, and: b] }
41+
}
42+
43+
fn var1(&self) -> c_int {
44+
unsafe { msg_send![self, var1] }
45+
}
46+
47+
fn var1_ivar(&self) -> &c_int {
48+
unsafe { self.inner.ivar("var1") }
49+
}
50+
51+
fn var1_ivar_mut(&mut self) -> &mut c_int {
52+
unsafe { self.inner.ivar_mut("var1") }
53+
}
54+
55+
fn add_to_ivar1(&mut self, number: c_int) {
56+
unsafe { msg_send![self, addToVar1: number] }
57+
}
58+
59+
fn var2(&self) -> bool {
60+
unsafe { msg_send_bool![self, var2] }
61+
}
62+
63+
fn var2_ivar(&self) -> &Bool {
64+
unsafe { self.inner.ivar("var2") }
65+
}
66+
67+
fn var2_ivar_mut(&mut self) -> &mut Bool {
68+
unsafe { self.inner.ivar_mut("var2") }
69+
}
70+
71+
fn var3(&self) -> *mut Object {
72+
unsafe { msg_send![self, var3] }
73+
}
74+
75+
fn set_var3(&mut self, obj: *mut Object) {
76+
unsafe { msg_send![self, setVar3: obj] }
77+
}
78+
79+
fn var3_ivar(&self) -> &*mut Object {
80+
unsafe { self.inner.ivar("var3") }
81+
}
82+
83+
fn var3_ivar_mut(&mut self) -> &mut *mut Object {
84+
unsafe { self.inner.ivar_mut("var3") }
85+
}
86+
}
87+
88+
#[cfg(feature = "malloc")]
89+
macro_rules! assert_in {
90+
($item:expr, $lst:expr) => {{
91+
let mut found = false;
92+
for &x in $lst.iter() {
93+
if x == $item {
94+
found = true;
95+
}
96+
}
97+
assert!(
98+
found,
99+
"Did not find {} in {}",
100+
stringify!($item),
101+
stringify!($lst),
102+
);
103+
}};
104+
}
105+
106+
#[test]
107+
fn test_class() {
108+
let cls = MyTestObject::class();
109+
assert_eq!(MyTestObject::add_numbers(-3, 15), 12);
110+
111+
#[cfg(feature = "malloc")]
112+
{
113+
let classes = Class::classes();
114+
assert_eq!(classes.len(), Class::classes_count());
115+
assert_in!(cls, classes);
116+
}
117+
118+
// Test objc2::runtime functionality
119+
assert_eq!(Class::get("MyTestObject"), Some(cls));
120+
assert_ne!(cls, class!(NSObject));
121+
assert_eq!(cls.name(), "MyTestObject");
122+
assert_eq!(cls.superclass(), Some(class!(NSObject)));
123+
assert_eq!(cls.metaclass().name(), "MyTestObject");
124+
assert_ne!(cls.metaclass(), cls);
125+
assert_eq!(cls.instance_size(), {
126+
#[repr(C)]
127+
struct MyTestObjectLayout {
128+
isa: *const Class,
129+
var1: c_int,
130+
var2: Bool,
131+
var3: *mut NSObject,
132+
}
133+
size_of::<MyTestObjectLayout>()
134+
});
135+
136+
let protocol = Protocol::get("NSObject").unwrap();
137+
assert!(cls.conforms_to(protocol));
138+
assert!(!cls.conforms_to(Protocol::get("NSCopying").unwrap()));
139+
#[cfg(feature = "malloc")]
140+
assert_in!(protocol, cls.adopted_protocols());
141+
142+
#[cfg(feature = "malloc")]
143+
{
144+
let method = cls.instance_method(sel!(addToVar1:)).unwrap();
145+
assert_in!(method, cls.instance_methods());
146+
147+
let ivar = cls.instance_variable("var1").unwrap();
148+
assert_in!(ivar, cls.instance_variables());
149+
}
150+
}
151+
152+
#[test]
153+
fn test_object() {
154+
autoreleasepool(|pool| {
155+
let _obj = MyTestObject::new_autoreleased(pool);
156+
});
157+
158+
let mut obj = MyTestObject::new();
159+
assert_eq!((*obj.inner).class(), MyTestObject::class());
160+
161+
assert_eq!(obj.var1(), 42);
162+
assert_eq!(*obj.var1_ivar(), 42);
163+
164+
obj.add_to_ivar1(3);
165+
assert_eq!(obj.var1(), 45);
166+
assert_eq!(*obj.var1_ivar(), 45);
167+
168+
*obj.var1_ivar_mut() = 100;
169+
assert_eq!(obj.var1(), 100);
170+
assert_eq!(*obj.var1_ivar(), 100);
171+
172+
assert!(obj.var2());
173+
assert!(obj.var2_ivar().is_true());
174+
175+
*obj.var2_ivar_mut() = Bool::NO;
176+
assert!(!obj.var2());
177+
assert!(obj.var2_ivar().is_false());
178+
179+
assert!(obj.var3().is_null());
180+
assert!(obj.var3_ivar().is_null());
181+
182+
let obj2 = ManuallyDrop::new(NSObject::new()).as_ptr().cast::<Object>();
183+
obj.set_var3(obj2);
184+
assert_eq!(obj.var3(), obj2);
185+
assert_eq!(*obj.var3_ivar(), obj2);
186+
187+
let obj3 = ManuallyDrop::new(NSObject::new()).as_ptr().cast::<Object>();
188+
*obj.var3_ivar_mut() = obj3;
189+
assert_ne!(obj.var3(), obj2);
190+
assert_ne!(*obj.var3_ivar(), obj2);
191+
assert_eq!(obj.var3(), obj3);
192+
assert_eq!(*obj.var3_ivar(), obj3);
193+
}

0 commit comments

Comments
 (0)