]> git.saurik.com Git - apple/objc4.git/blob - test/taggedPointers.m
objc4-680.tar.gz
[apple/objc4.git] / test / taggedPointers.m
1 // TEST_CONFIG
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 !__OBJC2__ || (!__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 -(void) finalize {
32 for (unsigned int i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) {
33 testassert(weaks[i] == nil);
34 }
35 [super finalize];
36 }
37 @end
38
39 OBJC_ROOT_CLASS
40 @interface TaggedBaseClass
41 @end
42
43 @implementation TaggedBaseClass
44 -(id) self { return self; }
45
46 + (void) initialize {
47 }
48
49 - (void) instanceMethod {
50 didIt = YES;
51 }
52
53 - (uintptr_t) taggedValue {
54 return _objc_getTaggedPointerValue(objc_unretainedPointer(self));
55 }
56
57 - (struct stret) stret: (struct stret) aStruct {
58 return aStruct;
59 }
60
61 - (long double) fpret: (long double) aValue {
62 return aValue;
63 }
64
65
66 -(void) dealloc {
67 fail("TaggedBaseClass dealloc called!");
68 }
69
70 static void *
71 retain_fn(void *self, SEL _cmd __unused) {
72 void * (*fn)(void *) = (typeof(fn))_objc_rootRetain;
73 return fn(self);
74 }
75
76 static void
77 release_fn(void *self, SEL _cmd __unused) {
78 void (*fn)(void *) = (typeof(fn))_objc_rootRelease;
79 fn(self);
80 }
81
82 static void *
83 autorelease_fn(void *self, SEL _cmd __unused) {
84 void * (*fn)(void *) = (typeof(fn))_objc_rootAutorelease;
85 return fn(self);
86 }
87
88 static unsigned long
89 retaincount_fn(void *self, SEL _cmd __unused) {
90 unsigned long (*fn)(void *) = (typeof(fn))_objc_rootRetainCount;
91 return fn(self);
92 }
93
94 +(void) load {
95 class_addMethod(self, sel_registerName("retain"), (IMP)retain_fn, "");
96 class_addMethod(self, sel_registerName("release"), (IMP)release_fn, "");
97 class_addMethod(self, sel_registerName("autorelease"), (IMP)autorelease_fn, "");
98 class_addMethod(self, sel_registerName("retainCount"), (IMP)retaincount_fn, "");
99 }
100
101 @end
102
103 @interface TaggedSubclass: TaggedBaseClass
104 @end
105
106 @implementation TaggedSubclass
107
108 - (void) instanceMethod {
109 return [super instanceMethod];
110 }
111
112 - (uintptr_t) taggedValue {
113 return [super taggedValue];
114 }
115
116 - (struct stret) stret: (struct stret) aStruct {
117 return [super stret: aStruct];
118 }
119
120 - (long double) fpret: (long double) aValue {
121 return [super fpret: aValue];
122 }
123 @end
124
125 @interface TaggedNSObjectSubclass : NSObject
126 @end
127
128 @implementation TaggedNSObjectSubclass
129
130 - (void) instanceMethod {
131 didIt = YES;
132 }
133
134 - (uintptr_t) taggedValue {
135 return _objc_getTaggedPointerValue(objc_unretainedPointer(self));
136 }
137
138 - (struct stret) stret: (struct stret) aStruct {
139 return aStruct;
140 }
141
142 - (long double) fpret: (long double) aValue {
143 return aValue;
144 }
145 @end
146
147 void testTaggedPointerValue(Class cls, objc_tag_index_t tag, uintptr_t value)
148 {
149 void *taggedAddress = _objc_makeTaggedPointer(tag, value);
150 testprintf("obj %p, tag %p, value %p\n",
151 taggedAddress, (void*)tag, (void*)value);
152
153 // _objc_makeTaggedPointer must quietly mask out of range values for now
154 value = (value << 4) >> 4;
155
156 testassert(_objc_isTaggedPointer(taggedAddress));
157 testassert(_objc_getTaggedPointerTag(taggedAddress) == tag);
158 testassert(_objc_getTaggedPointerValue(taggedAddress) == value);
159
160 testassert((uintptr_t)taggedAddress & objc_debug_taggedpointer_mask);
161 uintptr_t slot = ((uintptr_t)taggedAddress >> objc_debug_taggedpointer_slot_shift) & objc_debug_taggedpointer_slot_mask;
162 testassert(objc_debug_taggedpointer_classes[slot] == cls);
163 testassert((((uintptr_t)taggedAddress << objc_debug_taggedpointer_payload_lshift) >> objc_debug_taggedpointer_payload_rshift) == value);
164
165 id taggedPointer = objc_unretainedObject(taggedAddress);
166 testassert(!object_isClass(taggedPointer));
167 testassert(object_getClass(taggedPointer) == cls);
168 testassert([taggedPointer taggedValue] == value);
169
170 didIt = NO;
171 [taggedPointer instanceMethod];
172 testassert(didIt);
173
174 struct stret orig = STRET_RESULT;
175 testassert(stret_equal(orig, [taggedPointer stret: orig]));
176
177 long double dblvalue = 3.14156789;
178 testassert(dblvalue == [taggedPointer fpret: dblvalue]);
179
180 objc_setAssociatedObject(taggedPointer, (__bridge void *)taggedPointer, taggedPointer, OBJC_ASSOCIATION_RETAIN);
181 testassert(objc_getAssociatedObject(taggedPointer, (__bridge void *)taggedPointer) == taggedPointer);
182 objc_setAssociatedObject(taggedPointer, (__bridge void *)taggedPointer, nil, OBJC_ASSOCIATION_RETAIN);
183 testassert(objc_getAssociatedObject(taggedPointer, (__bridge void *)taggedPointer) == nil);
184 }
185
186 void testGenericTaggedPointer(objc_tag_index_t tag, Class cls)
187 {
188 testassert(cls);
189 testprintf("%s\n", class_getName(cls));
190
191 testTaggedPointerValue(cls, tag, 0);
192 testTaggedPointerValue(cls, tag, 1UL << 0);
193 testTaggedPointerValue(cls, tag, 1UL << 1);
194 testTaggedPointerValue(cls, tag, 1UL << 58);
195 testTaggedPointerValue(cls, tag, 1UL << 59);
196 testTaggedPointerValue(cls, tag, ~0UL >> 4);
197 testTaggedPointerValue(cls, tag, ~0UL);
198
199 // Tagged pointers should bypass refcount tables and autorelease pools
200 // and weak reference tables
201 WeakContainer *w = [WeakContainer new];
202 #if !__has_feature(objc_arc)
203 // prime method caches before leak checking
204 id taggedPointer = (id)_objc_makeTaggedPointer(tag, 1234);
205 [taggedPointer retain];
206 [taggedPointer release];
207 [taggedPointer autorelease];
208 #endif
209 leak_mark();
210 for (uintptr_t i = 0; i < sizeof(w->weaks)/sizeof(w->weaks[0]); i++) {
211 id o = objc_unretainedObject(_objc_makeTaggedPointer(tag, i));
212 testassert(object_getClass(o) == cls);
213
214 id result = WEAK_STORE(w->weaks[i], o);
215 testassert(result == o);
216 testassert(w->weaks[i] == o);
217
218 result = WEAK_LOAD(w->weaks[i]);
219 testassert(result == o);
220
221 if (!objc_collectingEnabled()) {
222 uintptr_t rc = _objc_rootRetainCount(o);
223 testassert(rc != 0);
224 _objc_rootRelease(o); testassert(_objc_rootRetainCount(o) == rc);
225 _objc_rootRelease(o); testassert(_objc_rootRetainCount(o) == rc);
226 _objc_rootRetain(o); testassert(_objc_rootRetainCount(o) == rc);
227 _objc_rootRetain(o); testassert(_objc_rootRetainCount(o) == rc);
228 _objc_rootRetain(o); testassert(_objc_rootRetainCount(o) == rc);
229 #if !__has_feature(objc_arc)
230 [o release]; testassert(_objc_rootRetainCount(o) == rc);
231 [o release]; testassert(_objc_rootRetainCount(o) == rc);
232 [o retain]; testassert(_objc_rootRetainCount(o) == rc);
233 [o retain]; testassert(_objc_rootRetainCount(o) == rc);
234 [o retain]; testassert(_objc_rootRetainCount(o) == rc);
235 objc_release(o); testassert(_objc_rootRetainCount(o) == rc);
236 objc_release(o); testassert(_objc_rootRetainCount(o) == rc);
237 objc_retain(o); testassert(_objc_rootRetainCount(o) == rc);
238 objc_retain(o); testassert(_objc_rootRetainCount(o) == rc);
239 objc_retain(o); testassert(_objc_rootRetainCount(o) == rc);
240 #endif
241 PUSH_POOL {
242 testassert(_objc_rootRetainCount(o) == rc);
243 _objc_rootAutorelease(o);
244 testassert(_objc_rootRetainCount(o) == rc);
245 #if !__has_feature(objc_arc)
246 [o autorelease];
247 testassert(_objc_rootRetainCount(o) == rc);
248 objc_autorelease(o);
249 testassert(_objc_rootRetainCount(o) == rc);
250 objc_retainAutorelease(o);
251 testassert(_objc_rootRetainCount(o) == rc);
252 objc_autoreleaseReturnValue(o);
253 testassert(_objc_rootRetainCount(o) == rc);
254 objc_retainAutoreleaseReturnValue(o);
255 testassert(_objc_rootRetainCount(o) == rc);
256 objc_retainAutoreleasedReturnValue(o);
257 testassert(_objc_rootRetainCount(o) == rc);
258 #endif
259 } POP_POOL;
260 testassert(_objc_rootRetainCount(o) == rc);
261 }
262 }
263 leak_check(0);
264 for (uintptr_t i = 0; i < 10000; i++) {
265 testassert(w->weaks[i] != NULL);
266 WEAK_STORE(w->weaks[i], NULL);
267 testassert(w->weaks[i] == NULL);
268 testassert(WEAK_LOAD(w->weaks[i]) == NULL);
269 }
270 RELEASE_VAR(w);
271 }
272
273 int main()
274 {
275 if (objc_collecting_enabled()) {
276 // GC's block objects crash without this
277 dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", RTLD_LAZY);
278 }
279
280 testassert(objc_debug_taggedpointer_mask != 0);
281 testassert(_objc_taggedPointersEnabled());
282
283 PUSH_POOL {
284 // Avoid CF's tagged pointer tags because of rdar://11368528
285
286 _objc_registerTaggedPointerClass(OBJC_TAG_1,
287 objc_getClass("TaggedBaseClass"));
288 testGenericTaggedPointer(OBJC_TAG_1,
289 objc_getClass("TaggedBaseClass"));
290
291 _objc_registerTaggedPointerClass(OBJC_TAG_7,
292 objc_getClass("TaggedSubclass"));
293 testGenericTaggedPointer(OBJC_TAG_7,
294 objc_getClass("TaggedSubclass"));
295
296 _objc_registerTaggedPointerClass(OBJC_TAG_NSManagedObjectID,
297 objc_getClass("TaggedNSObjectSubclass"));
298 testGenericTaggedPointer(OBJC_TAG_NSManagedObjectID,
299 objc_getClass("TaggedNSObjectSubclass"));
300 } POP_POOL;
301
302 succeed(__FILE__);
303 }
304
305 // OBJC_HAVE_TAGGED_POINTERS
306 #else
307 // not OBJC_HAVE_TAGGED_POINTERS
308
309 // Tagged pointers not supported.
310
311 int main()
312 {
313 #if __OBJC2__
314 testassert(objc_debug_taggedpointer_mask == 0);
315 #else
316 testassert(!dlsym(RTLD_DEFAULT, "objc_debug_taggedpointer_mask"));
317 #endif
318
319 succeed(__FILE__);
320 }
321
322 #endif