customrr-nsobject.m 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // This file is used in the customrr-nsobject-*.m tests
  2. #include "test.h"
  3. #include <objc/NSObject.h>
  4. #include <objc/objc-internal.h>
  5. #if __has_feature(ptrauth_calls)
  6. typedef IMP __ptrauth_objc_method_list_imp MethodListIMP;
  7. #else
  8. typedef IMP MethodListIMP;
  9. #endif
  10. static int Retains;
  11. static int Releases;
  12. static int Autoreleases;
  13. static int PlusInitializes;
  14. static int Allocs;
  15. static int AllocWithZones;
  16. static int Inits;
  17. static int PlusNew;
  18. static int Self;
  19. static int PlusSelf;
  20. id (*RealRetain)(id self, SEL _cmd);
  21. void (*RealRelease)(id self, SEL _cmd);
  22. id (*RealAutorelease)(id self, SEL _cmd);
  23. id (*RealAlloc)(id self, SEL _cmd);
  24. id (*RealAllocWithZone)(id self, SEL _cmd, void *zone);
  25. id (*RealPlusNew)(id self, SEL _cmd);
  26. id (*RealSelf)(id self);
  27. id (*RealPlusSelf)(id self);
  28. id HackRetain(id self, SEL _cmd) { Retains++; return RealRetain(self, _cmd); }
  29. void HackRelease(id self, SEL _cmd) { Releases++; return RealRelease(self, _cmd); }
  30. id HackAutorelease(id self, SEL _cmd) { Autoreleases++; return RealAutorelease(self, _cmd); }
  31. id HackAlloc(Class self, SEL _cmd) { Allocs++; return RealAlloc(self, _cmd); }
  32. id HackAllocWithZone(Class self, SEL _cmd, void *zone) { AllocWithZones++; return RealAllocWithZone(self, _cmd, zone); }
  33. void HackPlusInitialize(id self __unused, SEL _cmd __unused) { PlusInitializes++; }
  34. id HackInit(id self, SEL _cmd __unused) { Inits++; return self; }
  35. id HackPlusNew(id self, SEL _cmd __unused) { PlusNew++; return RealPlusNew(self, _cmd); }
  36. id HackSelf(id self) { Self++; return RealSelf(self); }
  37. id HackPlusSelf(id self) { PlusSelf++; return RealPlusSelf(self); }
  38. int main(int argc __unused, char **argv)
  39. {
  40. Class cls = objc_getClass("NSObject");
  41. Method meth;
  42. meth = class_getClassMethod(cls, @selector(initialize));
  43. method_setImplementation(meth, (IMP)HackPlusInitialize);
  44. // We either swizzle the method normally (testing that it properly
  45. // disables optimizations), or we hack the implementation into place
  46. // behind objc's back (so we can see whether it got called with the
  47. // optimizations still enabled).
  48. meth = class_getClassMethod(cls, @selector(allocWithZone:));
  49. RealAllocWithZone = (typeof(RealAllocWithZone))method_getImplementation(meth);
  50. #if SWIZZLE_AWZ
  51. method_setImplementation(meth, (IMP)HackAllocWithZone);
  52. #else
  53. ((MethodListIMP *)meth)[2] = (IMP)HackAllocWithZone;
  54. #endif
  55. meth = class_getClassMethod(cls, @selector(new));
  56. RealPlusNew = (typeof(RealPlusNew))method_getImplementation(meth);
  57. #if SWIZZLE_CORE
  58. method_setImplementation(meth, (IMP)HackPlusNew);
  59. #else
  60. ((MethodListIMP *)meth)[2] = (IMP)HackPlusNew;
  61. #endif
  62. meth = class_getClassMethod(cls, @selector(self));
  63. RealPlusSelf = (typeof(RealPlusSelf))method_getImplementation(meth);
  64. #if SWIZZLE_CORE
  65. method_setImplementation(meth, (IMP)HackPlusSelf);
  66. #else
  67. ((MethodListIMP *)meth)[2] = (IMP)HackPlusSelf;
  68. #endif
  69. meth = class_getInstanceMethod(cls, @selector(self));
  70. RealSelf = (typeof(RealSelf))method_getImplementation(meth);
  71. #if SWIZZLE_CORE
  72. method_setImplementation(meth, (IMP)HackSelf);
  73. #else
  74. ((MethodListIMP *)meth)[2] = (IMP)HackSelf;
  75. #endif
  76. meth = class_getInstanceMethod(cls, @selector(release));
  77. RealRelease = (typeof(RealRelease))method_getImplementation(meth);
  78. #if SWIZZLE_RELEASE
  79. method_setImplementation(meth, (IMP)HackRelease);
  80. #else
  81. ((MethodListIMP *)meth)[2] = (IMP)HackRelease;
  82. #endif
  83. // These other methods get hacked for counting purposes only
  84. meth = class_getInstanceMethod(cls, @selector(retain));
  85. RealRetain = (typeof(RealRetain))method_getImplementation(meth);
  86. ((MethodListIMP *)meth)[2] = (IMP)HackRetain;
  87. meth = class_getInstanceMethod(cls, @selector(autorelease));
  88. RealAutorelease = (typeof(RealAutorelease))method_getImplementation(meth);
  89. ((MethodListIMP *)meth)[2] = (IMP)HackAutorelease;
  90. meth = class_getClassMethod(cls, @selector(alloc));
  91. RealAlloc = (typeof(RealAlloc))method_getImplementation(meth);
  92. ((MethodListIMP *)meth)[2] = (IMP)HackAlloc;
  93. meth = class_getInstanceMethod(cls, @selector(init));
  94. ((MethodListIMP *)meth)[2] = (IMP)HackInit;
  95. // Verify that the swizzles occurred before +initialize by provoking it now
  96. testassert(PlusInitializes == 0);
  97. [NSObject self];
  98. testassert(PlusInitializes == 1);
  99. id obj;
  100. id result;
  101. Allocs = 0;
  102. AllocWithZones = 0;
  103. Inits = 0;
  104. obj = objc_alloc(cls);
  105. #if SWIZZLE_AWZ
  106. testprintf("swizzled AWZ should be called\n");
  107. testassert(Allocs == 1);
  108. testassert(AllocWithZones == 1);
  109. testassert(Inits == 0);
  110. #else
  111. testprintf("unswizzled AWZ should be bypassed\n");
  112. testassert(Allocs == 0);
  113. testassert(AllocWithZones == 0);
  114. testassert(Inits == 0);
  115. #endif
  116. testassert([obj isKindOfClass:[NSObject class]]);
  117. Allocs = 0;
  118. AllocWithZones = 0;
  119. Inits = 0;
  120. obj = [NSObject alloc];
  121. #if SWIZZLE_AWZ
  122. testprintf("swizzled AWZ should be called\n");
  123. testassert(Allocs == 1);
  124. testassert(AllocWithZones == 1);
  125. testassert(Inits == 0);
  126. #else
  127. testprintf("unswizzled AWZ should be bypassed\n");
  128. testassert(Allocs == 1);
  129. testassert(AllocWithZones == 0);
  130. testassert(Inits == 0);
  131. #endif
  132. testassert([obj isKindOfClass:[NSObject class]]);
  133. Allocs = 0;
  134. AllocWithZones = 0;
  135. Inits = 0;
  136. obj = objc_alloc_init(cls);
  137. #if SWIZZLE_AWZ
  138. testprintf("swizzled AWZ should be called\n");
  139. testassert(Allocs == 1);
  140. testassert(AllocWithZones == 1);
  141. testassert(Inits == 1);
  142. #else
  143. testprintf("unswizzled AWZ should be bypassed\n");
  144. testassert(Allocs == 0);
  145. testassert(AllocWithZones == 0);
  146. testassert(Inits == 1); // swizzled init is still called
  147. #endif
  148. testassert([obj isKindOfClass:[NSObject class]]);
  149. Retains = 0;
  150. result = objc_retain(obj);
  151. #if SWIZZLE_RELEASE
  152. testprintf("swizzled release should force retain\n");
  153. testassert(Retains == 1);
  154. #else
  155. testprintf("unswizzled release should bypass retain\n");
  156. testassert(Retains == 0);
  157. #endif
  158. testassert(result == obj);
  159. Releases = 0;
  160. Autoreleases = 0;
  161. PUSH_POOL {
  162. result = objc_autorelease(obj);
  163. #if SWIZZLE_RELEASE
  164. testprintf("swizzled release should force autorelease\n");
  165. testassert(Autoreleases == 1);
  166. #else
  167. testprintf("unswizzled release should bypass autorelease\n");
  168. testassert(Autoreleases == 0);
  169. #endif
  170. testassert(result == obj);
  171. } POP_POOL
  172. #if SWIZZLE_RELEASE
  173. testprintf("swizzled release should be called\n");
  174. testassert(Releases == 1);
  175. #else
  176. testprintf("unswizzled release should be bypassed\n");
  177. testassert(Releases == 0);
  178. #endif
  179. PlusNew = 0;
  180. Self = 0;
  181. PlusSelf = 0;
  182. Class nso = objc_opt_self([NSObject class]);
  183. obj = objc_opt_new(nso);
  184. obj = objc_opt_self(obj);
  185. #if SWIZZLE_CORE
  186. testprintf("swizzled Core should be called\n");
  187. testassert(PlusNew == 1);
  188. testassert(Self == 1);
  189. testassert(PlusSelf == 1);
  190. #else
  191. testprintf("unswizzled CORE should be bypassed\n");
  192. testassert(PlusNew == 0);
  193. testassert(Self == 0);
  194. testassert(PlusSelf == 0);
  195. #endif
  196. testassert([obj isKindOfClass:nso]);
  197. succeed(basename(argv[0]));
  198. }