taggedPointers.m 12 KB

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