1 // These options must match customrr2.m
5 $C{COMPILE} $DIR/customrr.m -fvisibility=default -o customrr.exe -fno-objc-convert-messages-to-runtime-calls
6 $C{COMPILE} -bundle -bundle_loader customrr.exe $DIR/customrr-cat1.m -o customrr-cat1.bundle
7 $C{COMPILE} -bundle -bundle_loader customrr.exe $DIR/customrr-cat2.m -o customrr-cat2.bundle
14 #include <Foundation/NSObject.h>
18 static int Autoreleases;
19 static int RetainCounts;
20 static int PlusRetains;
21 static int PlusReleases;
22 static int PlusAutoreleases;
23 static int PlusRetainCounts;
25 static int AllocWithZones;
27 static int SubRetains;
28 static int SubReleases;
29 static int SubAutoreleases;
30 static int SubRetainCounts;
31 static int SubPlusRetains;
32 static int SubPlusReleases;
33 static int SubPlusAutoreleases;
34 static int SubPlusRetainCounts;
36 static int SubAllocWithZones;
40 static id imp_fn(id self, SEL _cmd __unused, ...)
46 static void zero(void) {
64 SubPlusAutoreleases = 0;
65 SubPlusRetainCounts = 0;
67 SubAllocWithZones = 0;
73 id HackRetain(id self, SEL _cmd __unused) { Retains++; return self; }
74 void HackRelease(id self __unused, SEL _cmd __unused) { Releases++; }
75 id HackAutorelease(id self, SEL _cmd __unused) { Autoreleases++; return self; }
76 NSUInteger HackRetainCount(id self __unused, SEL _cmd __unused) { RetainCounts++; return 1; }
77 id HackPlusRetain(id self, SEL _cmd __unused) { PlusRetains++; return self; }
78 void HackPlusRelease(id self __unused, SEL _cmd __unused) { PlusReleases++; }
79 id HackPlusAutorelease(id self, SEL _cmd __unused) { PlusAutoreleases++; return self; }
80 NSUInteger HackPlusRetainCount(id self __unused, SEL _cmd __unused) { PlusRetainCounts++; return 1; }
81 id HackAlloc(Class self, SEL _cmd __unused) { Allocs++; return class_createInstance(self, 0); }
82 id HackAllocWithZone(Class self, SEL _cmd __unused) { AllocWithZones++; return class_createInstance(self, 0); }
85 @interface OverridingSub : NSObject @end
86 @implementation OverridingSub
88 -(id) retain { SubRetains++; return self; }
89 +(id) retain { SubPlusRetains++; return self; }
90 -(oneway void) release { SubReleases++; }
91 +(oneway void) release { SubPlusReleases++; }
92 -(id) autorelease { SubAutoreleases++; return self; }
93 +(id) autorelease { SubPlusAutoreleases++; return self; }
94 -(NSUInteger) retainCount { SubRetainCounts++; return 1; }
95 +(NSUInteger) retainCount { SubPlusRetainCounts++; return 1; }
99 @interface OverridingASub : NSObject @end
100 @implementation OverridingASub
101 +(id) alloc { SubAllocs++; return class_createInstance(self, 0); }
104 @interface OverridingAWZSub : NSObject @end
105 @implementation OverridingAWZSub
106 +(id) allocWithZone:(NSZone * __unused)z { SubAllocWithZones++; return class_createInstance(self, 0); }
109 @interface OverridingAAWZSub : NSObject @end
110 @implementation OverridingAAWZSub
111 +(id) alloc { SubAllocs++; return class_createInstance(self, 0); }
112 +(id) allocWithZone:(NSZone * __unused)z { SubAllocWithZones++; return class_createInstance(self, 0); }
116 @interface InheritingSub : NSObject @end
117 @implementation InheritingSub @end
119 @interface InheritingSub2 : NSObject @end
120 @implementation InheritingSub2 @end
121 @interface InheritingSub2_2 : InheritingSub2 @end
122 @implementation InheritingSub2_2 @end
124 @interface InheritingSub3 : NSObject @end
125 @implementation InheritingSub3 @end
126 @interface InheritingSub3_2 : InheritingSub3 @end
127 @implementation InheritingSub3_2 @end
129 @interface InheritingSub4 : NSObject @end
130 @implementation InheritingSub4 @end
131 @interface InheritingSub4_2 : InheritingSub4 @end
132 @implementation InheritingSub4_2 @end
134 @interface InheritingSub5 : NSObject @end
135 @implementation InheritingSub5 @end
136 @interface InheritingSub5_2 : InheritingSub5 @end
137 @implementation InheritingSub5_2 @end
139 @interface InheritingSub6 : NSObject @end
140 @implementation InheritingSub6 @end
141 @interface InheritingSub6_2 : InheritingSub6 @end
142 @implementation InheritingSub6_2 @end
144 @interface InheritingSub7 : NSObject @end
145 @implementation InheritingSub7 @end
146 @interface InheritingSub7_2 : InheritingSub7 @end
147 @implementation InheritingSub7_2 @end
149 @interface InheritingSubCat : NSObject @end
150 @implementation InheritingSubCat @end
151 @interface InheritingSubCat_2 : InheritingSubCat @end
152 @implementation InheritingSubCat_2 @end
155 extern uintptr_t OBJC_CLASS_$_UnrealizedSubA1;
156 @interface UnrealizedSubA1 : NSObject @end
157 @implementation UnrealizedSubA1 @end
158 extern uintptr_t OBJC_CLASS_$_UnrealizedSubA2;
159 @interface UnrealizedSubA2 : NSObject @end
160 @implementation UnrealizedSubA2 @end
161 extern uintptr_t OBJC_CLASS_$_UnrealizedSubA3;
162 @interface UnrealizedSubA3 : NSObject @end
163 @implementation UnrealizedSubA3 @end
165 extern uintptr_t OBJC_CLASS_$_UnrealizedSubB1;
166 @interface UnrealizedSubB1 : NSObject @end
167 @implementation UnrealizedSubB1 @end
168 extern uintptr_t OBJC_CLASS_$_UnrealizedSubB2;
169 @interface UnrealizedSubB2 : NSObject @end
170 @implementation UnrealizedSubB2 @end
171 extern uintptr_t OBJC_CLASS_$_UnrealizedSubB3;
172 @interface UnrealizedSubB3 : NSObject @end
173 @implementation UnrealizedSubB3 @end
175 extern uintptr_t OBJC_CLASS_$_UnrealizedSubC1;
176 @interface UnrealizedSubC1 : NSObject @end
177 @implementation UnrealizedSubC1 @end
178 extern uintptr_t OBJC_CLASS_$_UnrealizedSubC2;
179 @interface UnrealizedSubC2 : NSObject @end
180 @implementation UnrealizedSubC2 @end
181 extern uintptr_t OBJC_CLASS_$_UnrealizedSubC3;
182 @interface UnrealizedSubC3 : NSObject @end
183 @implementation UnrealizedSubC3 @end
186 int main(int argc __unused, char **argv)
188 objc_autoreleasePoolPush();
190 // Hack NSObject's RR methods.
191 // Don't use runtime functions to do this -
192 // we want the runtime to think that these are NSObject's real code
194 #if __has_feature(ptrauth_calls)
195 typedef IMP __ptrauth_objc_method_list_imp MethodListIMP;
197 typedef IMP MethodListIMP;
200 Class cls = [NSObject class];
201 IMP imp = class_getMethodImplementation(cls, @selector(retain));
202 MethodListIMP *m = (MethodListIMP *)
203 class_getInstanceMethod(cls, @selector(retain));
204 testassert(m[2] == imp); // verify Method struct is as we expect
206 m = (MethodListIMP *)class_getInstanceMethod(cls, @selector(retain));
207 m[2] = (IMP)HackRetain;
208 m = (MethodListIMP *)class_getInstanceMethod(cls, @selector(release));
209 m[2] = (IMP)HackRelease;
210 m = (MethodListIMP *)class_getInstanceMethod(cls, @selector(autorelease));
211 m[2] = (IMP)HackAutorelease;
212 m = (MethodListIMP *)class_getInstanceMethod(cls, @selector(retainCount));
213 m[2] = (IMP)HackRetainCount;
214 m = (MethodListIMP *)class_getClassMethod(cls, @selector(retain));
215 m[2] = (IMP)HackPlusRetain;
216 m = (MethodListIMP *)class_getClassMethod(cls, @selector(release));
217 m[2] = (IMP)HackPlusRelease;
218 m = (MethodListIMP *)class_getClassMethod(cls, @selector(autorelease));
219 m[2] = (IMP)HackPlusAutorelease;
220 m = (MethodListIMP *)class_getClassMethod(cls, @selector(retainCount));
221 m[2] = (IMP)HackPlusRetainCount;
222 m = (MethodListIMP *)class_getClassMethod(cls, @selector(alloc));
223 m[2] = (IMP)HackAlloc;
224 m = (MethodListIMP *)class_getClassMethod(cls, @selector(allocWithZone:));
225 m[2] = (IMP)HackAllocWithZone;
227 _objc_flush_caches(cls);
229 imp = class_getMethodImplementation(cls, @selector(retain));
230 testassert(imp == (IMP)HackRetain); // verify hack worked
233 Class cls = [NSObject class];
234 Class icl = [InheritingSub class];
235 Class ocl = [OverridingSub class];
237 Class oa1 = [OverridingASub class];
238 Class oa2 = [OverridingAWZSub class];
239 Class oa3 = [OverridingAAWZSub class];
241 NSObject *obj = [NSObject new];
242 InheritingSub *inh = [InheritingSub new];
243 OverridingSub *ovr = [OverridingSub new];
254 // vtable dispatch can introduce bypass just like the ARC entrypoints
256 testprintf("method dispatch does not bypass\n");
260 testassert(Retains == 1);
262 testassert(Releases == 1);
264 testassert(Autoreleases == 1);
267 testassert(PlusRetains == 1);
269 testassert(PlusReleases == 1);
271 testassert(PlusAutoreleases == 1);
274 testassert(Retains == 2);
276 testassert(Releases == 2);
278 testassert(Autoreleases == 2);
281 testassert(PlusRetains == 2);
283 testassert(PlusReleases == 2);
285 testassert(PlusAutoreleases == 2);
288 testassert(SubRetains == 1);
290 testassert(SubReleases == 1);
292 testassert(SubAutoreleases == 1);
295 testassert(SubPlusRetains == 1);
297 testassert(SubPlusReleases == 1);
299 testassert(SubPlusAutoreleases == 1);
301 [UnrealizedSubA1 retain];
302 testassert(PlusRetains == 3);
303 [UnrealizedSubA2 release];
304 testassert(PlusReleases == 3);
305 [UnrealizedSubA3 autorelease];
306 testassert(PlusAutoreleases == 3);
310 testprintf("objc_msgSend() does not bypass\n");
313 id (*retain_fn)(id, SEL) = (id(*)(id, SEL))objc_msgSend;
314 void (*release_fn)(id, SEL) = (void(*)(id, SEL))objc_msgSend;
315 id (*autorelease_fn)(id, SEL) = (id(*)(id, SEL))objc_msgSend;
317 retain_fn(obj, @selector(retain));
318 testassert(Retains == 1);
319 release_fn(obj, @selector(release));
320 testassert(Releases == 1);
321 autorelease_fn(obj, @selector(autorelease));
322 testassert(Autoreleases == 1);
324 retain_fn(cls, @selector(retain));
325 testassert(PlusRetains == 1);
326 release_fn(cls, @selector(release));
327 testassert(PlusReleases == 1);
328 autorelease_fn(cls, @selector(autorelease));
329 testassert(PlusAutoreleases == 1);
331 retain_fn(inh, @selector(retain));
332 testassert(Retains == 2);
333 release_fn(inh, @selector(release));
334 testassert(Releases == 2);
335 autorelease_fn(inh, @selector(autorelease));
336 testassert(Autoreleases == 2);
338 retain_fn(icl, @selector(retain));
339 testassert(PlusRetains == 2);
340 release_fn(icl, @selector(release));
341 testassert(PlusReleases == 2);
342 autorelease_fn(icl, @selector(autorelease));
343 testassert(PlusAutoreleases == 2);
345 retain_fn(ovr, @selector(retain));
346 testassert(SubRetains == 1);
347 release_fn(ovr, @selector(release));
348 testassert(SubReleases == 1);
349 autorelease_fn(ovr, @selector(autorelease));
350 testassert(SubAutoreleases == 1);
352 retain_fn(ocl, @selector(retain));
353 testassert(SubPlusRetains == 1);
354 release_fn(ocl, @selector(release));
355 testassert(SubPlusReleases == 1);
356 autorelease_fn(ocl, @selector(autorelease));
357 testassert(SubPlusAutoreleases == 1);
359 retain_fn((Class)&OBJC_CLASS_$_UnrealizedSubB1, @selector(retain));
360 testassert(PlusRetains == 3);
361 release_fn((Class)&OBJC_CLASS_$_UnrealizedSubB2, @selector(release));
362 testassert(PlusReleases == 3);
363 autorelease_fn((Class)&OBJC_CLASS_$_UnrealizedSubB3, @selector(autorelease));
364 testassert(PlusAutoreleases == 3);
367 testprintf("arc function bypasses instance but not class or override\n");
371 testassert(Retains == 0);
373 testassert(Releases == 0);
374 objc_autorelease(obj);
375 testassert(Autoreleases == 0);
377 #if SUPPORT_NONPOINTER_ISA
379 testassert(PlusRetains == 0);
381 testassert(PlusReleases == 0);
382 objc_autorelease(cls);
383 testassert(PlusAutoreleases == 0);
386 testassert(PlusRetains == 1);
388 testassert(PlusReleases == 1);
389 objc_autorelease(cls);
390 testassert(PlusAutoreleases == 1);
394 testassert(Retains == 0);
396 testassert(Releases == 0);
397 objc_autorelease(inh);
398 testassert(Autoreleases == 0);
400 #if SUPPORT_NONPOINTER_ISA
402 testassert(PlusRetains == 0);
404 testassert(PlusReleases == 0);
405 objc_autorelease(icl);
406 testassert(PlusAutoreleases == 0);
409 testassert(PlusRetains == 2);
411 testassert(PlusReleases == 2);
412 objc_autorelease(icl);
413 testassert(PlusAutoreleases == 2);
417 testassert(SubRetains == 1);
419 testassert(SubReleases == 1);
420 objc_autorelease(ovr);
421 testassert(SubAutoreleases == 1);
424 testassert(SubPlusRetains == 1);
426 testassert(SubPlusReleases == 1);
427 objc_autorelease(ocl);
428 testassert(SubPlusAutoreleases == 1);
430 #if SUPPORT_NONPOINTER_ISA
431 objc_retain((Class)&OBJC_CLASS_$_UnrealizedSubC1);
432 testassert(PlusRetains == 1);
433 objc_release((Class)&OBJC_CLASS_$_UnrealizedSubC2);
434 testassert(PlusReleases == 1);
435 objc_autorelease((Class)&OBJC_CLASS_$_UnrealizedSubC3);
436 testassert(PlusAutoreleases == 1);
438 objc_retain((Class)&OBJC_CLASS_$_UnrealizedSubC1);
439 testassert(PlusRetains == 3);
440 objc_release((Class)&OBJC_CLASS_$_UnrealizedSubC2);
441 testassert(PlusReleases == 3);
442 objc_autorelease((Class)&OBJC_CLASS_$_UnrealizedSubC3);
443 testassert(PlusAutoreleases == 3);
446 testprintf("unrelated addMethod does not clobber\n");
449 class_addMethod(cls, @selector(unrelatedMethod), (IMP)imp_fn, "");
452 testassert(Retains == 0);
454 testassert(Releases == 0);
455 objc_autorelease(obj);
456 testassert(Autoreleases == 0);
459 testprintf("add class method does not clobber\n");
463 testassert(Retains == 0);
465 testassert(Releases == 0);
466 objc_autorelease(obj);
467 testassert(Autoreleases == 0);
469 class_addMethod(object_getClass(cls), @selector(retain), (IMP)imp_fn, "");
472 testassert(Retains == 0);
474 testassert(Releases == 0);
475 objc_autorelease(obj);
476 testassert(Autoreleases == 0);
479 testprintf("addMethod clobbers (InheritingSub2, retain)\n");
482 ccc = [InheritingSub2 class];
484 cc2 = [InheritingSub2_2 class];
488 testassert(Retains == 0);
490 testassert(Releases == 0);
491 objc_autorelease(ooo);
492 testassert(Autoreleases == 0);
495 testassert(Retains == 0);
497 testassert(Releases == 0);
498 objc_autorelease(oo2);
499 testassert(Autoreleases == 0);
501 class_addMethod(ccc, @selector(retain), (IMP)imp_fn, "");
504 testassert(Retains == 0);
505 testassert(Imps == 1);
507 testassert(Releases == 1);
508 objc_autorelease(ooo);
509 testassert(Autoreleases == 1);
512 testassert(Retains == 0);
513 testassert(Imps == 2);
515 testassert(Releases == 2);
516 objc_autorelease(oo2);
517 testassert(Autoreleases == 2);
520 testprintf("addMethod clobbers (InheritingSub3, release)\n");
523 ccc = [InheritingSub3 class];
525 cc2 = [InheritingSub3_2 class];
529 testassert(Retains == 0);
531 testassert(Releases == 0);
532 objc_autorelease(ooo);
533 testassert(Autoreleases == 0);
536 testassert(Retains == 0);
538 testassert(Releases == 0);
539 objc_autorelease(oo2);
540 testassert(Autoreleases == 0);
542 class_addMethod(ccc, @selector(release), (IMP)imp_fn, "");
545 testassert(Retains == 1);
547 testassert(Releases == 0);
548 testassert(Imps == 1);
549 objc_autorelease(ooo);
550 testassert(Autoreleases == 1);
553 testassert(Retains == 2);
555 testassert(Releases == 0);
556 testassert(Imps == 2);
557 objc_autorelease(oo2);
558 testassert(Autoreleases == 2);
561 testprintf("addMethod clobbers (InheritingSub4, autorelease)\n");
564 ccc = [InheritingSub4 class];
566 cc2 = [InheritingSub4_2 class];
570 testassert(Retains == 0);
572 testassert(Releases == 0);
573 objc_autorelease(ooo);
574 testassert(Autoreleases == 0);
577 testassert(Retains == 0);
579 testassert(Releases == 0);
580 objc_autorelease(oo2);
581 testassert(Autoreleases == 0);
583 class_addMethod(ccc, @selector(autorelease), (IMP)imp_fn, "");
586 testassert(Retains == 1);
588 testassert(Releases == 1);
589 objc_autorelease(ooo);
590 testassert(Autoreleases == 0);
591 testassert(Imps == 1);
594 testassert(Retains == 2);
596 testassert(Releases == 2);
597 objc_autorelease(oo2);
598 testassert(Autoreleases == 0);
599 testassert(Imps == 2);
602 testprintf("addMethod clobbers (InheritingSub5, retainCount)\n");
605 ccc = [InheritingSub5 class];
607 cc2 = [InheritingSub5_2 class];
611 testassert(Retains == 0);
613 testassert(Releases == 0);
614 objc_autorelease(ooo);
615 testassert(Autoreleases == 0);
618 testassert(Retains == 0);
620 testassert(Releases == 0);
621 objc_autorelease(oo2);
622 testassert(Autoreleases == 0);
624 class_addMethod(ccc, @selector(retainCount), (IMP)imp_fn, "");
627 testassert(Retains == 1);
629 testassert(Releases == 1);
630 objc_autorelease(ooo);
631 testassert(Autoreleases == 1);
632 // no bypassing call for -retainCount
635 testassert(Retains == 2);
637 testassert(Releases == 2);
638 objc_autorelease(oo2);
639 testassert(Autoreleases == 2);
640 // no bypassing call for -retainCount
643 testprintf("setSuperclass to clean super does not clobber (InheritingSub6)\n");
646 ccc = [InheritingSub6 class];
648 cc2 = [InheritingSub6_2 class];
652 testassert(Retains == 0);
654 testassert(Releases == 0);
655 objc_autorelease(ooo);
656 testassert(Autoreleases == 0);
659 testassert(Retains == 0);
661 testassert(Releases == 0);
662 objc_autorelease(oo2);
663 testassert(Autoreleases == 0);
665 class_setSuperclass(ccc, [InheritingSub class]);
668 testassert(Retains == 0);
670 testassert(Releases == 0);
671 objc_autorelease(ooo);
672 testassert(Autoreleases == 0);
675 testassert(Retains == 0);
677 testassert(Releases == 0);
678 objc_autorelease(oo2);
679 testassert(Autoreleases == 0);
682 testprintf("setSuperclass to dirty super clobbers (InheritingSub7)\n");
685 ccc = [InheritingSub7 class];
687 cc2 = [InheritingSub7_2 class];
691 testassert(Retains == 0);
693 testassert(Releases == 0);
694 objc_autorelease(ooo);
695 testassert(Autoreleases == 0);
698 testassert(Retains == 0);
700 testassert(Releases == 0);
701 objc_autorelease(oo2);
702 testassert(Autoreleases == 0);
704 class_setSuperclass(ccc, [OverridingSub class]);
707 testassert(SubRetains == 1);
709 testassert(SubReleases == 1);
710 objc_autorelease(ooo);
711 testassert(SubAutoreleases == 1);
714 testassert(SubRetains == 2);
716 testassert(SubReleases == 2);
717 objc_autorelease(oo2);
718 testassert(SubAutoreleases == 2);
721 testprintf("category replacement of unrelated method does not clobber (InheritingSubCat)\n");
724 ccc = [InheritingSubCat class];
726 cc2 = [InheritingSubCat_2 class];
730 testassert(Retains == 0);
732 testassert(Releases == 0);
733 objc_autorelease(ooo);
734 testassert(Autoreleases == 0);
737 testassert(Retains == 0);
739 testassert(Releases == 0);
740 objc_autorelease(oo2);
741 testassert(Autoreleases == 0);
743 dlh = dlopen("customrr-cat1.bundle", RTLD_LAZY);
747 testassert(Retains == 0);
749 testassert(Releases == 0);
750 objc_autorelease(ooo);
751 testassert(Autoreleases == 0);
754 testassert(Retains == 0);
756 testassert(Releases == 0);
757 objc_autorelease(oo2);
758 testassert(Autoreleases == 0);
761 testprintf("category replacement clobbers (InheritingSubCat)\n");
764 ccc = [InheritingSubCat class];
766 cc2 = [InheritingSubCat_2 class];
770 testassert(Retains == 0);
772 testassert(Releases == 0);
773 objc_autorelease(ooo);
774 testassert(Autoreleases == 0);
777 testassert(Retains == 0);
779 testassert(Releases == 0);
780 objc_autorelease(oo2);
781 testassert(Autoreleases == 0);
783 dlh = dlopen("customrr-cat2.bundle", RTLD_LAZY);
787 testassert(Retains == 1);
789 testassert(Releases == 1);
790 objc_autorelease(ooo);
791 testassert(Autoreleases == 1);
794 testassert(Retains == 2);
796 testassert(Releases == 2);
797 objc_autorelease(oo2);
798 testassert(Autoreleases == 2);
801 testprintf("allocateClassPair with clean super does not clobber\n");
805 testassert(Retains == 0);
807 testassert(Releases == 0);
808 objc_autorelease(inh);
809 testassert(Autoreleases == 0);
811 ccc = objc_allocateClassPair([InheritingSub class], "CleanClassPair", 0);
812 objc_registerClassPair(ccc);
816 testassert(Retains == 0);
818 testassert(Releases == 0);
819 objc_autorelease(inh);
820 testassert(Autoreleases == 0);
823 testassert(Retains == 0);
825 testassert(Releases == 0);
826 objc_autorelease(ooo);
827 testassert(Autoreleases == 0);
830 testprintf("allocateClassPair with clobbered super clobbers\n");
833 ccc = objc_allocateClassPair([OverridingSub class], "DirtyClassPair", 0);
834 objc_registerClassPair(ccc);
838 testassert(SubRetains == 1);
840 testassert(SubReleases == 1);
841 objc_autorelease(ooo);
842 testassert(SubAutoreleases == 1);
845 testprintf("allocateClassPair with clean super and override clobbers\n");
848 ccc = objc_allocateClassPair([InheritingSub class], "Dirty2ClassPair", 0);
849 class_addMethod(ccc, @selector(autorelease), (IMP)imp_fn, "");
850 objc_registerClassPair(ccc);
854 testassert(Retains == 1);
856 testassert(Releases == 1);
857 objc_autorelease(ooo);
858 testassert(Autoreleases == 0);
859 testassert(Imps == 1);
862 // method_setImplementation and method_exchangeImplementations only
863 // clobber when manipulating NSObject. We can only test one at a time.
864 // To test both, we need two tests: customrr and customrr2.
866 // These tests also check recursive clobber.
868 #if TEST_EXCHANGEIMPLEMENTATIONS
869 testprintf("exchangeImplementations clobbers (recursive)\n");
871 testprintf("setImplementation clobbers (recursive)\n");
876 testassert(Retains == 0);
878 testassert(Releases == 0);
879 objc_autorelease(obj);
880 testassert(Autoreleases == 0);
883 testassert(Retains == 0);
885 testassert(Releases == 0);
886 objc_autorelease(inh);
887 testassert(Autoreleases == 0);
889 Method meth = class_getInstanceMethod(cls, @selector(retainCount));
891 #if TEST_EXCHANGEIMPLEMENTATIONS
892 method_exchangeImplementations(meth, meth);
894 method_setImplementation(meth, (IMP)imp_fn);
898 testassert(Retains == 1);
900 testassert(Releases == 1);
901 objc_autorelease(obj);
902 testassert(Autoreleases == 1);
905 testassert(Retains == 2);
907 testassert(Releases == 2);
908 objc_autorelease(inh);
909 testassert(Autoreleases == 2);
912 // do not add more tests here - the recursive test must be LAST
914 succeed(basename(argv[0]));