]> git.saurik.com Git - apple/objc4.git/blob - test/nonpointerisa.m
objc4-818.2.tar.gz
[apple/objc4.git] / test / nonpointerisa.m
1 // TEST_CFLAGS -framework Foundation
2 // TEST_CONFIG MEM=mrc
3
4 #include "test.h"
5 #include <dlfcn.h>
6
7 #include <objc/objc-gdb.h>
8 #include <Foundation/Foundation.h>
9
10 #define ISA(x) (*((uintptr_t *)(x)))
11 #define NONPOINTER(x) (ISA(x) & 1)
12
13 #if SUPPORT_NONPOINTER_ISA
14 # if __x86_64__
15 # define RC_ONE (1ULL<<56)
16 # elif __arm64__ && __LP64__
17 // Quiet the warning about redefining the macro from isa.h.
18 # undef RC_ONE
19 # define RC_ONE (objc_debug_isa_magic_value == 1 ? 1ULL<<56 : 1ULL<<45)
20 # elif __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)
21 # define RC_ONE (1ULL<<25)
22 # else
23 # error unknown architecture
24 # endif
25 #endif
26
27
28 void check_raw_pointer(id obj, Class cls)
29 {
30 testassert(object_getClass(obj) == cls);
31 testassert(!NONPOINTER(obj));
32
33 uintptr_t isa = ISA(obj);
34 testassertequal(ptrauth_strip((void *)isa, ptrauth_key_process_independent_data), (void *)cls);
35 testassertequal((Class)(isa & objc_debug_isa_class_mask), cls);
36 testassertequal(ptrauth_strip((void *)(isa & ~objc_debug_isa_class_mask), ptrauth_key_process_independent_data), 0);
37
38 CFRetain(obj);
39 testassert(ISA(obj) == isa);
40 testassert([obj retainCount] == 2);
41 [obj retain];
42 testassert(ISA(obj) == isa);
43 testassert([obj retainCount] == 3);
44 CFRelease(obj);
45 testassert(ISA(obj) == isa);
46 testassert([obj retainCount] == 2);
47 [obj release];
48 testassert(ISA(obj) == isa);
49 testassert([obj retainCount] == 1);
50 }
51
52
53 #if ! SUPPORT_NONPOINTER_ISA
54
55 int main()
56 {
57 #if OBJC_HAVE_NONPOINTER_ISA || OBJC_HAVE_PACKED_NONPOINTER_ISA || OBJC_HAVE_INDEXED_NONPOINTER_ISA
58 # error wrong
59 #endif
60
61 testprintf("Isa with index\n");
62 id index_o = [NSObject new];
63 check_raw_pointer(index_o, [NSObject class]);
64
65 // These variables DO NOT exist without non-pointer isa support.
66 testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_packed_isa_class_mask"));
67 testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_magic_mask"));
68 testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_magic_value"));
69 testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_index_mask"));
70 testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_index_shift"));
71
72 // These variables DO exist even without non-pointer isa support.
73 testassert(dlsym(RTLD_DEFAULT, "objc_debug_isa_class_mask"));
74 testassert(dlsym(RTLD_DEFAULT, "objc_debug_isa_magic_mask"));
75 testassert(dlsym(RTLD_DEFAULT, "objc_debug_isa_magic_value"));
76
77 succeed(__FILE__);
78 }
79
80 #else
81 // SUPPORT_NONPOINTER_ISA
82
83 void check_nonpointer(id obj, Class cls)
84 {
85 testassertequal(object_getClass(obj), cls);
86 testassert(NONPOINTER(obj));
87
88 uintptr_t isa = ISA(obj);
89
90 if (objc_debug_indexed_isa_magic_mask != 0) {
91 // Indexed isa.
92 testassertequal((isa & objc_debug_indexed_isa_magic_mask), objc_debug_indexed_isa_magic_value);
93 testassert((isa & ~objc_debug_indexed_isa_index_mask) != 0);
94 uintptr_t index = (isa & objc_debug_indexed_isa_index_mask) >> objc_debug_indexed_isa_index_shift;
95 testassert(index < objc_indexed_classes_count);
96 testassertequal(objc_indexed_classes[index], cls);
97 } else {
98 // Packed isa.
99 testassertequal((Class)(isa & objc_debug_isa_class_mask), cls);
100 testassert((Class)(isa & ~objc_debug_isa_class_mask) != 0);
101 testassertequal((isa & objc_debug_isa_magic_mask), objc_debug_isa_magic_value);
102 }
103
104 CFRetain(obj);
105 testassertequal(ISA(obj), isa + RC_ONE);
106 testassertequal([obj retainCount], 2);
107 [obj retain];
108 testassertequal(ISA(obj), isa + RC_ONE*2);
109 testassertequal([obj retainCount], 3);
110 CFRelease(obj);
111 testassertequal(ISA(obj), isa + RC_ONE);
112 testassertequal([obj retainCount], 2);
113 [obj release];
114 testassertequal(ISA(obj), isa);
115 testassertequal([obj retainCount], 1);
116 }
117
118
119 @interface Fake_OS_object : NSObject {
120 int refcnt;
121 int xref_cnt;
122 }
123 @end
124
125 @implementation Fake_OS_object
126 +(void)initialize {
127 static bool initialized;
128 if (!initialized) {
129 initialized = true;
130 testprintf("Nonpointer during +initialize\n");
131 testassert(!NONPOINTER(self));
132 id o = [Fake_OS_object new];
133 check_nonpointer(o, self);
134 [o release];
135 }
136 }
137 @end
138
139 @interface Sub_OS_object : NSObject @end
140
141 @implementation Sub_OS_object
142 @end
143
144
145
146 int main()
147 {
148 Class OS_object = objc_getClass("OS_object");
149 class_setSuperclass([Sub_OS_object class], OS_object);
150
151 uintptr_t isa;
152
153 #if SUPPORT_PACKED_ISA
154 # if !OBJC_HAVE_NONPOINTER_ISA || !OBJC_HAVE_PACKED_NONPOINTER_ISA || OBJC_HAVE_INDEXED_NONPOINTER_ISA
155 # error wrong
156 # endif
157 void *absoluteMask = (void *)&objc_absolute_packed_isa_class_mask;
158 #if __has_feature(ptrauth_calls)
159 absoluteMask = ptrauth_strip(absoluteMask, ptrauth_key_process_independent_data);
160 #endif
161 // absoluteMask should "cover" objc_debug_isa_class_mask
162 testassert((objc_debug_isa_class_mask & (uintptr_t)absoluteMask) == objc_debug_isa_class_mask);
163 // absoluteMask should only possibly differ in the high bits
164 testassert((objc_debug_isa_class_mask & 0xffff) == ((uintptr_t)absoluteMask & 0xffff));
165
166 // Indexed isa variables DO NOT exist on packed-isa platforms
167 testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_magic_mask"));
168 testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_magic_value"));
169 testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_index_mask"));
170 testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_indexed_isa_index_shift"));
171
172 #elif SUPPORT_INDEXED_ISA
173 # if !OBJC_HAVE_NONPOINTER_ISA || OBJC_HAVE_PACKED_NONPOINTER_ISA || !OBJC_HAVE_INDEXED_NONPOINTER_ISA
174 # error wrong
175 # endif
176 testassert(objc_debug_indexed_isa_magic_mask == (uintptr_t)&objc_absolute_indexed_isa_magic_mask);
177 testassert(objc_debug_indexed_isa_magic_value == (uintptr_t)&objc_absolute_indexed_isa_magic_value);
178 testassert(objc_debug_indexed_isa_index_mask == (uintptr_t)&objc_absolute_indexed_isa_index_mask);
179 testassert(objc_debug_indexed_isa_index_shift == (uintptr_t)&objc_absolute_indexed_isa_index_shift);
180
181 // Packed isa variable DOES NOT exist on indexed-isa platforms.
182 testassert(!dlsym(RTLD_DEFAULT, "objc_absolute_packed_isa_class_mask"));
183
184 #else
185 # error unknown nonpointer isa format
186 #endif
187
188 testprintf("Isa with index\n");
189 id index_o = [Fake_OS_object new];
190 check_nonpointer(index_o, [Fake_OS_object class]);
191
192 testprintf("Weakly referenced\n");
193 isa = ISA(index_o);
194 id weak;
195 objc_storeWeak(&weak, index_o);
196 testassert(__builtin_popcountl(isa ^ ISA(index_o)) == 1);
197
198 testprintf("Has associated references\n");
199 id assoc = @"thing";
200 isa = ISA(index_o);
201 objc_setAssociatedObject(index_o, assoc, assoc, OBJC_ASSOCIATION_ASSIGN);
202 testassert(__builtin_popcountl(isa ^ ISA(index_o)) == 1);
203
204 testprintf("Isa without index\n");
205 id raw_o = [OS_object alloc];
206 check_raw_pointer(raw_o, [OS_object class]);
207
208
209 id buf[4];
210 id bufo = (id)buf;
211
212 testprintf("Change isa 0 -> raw pointer\n");
213 bzero(buf, sizeof(buf));
214 object_setClass(bufo, [OS_object class]);
215 check_raw_pointer(bufo, [OS_object class]);
216
217 testprintf("Change isa 0 -> nonpointer\n");
218 bzero(buf, sizeof(buf));
219 object_setClass(bufo, [NSObject class]);
220 check_nonpointer(bufo, [NSObject class]);
221
222 testprintf("Change isa nonpointer -> nonpointer\n");
223 testassert(NONPOINTER(bufo));
224 _objc_rootRetain(bufo);
225 testassert(_objc_rootRetainCount(bufo) == 2);
226 object_setClass(bufo, [Fake_OS_object class]);
227 testassert(_objc_rootRetainCount(bufo) == 2);
228 _objc_rootRelease(bufo);
229 testassert(_objc_rootRetainCount(bufo) == 1);
230 check_nonpointer(bufo, [Fake_OS_object class]);
231
232 testprintf("Change isa nonpointer -> raw pointer\n");
233 // Retain count must be preserved.
234 // Use root* to avoid OS_object's overrides.
235 testassert(NONPOINTER(bufo));
236 _objc_rootRetain(bufo);
237 testassert(_objc_rootRetainCount(bufo) == 2);
238 object_setClass(bufo, [OS_object class]);
239 testassert(_objc_rootRetainCount(bufo) == 2);
240 _objc_rootRelease(bufo);
241 testassert(_objc_rootRetainCount(bufo) == 1);
242 check_raw_pointer(bufo, [OS_object class]);
243
244 testprintf("Change isa raw pointer -> nonpointer (doesn't happen)\n");
245 testassert(!NONPOINTER(bufo));
246 _objc_rootRetain(bufo);
247 testassert(_objc_rootRetainCount(bufo) == 2);
248 object_setClass(bufo, [Fake_OS_object class]);
249 testassert(_objc_rootRetainCount(bufo) == 2);
250 _objc_rootRelease(bufo);
251 testassert(_objc_rootRetainCount(bufo) == 1);
252 check_raw_pointer(bufo, [Fake_OS_object class]);
253
254 testprintf("Change isa raw pointer -> raw pointer\n");
255 testassert(!NONPOINTER(bufo));
256 _objc_rootRetain(bufo);
257 testassert(_objc_rootRetainCount(bufo) == 2);
258 object_setClass(bufo, [Sub_OS_object class]);
259 testassert(_objc_rootRetainCount(bufo) == 2);
260 _objc_rootRelease(bufo);
261 testassert(_objc_rootRetainCount(bufo) == 1);
262 check_raw_pointer(bufo, [Sub_OS_object class]);
263
264
265 succeed(__FILE__);
266 }
267
268 // SUPPORT_NONPOINTER_ISA
269 #endif