]> git.saurik.com Git - apple/objc4.git/blame - test/taggedPointers.m
objc4-818.2.tar.gz
[apple/objc4.git] / test / taggedPointers.m
CommitLineData
13ba007e
A
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
16static 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
33OBJC_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
64static void *
65retain_fn(void *self, SEL _cmd __unused) {
66 void * (*fn)(void *) = (typeof(fn))_objc_rootRetain;
67 return fn(self);
68}
69
70static void
71release_fn(void *self, SEL _cmd __unused) {
72 void (*fn)(void *) = (typeof(fn))_objc_rootRelease;
73 fn(self);
74}
75
76static void *
77autorelease_fn(void *self, SEL _cmd __unused) {
78 void * (*fn)(void *) = (typeof(fn))_objc_rootAutorelease;
79 return fn(self);
80}
81
82static unsigned long
83retaincount_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
141void 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
196void 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
34d5b5e8
A
298#if OBJC_SPLIT_TAGGED_POINTERS
299void testConstantTaggedPointerRoundTrip(void *ptr)
300{
301 uintptr_t tagged = (uintptr_t)ptr | objc_debug_constant_cfstring_tag_bits;
302 void *untagged = _objc_getTaggedPointerRawPointerValue((void *)tagged);
303 testassert(ptr == untagged);
304}
305
306void testConstantTaggedPointers(void)
307{
308 testConstantTaggedPointerRoundTrip(0);
309 testConstantTaggedPointerRoundTrip((void *)sizeof(void *));
310 testConstantTaggedPointerRoundTrip((void *)(MACH_VM_MAX_ADDRESS - sizeof(void *)));
311}
312#endif
313
13ba007e
A
314int main()
315{
316 testassert(objc_debug_taggedpointer_mask != 0);
317 testassert(_objc_taggedPointersEnabled());
318
319 PUSH_POOL {
320 // Avoid CF's tagged pointer tags because of rdar://11368528
321
322 // Reserved slot should be nil until the
323 // first extended tag is registered.
324 // This test no longer works because XPC now uses extended tags.
325#define HAVE_XPC_TAGS 1
326
327 uintptr_t extSlot = (~objc_debug_taggedpointer_obfuscator >> objc_debug_taggedpointer_slot_shift) & objc_debug_taggedpointer_slot_mask;
328 Class extPlaceholder = objc_getClass("__NSUnrecognizedTaggedPointer");
329 testassert(extPlaceholder != nil);
330
331#if !HAVE_XPC_TAGS
332 testassert(objc_debug_taggedpointer_classes[extSlot] == nil);
333#endif
334
335 _objc_registerTaggedPointerClass(OBJC_TAG_1,
336 objc_getClass("TaggedBaseClass60"));
337 testGenericTaggedPointer(OBJC_TAG_1,
338 objc_getClass("TaggedBaseClass60"));
339
340#if !HAVE_XPC_TAGS
341 testassert(objc_debug_taggedpointer_classes[extSlot] == nil);
342#endif
343
344 _objc_registerTaggedPointerClass(OBJC_TAG_First52BitPayload,
345 objc_getClass("TaggedSubclass52"));
346 testGenericTaggedPointer(OBJC_TAG_First52BitPayload,
347 objc_getClass("TaggedSubclass52"));
348
349 testassert(objc_debug_taggedpointer_classes[extSlot] == extPlaceholder);
350
351 _objc_registerTaggedPointerClass(OBJC_TAG_NSManagedObjectID,
352 objc_getClass("TaggedNSObjectSubclass"));
353 testGenericTaggedPointer(OBJC_TAG_NSManagedObjectID,
354 objc_getClass("TaggedNSObjectSubclass"));
34d5b5e8
A
355
356#if OBJC_SPLIT_TAGGED_POINTERS
357 testConstantTaggedPointers();
358#endif
13ba007e
A
359 } POP_POOL;
360
361 succeed(__FILE__);
362}
363
364// OBJC_HAVE_TAGGED_POINTERS
365#else
366// not OBJC_HAVE_TAGGED_POINTERS
367
368// Tagged pointers not supported.
369
370int main()
371{
372 testassert(objc_debug_taggedpointer_mask == 0);
373 succeed(__FILE__);
374}
375
376#endif