]> git.saurik.com Git - apple/objc4.git/blob - test/taggedPointers.m
objc4-781.2.tar.gz
[apple/objc4.git] / test / taggedPointers.m
1 // TEST_CFLAGS -fobjc-weak
2
3 #include "test.h"
4 #include <objc/runtime.h>
5 #include <objc/objc-internal.h>
6 #include <objc/objc-gdb.h>
7 #include <dlfcn.h>
8 #import <Foundation/NSObject.h>
9
10 #if OBJC_HAVE_TAGGED_POINTERS
11
12 #if !__x86_64__ && !__arm64__
13 #error wrong architecture for tagged pointers
14 #endif
15
16 static BOOL didIt;
17
18 @interface WeakContainer : NSObject
19 {
20 @public
21 __weak id weaks[10000];
22 }
23 @end
24 @implementation WeakContainer
25 -(void) dealloc {
26 for (unsigned int i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) {
27 testassert(weaks[i] == nil);
28 }
29 SUPER_DEALLOC();
30 }
31 @end
32
33 OBJC_ROOT_CLASS
34 @interface TaggedBaseClass60
35 @end
36
37 @implementation TaggedBaseClass60
38 -(id) self { return self; }
39
40 + (void) initialize {
41 }
42
43 - (void) instanceMethod {
44 didIt = YES;
45 }
46
47 - (uintptr_t) taggedValue {
48 return _objc_getTaggedPointerValue((__bridge void*)self);
49 }
50
51 - (struct stret) stret: (struct stret) aStruct {
52 return aStruct;
53 }
54
55 - (long double) fpret: (long double) aValue {
56 return aValue;
57 }
58
59
60 -(void) dealloc {
61 fail("TaggedBaseClass60 dealloc called!");
62 }
63
64 static void *
65 retain_fn(void *self, SEL _cmd __unused) {
66 void * (*fn)(void *) = (typeof(fn))_objc_rootRetain;
67 return fn(self);
68 }
69
70 static void
71 release_fn(void *self, SEL _cmd __unused) {
72 void (*fn)(void *) = (typeof(fn))_objc_rootRelease;
73 fn(self);
74 }
75
76 static void *
77 autorelease_fn(void *self, SEL _cmd __unused) {
78 void * (*fn)(void *) = (typeof(fn))_objc_rootAutorelease;
79 return fn(self);
80 }
81
82 static unsigned long
83 retaincount_fn(void *self, SEL _cmd __unused) {
84 unsigned long (*fn)(void *) = (typeof(fn))_objc_rootRetainCount;
85 return fn(self);
86 }
87
88 +(void) load {
89 class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
90 class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
91 class_addMethod(self, sel_registerName("autorelease"), (IMP)autorelease_fn, "");
92 class_addMethod(self, sel_registerName("retainCount"), (IMP)retaincount_fn, "");
93 }
94
95 @end
96
97 @interface TaggedSubclass52: TaggedBaseClass60
98 @end
99
100 @implementation TaggedSubclass52
101
102 - (void) instanceMethod {
103 return [super instanceMethod];
104 }
105
106 - (uintptr_t) taggedValue {
107 return [super taggedValue];
108 }
109
110 - (struct stret) stret: (struct stret) aStruct {
111 return [super stret: aStruct];
112 }
113
114 - (long double) fpret: (long double) aValue {
115 return [super fpret: aValue];
116 }
117 @end
118
119 @interface TaggedNSObjectSubclass : NSObject
120 @end
121
122 @implementation TaggedNSObjectSubclass
123
124 - (void) instanceMethod {
125 didIt = YES;
126 }
127
128 - (uintptr_t) taggedValue {
129 return _objc_getTaggedPointerValue((__bridge void*)self);
130 }
131
132 - (struct stret) stret: (struct stret) aStruct {
133 return aStruct;
134 }
135
136 - (long double) fpret: (long double) aValue {
137 return aValue;
138 }
139 @end
140
141 void testTaggedPointerValue(Class cls, objc_tag_index_t tag, uintptr_t value)
142 {
143 void *taggedAddress = _objc_makeTaggedPointer(tag, value);
144 testprintf("obj %p, tag %p, value %p\n",
145 taggedAddress, (void*)tag, (void*)value);
146
147 bool ext = (tag >= OBJC_TAG_First52BitPayload);
148
149 // _objc_makeTaggedPointer must quietly mask out of range values for now
150 if (ext) {
151 value = (value << 12) >> 12;
152 } else {
153 value = (value << 4) >> 4;
154 }
155
156 testassert(_objc_isTaggedPointer(taggedAddress));
157 testassert(_objc_getTaggedPointerTag(taggedAddress) == tag);
158 testassert(_objc_getTaggedPointerValue(taggedAddress) == value);
159 testassert(objc_debug_taggedpointer_obfuscator != 0);
160
161 if (ext) {
162 uintptr_t slot = ((uintptr_t)taggedAddress >> objc_debug_taggedpointer_ext_slot_shift) & objc_debug_taggedpointer_ext_slot_mask;
163 testassert(objc_debug_taggedpointer_ext_classes[slot] == cls);
164 uintptr_t deobfuscated = (uintptr_t)taggedAddress ^ objc_debug_taggedpointer_obfuscator;
165 testassert(((deobfuscated << objc_debug_taggedpointer_ext_payload_lshift) >> objc_debug_taggedpointer_ext_payload_rshift) == value);
166 }
167 else {
168 testassert(((uintptr_t)taggedAddress & objc_debug_taggedpointer_mask) == objc_debug_taggedpointer_mask);
169 uintptr_t slot = ((uintptr_t)taggedAddress >> objc_debug_taggedpointer_slot_shift) & objc_debug_taggedpointer_slot_mask;
170 testassert(objc_debug_taggedpointer_classes[slot] == cls);
171 uintptr_t deobfuscated = (uintptr_t)taggedAddress ^ objc_debug_taggedpointer_obfuscator;
172 testassert(((deobfuscated << objc_debug_taggedpointer_payload_lshift) >> objc_debug_taggedpointer_payload_rshift) == value);
173 }
174
175 id taggedPointer = (__bridge id)taggedAddress;
176 testassert(!object_isClass(taggedPointer));
177 testassert(object_getClass(taggedPointer) == cls);
178 testassert([taggedPointer taggedValue] == value);
179
180 didIt = NO;
181 [taggedPointer instanceMethod];
182 testassert(didIt);
183
184 struct stret orig = STRET_RESULT;
185 testassert(stret_equal(orig, [taggedPointer stret: orig]));
186
187 long double dblvalue = 3.14156789;
188 testassert(dblvalue == [taggedPointer fpret: dblvalue]);
189
190 objc_setAssociatedObject(taggedPointer, (__bridge void *)taggedPointer, taggedPointer, OBJC_ASSOCIATION_RETAIN);
191 testassert(objc_getAssociatedObject(taggedPointer, (__bridge void *)taggedPointer) == taggedPointer);
192 objc_setAssociatedObject(taggedPointer, (__bridge void *)taggedPointer, nil, OBJC_ASSOCIATION_RETAIN);
193 testassert(objc_getAssociatedObject(taggedPointer, (__bridge void *)taggedPointer) == nil);
194 }
195
196 void testGenericTaggedPointer(objc_tag_index_t tag, Class cls)
197 {
198 testassert(cls);
199 testprintf("%s\n", class_getName(cls));
200
201 testTaggedPointerValue(cls, tag, 0);
202 testTaggedPointerValue(cls, tag, 1UL << 0);
203 testTaggedPointerValue(cls, tag, 1UL << 1);
204 testTaggedPointerValue(cls, tag, 1UL << 50);
205 testTaggedPointerValue(cls, tag, 1UL << 51);
206 testTaggedPointerValue(cls, tag, 1UL << 52);
207 testTaggedPointerValue(cls, tag, 1UL << 58);
208 testTaggedPointerValue(cls, tag, 1UL << 59);
209 testTaggedPointerValue(cls, tag, ~0UL >> 4);
210 testTaggedPointerValue(cls, tag, ~0UL);
211
212 // Tagged pointers should bypass refcount tables and autorelease pools
213 // and weak reference tables
214 WeakContainer *w = [WeakContainer new];
215
216 // force sidetable retain of the WeakContainer before leak checking
217 objc_retain(w);
218 #if !__has_feature(objc_arc)
219 // prime method caches before leak checking
220 id taggedPointer = (id)_objc_makeTaggedPointer(tag, 1234);
221 [taggedPointer retain];
222 [taggedPointer release];
223 [taggedPointer autorelease];
224 #endif
225 // prime is_debug() before leak checking
226 (void)is_debug();
227
228 leak_mark();
229 testonthread(^(void) {
230 for (uintptr_t i = 0; i < sizeof(w->weaks)/sizeof(w->weaks[0]); i++) {
231 id o = (__bridge id)_objc_makeTaggedPointer(tag, i);
232 testassert(object_getClass(o) == cls);
233
234 id result = WEAK_STORE(w->weaks[i], o);
235 testassert(result == o);
236 testassert(w->weaks[i] == o);
237
238 result = WEAK_LOAD(w->weaks[i]);
239 testassert(result == o);
240
241 uintptr_t rc = _objc_rootRetainCount(o);
242 testassert(rc != 0);
243 _objc_rootRelease(o); testassert(_objc_rootRetainCount(o) == rc);
244 _objc_rootRelease(o); testassert(_objc_rootRetainCount(o) == rc);
245 _objc_rootRetain(o); testassert(_objc_rootRetainCount(o) == rc);
246 _objc_rootRetain(o); testassert(_objc_rootRetainCount(o) == rc);
247 _objc_rootRetain(o); testassert(_objc_rootRetainCount(o) == rc);
248 #if !__has_feature(objc_arc)
249 [o release]; testassert(_objc_rootRetainCount(o) == rc);
250 [o release]; testassert(_objc_rootRetainCount(o) == rc);
251 [o retain]; testassert(_objc_rootRetainCount(o) == rc);
252 [o retain]; testassert(_objc_rootRetainCount(o) == rc);
253 [o retain]; testassert(_objc_rootRetainCount(o) == rc);
254 objc_release(o); testassert(_objc_rootRetainCount(o) == rc);
255 objc_release(o); testassert(_objc_rootRetainCount(o) == rc);
256 objc_retain(o); testassert(_objc_rootRetainCount(o) == rc);
257 objc_retain(o); testassert(_objc_rootRetainCount(o) == rc);
258 objc_retain(o); testassert(_objc_rootRetainCount(o) == rc);
259 #endif
260 PUSH_POOL {
261 testassert(_objc_rootRetainCount(o) == rc);
262 _objc_rootAutorelease(o);
263 testassert(_objc_rootRetainCount(o) == rc);
264 #if !__has_feature(objc_arc)
265 [o autorelease];
266 testassert(_objc_rootRetainCount(o) == rc);
267 objc_autorelease(o);
268 testassert(_objc_rootRetainCount(o) == rc);
269 objc_retainAutorelease(o);
270 testassert(_objc_rootRetainCount(o) == rc);
271 objc_autoreleaseReturnValue(o);
272 testassert(_objc_rootRetainCount(o) == rc);
273 objc_retainAutoreleaseReturnValue(o);
274 testassert(_objc_rootRetainCount(o) == rc);
275 objc_retainAutoreleasedReturnValue(o);
276 testassert(_objc_rootRetainCount(o) == rc);
277 #endif
278 } POP_POOL;
279 testassert(_objc_rootRetainCount(o) == rc);
280 }
281 });
282 if (is_debug()) {
283 // libobjc's debug lock checking makes this leak check fail
284 testwarn("skipping leak check with debug libobjc build");
285 } else {
286 leak_check(0);
287 }
288 for (uintptr_t i = 0; i < 10000; i++) {
289 testassert(w->weaks[i] != NULL);
290 WEAK_STORE(w->weaks[i], NULL);
291 testassert(w->weaks[i] == NULL);
292 testassert(WEAK_LOAD(w->weaks[i]) == NULL);
293 }
294 objc_release(w);
295 RELEASE_VAR(w);
296 }
297
298 int main()
299 {
300 testassert(objc_debug_taggedpointer_mask != 0);
301 testassert(_objc_taggedPointersEnabled());
302
303 PUSH_POOL {
304 // Avoid CF's tagged pointer tags because of rdar://11368528
305
306 // Reserved slot should be nil until the
307 // first extended tag is registered.
308 // This test no longer works because XPC now uses extended tags.
309 #define HAVE_XPC_TAGS 1
310
311 uintptr_t extSlot = (~objc_debug_taggedpointer_obfuscator >> objc_debug_taggedpointer_slot_shift) & objc_debug_taggedpointer_slot_mask;
312 Class extPlaceholder = objc_getClass("__NSUnrecognizedTaggedPointer");
313 testassert(extPlaceholder != nil);
314
315 #if !HAVE_XPC_TAGS
316 testassert(objc_debug_taggedpointer_classes[extSlot] == nil);
317 #endif
318
319 _objc_registerTaggedPointerClass(OBJC_TAG_1,
320 objc_getClass("TaggedBaseClass60"));
321 testGenericTaggedPointer(OBJC_TAG_1,
322 objc_getClass("TaggedBaseClass60"));
323
324 #if !HAVE_XPC_TAGS
325 testassert(objc_debug_taggedpointer_classes[extSlot] == nil);
326 #endif
327
328 _objc_registerTaggedPointerClass(OBJC_TAG_First52BitPayload,
329 objc_getClass("TaggedSubclass52"));
330 testGenericTaggedPointer(OBJC_TAG_First52BitPayload,
331 objc_getClass("TaggedSubclass52"));
332
333 testassert(objc_debug_taggedpointer_classes[extSlot] == extPlaceholder);
334
335 _objc_registerTaggedPointerClass(OBJC_TAG_NSManagedObjectID,
336 objc_getClass("TaggedNSObjectSubclass"));
337 testGenericTaggedPointer(OBJC_TAG_NSManagedObjectID,
338 objc_getClass("TaggedNSObjectSubclass"));
339 } POP_POOL;
340
341 succeed(__FILE__);
342 }
343
344 // OBJC_HAVE_TAGGED_POINTERS
345 #else
346 // not OBJC_HAVE_TAGGED_POINTERS
347
348 // Tagged pointers not supported.
349
350 int main()
351 {
352 testassert(objc_debug_taggedpointer_mask == 0);
353 succeed(__FILE__);
354 }
355
356 #endif