1 // TEST_CFLAGS -fobjc-weak
4 #include <objc/runtime.h>
5 #include <objc/objc-internal.h>
6 #include <objc/objc-gdb.h>
8 #import <Foundation/NSObject.h>
10 #if OBJC_HAVE_TAGGED_POINTERS
12 #if !__x86_64__ && !__arm64__
13 #error wrong architecture for tagged pointers
18 @interface WeakContainer : NSObject
21 __weak id weaks[10000];
24 @implementation WeakContainer
26 for (unsigned int i = 0; i < sizeof(weaks)/sizeof(weaks[0]); i++) {
27 testassert(weaks[i] == nil);
34 @interface TaggedBaseClass60
37 @implementation TaggedBaseClass60
38 -(id) self { return self; }
43 - (void) instanceMethod {
47 - (uintptr_t) taggedValue {
48 return _objc_getTaggedPointerValue((__bridge void*)self);
51 - (struct stret) stret: (struct stret) aStruct {
55 - (long double) fpret: (long double) aValue {
61 fail("TaggedBaseClass60 dealloc called!");
65 retain_fn(void *self, SEL _cmd __unused) {
66 void * (*fn)(void *) = (typeof(fn))_objc_rootRetain;
71 release_fn(void *self, SEL _cmd __unused) {
72 void (*fn)(void *) = (typeof(fn))_objc_rootRelease;
77 autorelease_fn(void *self, SEL _cmd __unused) {
78 void * (*fn)(void *) = (typeof(fn))_objc_rootAutorelease;
83 retaincount_fn(void *self, SEL _cmd __unused) {
84 unsigned long (*fn)(void *) = (typeof(fn))_objc_rootRetainCount;
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, "");
97 @interface TaggedSubclass52: TaggedBaseClass60
100 @implementation TaggedSubclass52
102 - (void) instanceMethod {
103 return [super instanceMethod];
106 - (uintptr_t) taggedValue {
107 return [super taggedValue];
110 - (struct stret) stret: (struct stret) aStruct {
111 return [super stret: aStruct];
114 - (long double) fpret: (long double) aValue {
115 return [super fpret: aValue];
119 @interface TaggedNSObjectSubclass : NSObject
122 @implementation TaggedNSObjectSubclass
124 - (void) instanceMethod {
128 - (uintptr_t) taggedValue {
129 return _objc_getTaggedPointerValue((__bridge void*)self);
132 - (struct stret) stret: (struct stret) aStruct {
136 - (long double) fpret: (long double) aValue {
141 void testTaggedPointerValue(Class cls, objc_tag_index_t tag, uintptr_t value)
143 void *taggedAddress = _objc_makeTaggedPointer(tag, value);
144 testprintf("obj %p, tag %p, value %p\n",
145 taggedAddress, (void*)tag, (void*)value);
147 bool ext = (tag >= OBJC_TAG_First52BitPayload);
149 // _objc_makeTaggedPointer must quietly mask out of range values for now
151 value = (value << 12) >> 12;
153 value = (value << 4) >> 4;
156 testassert(_objc_isTaggedPointer(taggedAddress));
157 testassert(_objc_getTaggedPointerTag(taggedAddress) == tag);
158 testassert(_objc_getTaggedPointerValue(taggedAddress) == value);
159 testassert(objc_debug_taggedpointer_obfuscator != 0);
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);
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);
175 id taggedPointer = (__bridge id)taggedAddress;
176 testassert(!object_isClass(taggedPointer));
177 testassert(object_getClass(taggedPointer) == cls);
178 testassert([taggedPointer taggedValue] == value);
181 [taggedPointer instanceMethod];
184 struct stret orig = STRET_RESULT;
185 testassert(stret_equal(orig, [taggedPointer stret: orig]));
187 long double dblvalue = 3.14156789;
188 testassert(dblvalue == [taggedPointer fpret: dblvalue]);
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);
196 void testGenericTaggedPointer(objc_tag_index_t tag, Class cls)
199 testprintf("%s\n", class_getName(cls));
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);
212 // Tagged pointers should bypass refcount tables and autorelease pools
213 // and weak reference tables
214 WeakContainer *w = [WeakContainer new];
216 // force sidetable retain of the WeakContainer before leak checking
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];
225 // prime is_debug() before leak checking
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);
234 id result = WEAK_STORE(w->weaks[i], o);
235 testassert(result == o);
236 testassert(w->weaks[i] == o);
238 result = WEAK_LOAD(w->weaks[i]);
239 testassert(result == o);
241 uintptr_t rc = _objc_rootRetainCount(o);
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);
261 testassert(_objc_rootRetainCount(o) == rc);
262 _objc_rootAutorelease(o);
263 testassert(_objc_rootRetainCount(o) == rc);
264 #if !__has_feature(objc_arc)
266 testassert(_objc_rootRetainCount(o) == rc);
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);
279 testassert(_objc_rootRetainCount(o) == rc);
283 // libobjc's debug lock checking makes this leak check fail
284 testwarn("skipping leak check with debug libobjc build");
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);
298 #if OBJC_SPLIT_TAGGED_POINTERS
299 void testConstantTaggedPointerRoundTrip(void *ptr)
301 uintptr_t tagged = (uintptr_t)ptr | objc_debug_constant_cfstring_tag_bits;
302 void *untagged = _objc_getTaggedPointerRawPointerValue((void *)tagged);
303 testassert(ptr == untagged);
306 void testConstantTaggedPointers(void)
308 testConstantTaggedPointerRoundTrip(0);
309 testConstantTaggedPointerRoundTrip((void *)sizeof(void *));
310 testConstantTaggedPointerRoundTrip((void *)(MACH_VM_MAX_ADDRESS - sizeof(void *)));
316 testassert(objc_debug_taggedpointer_mask != 0);
317 testassert(_objc_taggedPointersEnabled());
320 // Avoid CF's tagged pointer tags because of rdar://11368528
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
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);
332 testassert(objc_debug_taggedpointer_classes[extSlot] == nil);
335 _objc_registerTaggedPointerClass(OBJC_TAG_1,
336 objc_getClass("TaggedBaseClass60"));
337 testGenericTaggedPointer(OBJC_TAG_1,
338 objc_getClass("TaggedBaseClass60"));
341 testassert(objc_debug_taggedpointer_classes[extSlot] == nil);
344 _objc_registerTaggedPointerClass(OBJC_TAG_First52BitPayload,
345 objc_getClass("TaggedSubclass52"));
346 testGenericTaggedPointer(OBJC_TAG_First52BitPayload,
347 objc_getClass("TaggedSubclass52"));
349 testassert(objc_debug_taggedpointer_classes[extSlot] == extPlaceholder);
351 _objc_registerTaggedPointerClass(OBJC_TAG_NSManagedObjectID,
352 objc_getClass("TaggedNSObjectSubclass"));
353 testGenericTaggedPointer(OBJC_TAG_NSManagedObjectID,
354 objc_getClass("TaggedNSObjectSubclass"));
356 #if OBJC_SPLIT_TAGGED_POINTERS
357 testConstantTaggedPointers();
364 // OBJC_HAVE_TAGGED_POINTERS
366 // not OBJC_HAVE_TAGGED_POINTERS
368 // Tagged pointers not supported.
372 testassert(objc_debug_taggedpointer_mask == 0);