1// These options must match customrr2.m
2// TEST_CONFIG MEM=mrc
3/*
4TEST_BUILD
5    $C{COMPILE} $DIR/customrr.m -fvisibility=default -o customrr.out 
6    $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/customrr-cat1.m -o customrr-cat1.dylib
7    $C{COMPILE} -undefined dynamic_lookup -dynamiclib $DIR/customrr-cat2.m -o customrr-cat2.dylib
8END
9*/
10
11
12#include "test.h"
13#include <dlfcn.h>
14#include <Foundation/NSObject.h>
15
16#if !__OBJC2__
17
18// pacify exported symbols list
19OBJC_ROOT_CLASS
20@interface InheritingSubCat @end
21@implementation InheritingSubCat @end
22
23int main(int argc __unused, char **argv)
24{
25    succeed(basename(argv[0]));
26}
27
28#else
29
30static int Retains;
31static int Releases;
32static int Autoreleases;
33static int RetainCounts;
34static int PlusRetains;
35static int PlusReleases;
36static int PlusAutoreleases;
37static int PlusRetainCounts;
38static int Allocs;
39static int AllocWithZones;
40
41static int SubRetains;
42static int SubReleases;
43static int SubAutoreleases;
44static int SubRetainCounts;
45static int SubPlusRetains;
46static int SubPlusReleases;
47static int SubPlusAutoreleases;
48static int SubPlusRetainCounts;
49static int SubAllocs;
50static int SubAllocWithZones;
51
52static int Imps;
53
54static id imp_fn(id self, SEL _cmd __unused, ...)
55{
56    Imps++;
57    return self;
58}
59
60static void zero(void) {
61    Retains = 0;
62    Releases = 0;
63    Autoreleases = 0;
64    RetainCounts = 0;
65    PlusRetains = 0;
66    PlusReleases = 0;
67    PlusAutoreleases = 0;
68    PlusRetainCounts = 0;
69    Allocs = 0;
70    AllocWithZones = 0;
71
72    SubRetains = 0;
73    SubReleases = 0;
74    SubAutoreleases = 0;
75    SubRetainCounts = 0;
76    SubPlusRetains = 0;
77    SubPlusReleases = 0;
78    SubPlusAutoreleases = 0;
79    SubPlusRetainCounts = 0;
80    SubAllocs = 0;
81    SubAllocWithZones = 0;
82
83    Imps = 0;
84}
85
86
87id HackRetain(id self, SEL _cmd __unused) { Retains++; return self; }
88void HackRelease(id self __unused, SEL _cmd __unused) { Releases++; }
89id HackAutorelease(id self, SEL _cmd __unused) { Autoreleases++; return self; }
90NSUInteger HackRetainCount(id self __unused, SEL _cmd __unused) { RetainCounts++; return 1; }
91id HackPlusRetain(id self, SEL _cmd __unused) { PlusRetains++; return self; }
92void HackPlusRelease(id self __unused, SEL _cmd __unused) { PlusReleases++; }
93id HackPlusAutorelease(id self, SEL _cmd __unused) { PlusAutoreleases++; return self; }
94NSUInteger HackPlusRetainCount(id self __unused, SEL _cmd __unused) { PlusRetainCounts++; return 1; }
95id HackAlloc(Class self, SEL _cmd __unused) { Allocs++; return class_createInstance(self, 0); }
96id HackAllocWithZone(Class self, SEL _cmd __unused) { AllocWithZones++; return class_createInstance(self, 0); }
97
98
99@interface OverridingSub : NSObject @end
100@implementation OverridingSub 
101
102-(id) retain { SubRetains++; return self; }
103+(id) retain { SubPlusRetains++; return self; }
104-(oneway void) release { SubReleases++; }
105+(oneway void) release { SubPlusReleases++; }
106-(id) autorelease { SubAutoreleases++; return self; }
107+(id) autorelease { SubPlusAutoreleases++; return self; }
108-(NSUInteger) retainCount { SubRetainCounts++; return 1; }
109+(NSUInteger) retainCount { SubPlusRetainCounts++; return 1; }
110
111@end
112
113@interface OverridingASub : NSObject @end
114@implementation OverridingASub
115+(id) alloc { SubAllocs++; return class_createInstance(self, 0); }
116@end
117
118@interface OverridingAWZSub : NSObject @end
119@implementation OverridingAWZSub
120+(id) allocWithZone:(NSZone * __unused)z { SubAllocWithZones++; return class_createInstance(self, 0); }
121@end
122
123@interface OverridingAAWZSub : NSObject @end
124@implementation OverridingAAWZSub
125+(id) alloc { SubAllocs++; return class_createInstance(self, 0); }
126+(id) allocWithZone:(NSZone * __unused)z { SubAllocWithZones++; return class_createInstance(self, 0); }
127@end
128
129
130@interface InheritingSub : NSObject @end
131@implementation InheritingSub @end
132
133@interface InheritingSub2 : NSObject @end
134@implementation InheritingSub2 @end
135@interface InheritingSub2_2 : InheritingSub2 @end
136@implementation InheritingSub2_2 @end
137
138@interface InheritingSub3 : NSObject @end
139@implementation InheritingSub3 @end
140@interface InheritingSub3_2 : InheritingSub3 @end
141@implementation InheritingSub3_2 @end
142
143@interface InheritingSub4 : NSObject @end
144@implementation InheritingSub4 @end
145@interface InheritingSub4_2 : InheritingSub4 @end
146@implementation InheritingSub4_2 @end
147
148@interface InheritingSub5 : NSObject @end
149@implementation InheritingSub5 @end
150@interface InheritingSub5_2 : InheritingSub5 @end
151@implementation InheritingSub5_2 @end
152
153@interface InheritingSub6 : NSObject @end
154@implementation InheritingSub6 @end
155@interface InheritingSub6_2 : InheritingSub6 @end
156@implementation InheritingSub6_2 @end
157
158@interface InheritingSub7 : NSObject @end
159@implementation InheritingSub7 @end
160@interface InheritingSub7_2 : InheritingSub7 @end
161@implementation InheritingSub7_2 @end
162
163@interface InheritingSubCat : NSObject @end
164@implementation InheritingSubCat @end
165@interface InheritingSubCat_2 : InheritingSubCat @end
166@implementation InheritingSubCat_2 @end
167
168
169extern uintptr_t OBJC_CLASS_$_UnrealizedSubA1;
170@interface UnrealizedSubA1 : NSObject @end
171@implementation UnrealizedSubA1 @end
172extern uintptr_t OBJC_CLASS_$_UnrealizedSubA2;
173@interface UnrealizedSubA2 : NSObject @end
174@implementation UnrealizedSubA2 @end
175extern uintptr_t OBJC_CLASS_$_UnrealizedSubA3;
176@interface UnrealizedSubA3 : NSObject @end
177@implementation UnrealizedSubA3 @end
178
179extern uintptr_t OBJC_CLASS_$_UnrealizedSubB1;
180@interface UnrealizedSubB1 : NSObject @end
181@implementation UnrealizedSubB1 @end
182extern uintptr_t OBJC_CLASS_$_UnrealizedSubB2;
183@interface UnrealizedSubB2 : NSObject @end
184@implementation UnrealizedSubB2 @end
185extern uintptr_t OBJC_CLASS_$_UnrealizedSubB3;
186@interface UnrealizedSubB3 : NSObject @end
187@implementation UnrealizedSubB3 @end
188
189extern uintptr_t OBJC_CLASS_$_UnrealizedSubC1;
190@interface UnrealizedSubC1 : NSObject @end
191@implementation UnrealizedSubC1 @end
192extern uintptr_t OBJC_CLASS_$_UnrealizedSubC2;
193@interface UnrealizedSubC2 : NSObject @end
194@implementation UnrealizedSubC2 @end
195extern uintptr_t OBJC_CLASS_$_UnrealizedSubC3;
196@interface UnrealizedSubC3 : NSObject @end
197@implementation UnrealizedSubC3 @end
198
199
200int main(int argc __unused, char **argv)
201{
202    objc_autoreleasePoolPush();
203
204    // Hack NSObject's RR methods.
205    // Don't use runtime functions to do this - 
206    // we want the runtime to think that these are NSObject's real code
207    {
208        Class cls = [NSObject class];
209        IMP *m;
210        IMP imp;
211        imp = class_getMethodImplementation(cls, @selector(retain));
212        m = (IMP *)class_getInstanceMethod(cls, @selector(retain));
213        testassert(m[2] == imp);  // verify Method struct is as we expect
214
215        m = (IMP *)class_getInstanceMethod(cls, @selector(retain));
216        m[2] = (IMP)HackRetain;
217        m = (IMP *)class_getInstanceMethod(cls, @selector(release));
218        m[2] = (IMP)HackRelease;
219        m = (IMP *)class_getInstanceMethod(cls, @selector(autorelease));
220        m[2] = (IMP)HackAutorelease;
221        m = (IMP *)class_getInstanceMethod(cls, @selector(retainCount));
222        m[2] = (IMP)HackRetainCount;
223        m = (IMP *)class_getClassMethod(cls, @selector(retain));
224        m[2] = (IMP)HackPlusRetain;
225        m = (IMP *)class_getClassMethod(cls, @selector(release));
226        m[2] = (IMP)HackPlusRelease;
227        m = (IMP *)class_getClassMethod(cls, @selector(autorelease));
228        m[2] = (IMP)HackPlusAutorelease;
229        m = (IMP *)class_getClassMethod(cls, @selector(retainCount));
230        m[2] = (IMP)HackPlusRetainCount;
231        m = (IMP *)class_getClassMethod(cls, @selector(alloc));
232        m[2] = (IMP)HackAlloc;
233        m = (IMP *)class_getClassMethod(cls, @selector(allocWithZone:));
234        m[2] = (IMP)HackAllocWithZone;
235
236        _objc_flush_caches(cls);
237
238        imp = class_getMethodImplementation(cls, @selector(retain));
239        testassert(imp == (IMP)HackRetain);  // verify hack worked
240    }
241
242    Class cls = [NSObject class];
243    Class icl = [InheritingSub class];
244    Class ocl = [OverridingSub class];
245    /*
246    Class oa1 = [OverridingASub class];
247    Class oa2 = [OverridingAWZSub class];
248    Class oa3 = [OverridingAAWZSub class];
249    */
250    NSObject *obj = [NSObject new];
251    InheritingSub *inh = [InheritingSub new];
252    OverridingSub *ovr = [OverridingSub new];
253
254    Class ccc;
255    id ooo;
256    Class cc2;
257    id oo2;
258
259    void *dlh;
260
261
262    testprintf("method dispatch does not bypass\n");
263    zero();
264    
265    [obj retain];
266    testassert(Retains == 1);
267    [obj release];
268    testassert(Releases == 1);
269    [obj autorelease];
270    testassert(Autoreleases == 1);
271
272    [cls retain];
273    testassert(PlusRetains == 1);
274    [cls release];
275    testassert(PlusReleases == 1);
276    [cls autorelease];
277    testassert(PlusAutoreleases == 1);
278
279    [inh retain];
280    testassert(Retains == 2);
281    [inh release];
282    testassert(Releases == 2);
283    [inh autorelease];
284    testassert(Autoreleases == 2);
285
286    [icl retain];
287    testassert(PlusRetains == 2);
288    [icl release];
289    testassert(PlusReleases == 2);
290    [icl autorelease];
291    testassert(PlusAutoreleases == 2);
292    
293    [ovr retain];
294    testassert(SubRetains == 1);
295    [ovr release];
296    testassert(SubReleases == 1);
297    [ovr autorelease];
298    testassert(SubAutoreleases == 1);
299
300    [ocl retain];
301    testassert(SubPlusRetains == 1);
302    [ocl release];
303    testassert(SubPlusReleases == 1);
304    [ocl autorelease];
305    testassert(SubPlusAutoreleases == 1);
306
307    [UnrealizedSubA1 retain];
308    testassert(PlusRetains == 3);
309    [UnrealizedSubA2 release];
310    testassert(PlusReleases == 3);
311    [UnrealizedSubA3 autorelease];
312    testassert(PlusAutoreleases == 3);
313
314
315    testprintf("objc_msgSend() does not bypass\n");
316    zero();
317
318    id (*retain_fn)(id, SEL) = (id(*)(id, SEL))objc_msgSend;
319    void (*release_fn)(id, SEL) = (void(*)(id, SEL))objc_msgSend;
320    id (*autorelease_fn)(id, SEL) = (id(*)(id, SEL))objc_msgSend;
321
322    retain_fn(obj, @selector(retain));
323    testassert(Retains == 1);
324    release_fn(obj, @selector(release));
325    testassert(Releases == 1);
326    autorelease_fn(obj, @selector(autorelease));
327    testassert(Autoreleases == 1);
328
329    retain_fn(cls, @selector(retain));
330    testassert(PlusRetains == 1);
331    release_fn(cls, @selector(release));
332    testassert(PlusReleases == 1);
333    autorelease_fn(cls, @selector(autorelease));
334    testassert(PlusAutoreleases == 1);
335
336    retain_fn(inh, @selector(retain));
337    testassert(Retains == 2);
338    release_fn(inh, @selector(release));
339    testassert(Releases == 2);
340    autorelease_fn(inh, @selector(autorelease));
341    testassert(Autoreleases == 2);
342
343    retain_fn(icl, @selector(retain));
344    testassert(PlusRetains == 2);
345    release_fn(icl, @selector(release));
346    testassert(PlusReleases == 2);
347    autorelease_fn(icl, @selector(autorelease));
348    testassert(PlusAutoreleases == 2);
349    
350    retain_fn(ovr, @selector(retain));
351    testassert(SubRetains == 1);
352    release_fn(ovr, @selector(release));
353    testassert(SubReleases == 1);
354    autorelease_fn(ovr, @selector(autorelease));
355    testassert(SubAutoreleases == 1);
356
357    retain_fn(ocl, @selector(retain));
358    testassert(SubPlusRetains == 1);
359    release_fn(ocl, @selector(release));
360    testassert(SubPlusReleases == 1);
361    autorelease_fn(ocl, @selector(autorelease));
362    testassert(SubPlusAutoreleases == 1);
363
364#if __OBJC2__
365    retain_fn((Class)&OBJC_CLASS_$_UnrealizedSubB1, @selector(retain));
366    testassert(PlusRetains == 3);
367    release_fn((Class)&OBJC_CLASS_$_UnrealizedSubB2, @selector(release));
368    testassert(PlusReleases == 3);
369    autorelease_fn((Class)&OBJC_CLASS_$_UnrealizedSubB3, @selector(autorelease));
370    testassert(PlusAutoreleases == 3);
371#endif
372
373
374    testprintf("arc function bypasses instance but not class or override\n");
375    zero();
376    
377    objc_retain(obj);
378    testassert(Retains == 0);
379    objc_release(obj);
380    testassert(Releases == 0);
381    objc_autorelease(obj);
382    testassert(Autoreleases == 0);
383
384    objc_retain(cls);
385    testassert(PlusRetains == 1);
386    objc_release(cls);
387    testassert(PlusReleases == 1);
388    objc_autorelease(cls);
389    testassert(PlusAutoreleases == 1);
390
391    objc_retain(inh);
392    testassert(Retains == 0);
393    objc_release(inh);
394    testassert(Releases == 0);
395    objc_autorelease(inh);
396    testassert(Autoreleases == 0);
397
398    objc_retain(icl);
399    testassert(PlusRetains == 2);
400    objc_release(icl);
401    testassert(PlusReleases == 2);
402    objc_autorelease(icl);
403    testassert(PlusAutoreleases == 2);
404    
405    objc_retain(ovr);
406    testassert(SubRetains == 1);
407    objc_release(ovr);
408    testassert(SubReleases == 1);
409    objc_autorelease(ovr);
410    testassert(SubAutoreleases == 1);
411
412    objc_retain(ocl);
413    testassert(SubPlusRetains == 1);
414    objc_release(ocl);
415    testassert(SubPlusReleases == 1);
416    objc_autorelease(ocl);
417    testassert(SubPlusAutoreleases == 1);
418
419#if __OBJC2__
420#if 1
421    testwarn("rdar://12961688 CustomRR is wrong for unrealized classes");
422#else
423    objc_retain((Class)&OBJC_CLASS_$_UnrealizedSubC1);
424    testassert(PlusRetains == 3);
425    objc_release((Class)&OBJC_CLASS_$_UnrealizedSubC2);
426    testassert(PlusReleases == 3);
427    objc_autorelease((Class)&OBJC_CLASS_$_UnrealizedSubC3);
428    testassert(PlusAutoreleases == 3);
429#endif
430#endif
431
432
433    testprintf("unrelated addMethod does not clobber\n");
434    zero();
435
436    class_addMethod(cls, @selector(unrelatedMethod), (IMP)imp_fn, "");
437    
438    objc_retain(obj);
439    testassert(Retains == 0);
440    objc_release(obj);
441    testassert(Releases == 0);
442    objc_autorelease(obj);
443    testassert(Autoreleases == 0);
444
445
446    testprintf("add class method does not clobber\n");
447    zero();
448    
449    objc_retain(obj);
450    testassert(Retains == 0);
451    objc_release(obj);
452    testassert(Releases == 0);
453    objc_autorelease(obj);
454    testassert(Autoreleases == 0);
455
456    class_addMethod(cls->isa, @selector(retain), (IMP)imp_fn, "");
457    
458    objc_retain(obj);
459    testassert(Retains == 0);
460    objc_release(obj);
461    testassert(Releases == 0);
462    objc_autorelease(obj);
463    testassert(Autoreleases == 0);
464
465
466    testprintf("addMethod clobbers (InheritingSub2, retain)\n");
467    zero();
468
469    ccc = [InheritingSub2 class];
470    ooo = [ccc new];
471    cc2 = [InheritingSub2_2 class];
472    oo2 = [cc2 new];
473    
474    objc_retain(ooo);
475    testassert(Retains == 0);
476    objc_release(ooo);
477    testassert(Releases == 0);
478    objc_autorelease(ooo);
479    testassert(Autoreleases == 0);
480
481    objc_retain(oo2);
482    testassert(Retains == 0);
483    objc_release(oo2);
484    testassert(Releases == 0);
485    objc_autorelease(oo2);
486    testassert(Autoreleases == 0);
487
488    class_addMethod(ccc, @selector(retain), (IMP)imp_fn, "");
489
490    objc_retain(ooo);
491    testassert(Retains == 0);
492    testassert(Imps == 1);
493    objc_release(ooo);
494    testassert(Releases == 1);
495    objc_autorelease(ooo);
496    testassert(Autoreleases == 1);
497
498    objc_retain(oo2);
499    testassert(Retains == 0);
500    testassert(Imps == 2);
501    objc_release(oo2);
502    testassert(Releases == 2);
503    objc_autorelease(oo2);
504    testassert(Autoreleases == 2);
505
506
507    testprintf("addMethod clobbers (InheritingSub3, release)\n");
508    zero();
509
510    ccc = [InheritingSub3 class];
511    ooo = [ccc new];
512    cc2 = [InheritingSub3_2 class];
513    oo2 = [cc2 new];
514    
515    objc_retain(ooo);
516    testassert(Retains == 0);
517    objc_release(ooo);
518    testassert(Releases == 0);
519    objc_autorelease(ooo);
520    testassert(Autoreleases == 0);
521    
522    objc_retain(oo2);
523    testassert(Retains == 0);
524    objc_release(oo2);
525    testassert(Releases == 0);
526    objc_autorelease(oo2);
527    testassert(Autoreleases == 0);
528
529    class_addMethod(ccc, @selector(release), (IMP)imp_fn, "");
530
531    objc_retain(ooo);
532    testassert(Retains == 1);
533    objc_release(ooo);
534    testassert(Releases == 0);
535    testassert(Imps == 1);
536    objc_autorelease(ooo);
537    testassert(Autoreleases == 1);
538
539    objc_retain(oo2);
540    testassert(Retains == 2);
541    objc_release(oo2);
542    testassert(Releases == 0);
543    testassert(Imps == 2);
544    objc_autorelease(oo2);
545    testassert(Autoreleases == 2);
546
547
548    testprintf("addMethod clobbers (InheritingSub4, autorelease)\n");
549    zero();
550
551    ccc = [InheritingSub4 class];
552    ooo = [ccc new];
553    cc2 = [InheritingSub4_2 class];
554    oo2 = [cc2 new];
555    
556    objc_retain(ooo);
557    testassert(Retains == 0);
558    objc_release(ooo);
559    testassert(Releases == 0);
560    objc_autorelease(ooo);
561    testassert(Autoreleases == 0);
562    
563    objc_retain(oo2);
564    testassert(Retains == 0);
565    objc_release(oo2);
566    testassert(Releases == 0);
567    objc_autorelease(oo2);
568    testassert(Autoreleases == 0);
569
570    class_addMethod(ccc, @selector(autorelease), (IMP)imp_fn, "");
571
572    objc_retain(ooo);
573    testassert(Retains == 1);
574    objc_release(ooo);
575    testassert(Releases == 1);
576    objc_autorelease(ooo);
577    testassert(Autoreleases == 0);
578    testassert(Imps == 1);
579
580    objc_retain(oo2);
581    testassert(Retains == 2);
582    objc_release(oo2);
583    testassert(Releases == 2);
584    objc_autorelease(oo2);
585    testassert(Autoreleases == 0);
586    testassert(Imps == 2);
587
588
589    testprintf("addMethod clobbers (InheritingSub5, retainCount)\n");
590    zero();
591
592    ccc = [InheritingSub5 class];
593    ooo = [ccc new];
594    cc2 = [InheritingSub5_2 class];
595    oo2 = [cc2 new];
596    
597    objc_retain(ooo);
598    testassert(Retains == 0);
599    objc_release(ooo);
600    testassert(Releases == 0);
601    objc_autorelease(ooo);
602    testassert(Autoreleases == 0);
603
604    objc_retain(oo2);
605    testassert(Retains == 0);
606    objc_release(oo2);
607    testassert(Releases == 0);
608    objc_autorelease(oo2);
609    testassert(Autoreleases == 0);
610
611    class_addMethod(ccc, @selector(retainCount), (IMP)imp_fn, "");
612
613    objc_retain(ooo);
614    testassert(Retains == 1);
615    objc_release(ooo);
616    testassert(Releases == 1);
617    objc_autorelease(ooo);
618    testassert(Autoreleases == 1);
619    // no bypassing call for -retainCount
620
621    objc_retain(oo2);
622    testassert(Retains == 2);
623    objc_release(oo2);
624    testassert(Releases == 2);
625    objc_autorelease(oo2);
626    testassert(Autoreleases == 2);
627    // no bypassing call for -retainCount
628
629
630    testprintf("setSuperclass to clean super does not clobber (InheritingSub6)\n");
631    zero();
632
633    ccc = [InheritingSub6 class];
634    ooo = [ccc new];
635    cc2 = [InheritingSub6_2 class];
636    oo2 = [cc2 new];
637    
638    objc_retain(ooo);
639    testassert(Retains == 0);
640    objc_release(ooo);
641    testassert(Releases == 0);
642    objc_autorelease(ooo);
643    testassert(Autoreleases == 0);
644
645    objc_retain(oo2);
646    testassert(Retains == 0);
647    objc_release(oo2);
648    testassert(Releases == 0);
649    objc_autorelease(oo2);
650    testassert(Autoreleases == 0);
651
652    class_setSuperclass(ccc, [InheritingSub class]);
653    
654    objc_retain(ooo);
655    testassert(Retains == 0);
656    objc_release(ooo);
657    testassert(Releases == 0);
658    objc_autorelease(ooo);
659    testassert(Autoreleases == 0);
660    
661    objc_retain(oo2);
662    testassert(Retains == 0);
663    objc_release(oo2);
664    testassert(Releases == 0);
665    objc_autorelease(oo2);
666    testassert(Autoreleases == 0);
667
668
669    testprintf("setSuperclass to dirty super clobbers (InheritingSub7)\n");
670    zero();
671
672    ccc = [InheritingSub7 class];
673    ooo = [ccc new];
674    cc2 = [InheritingSub7_2 class];
675    oo2 = [cc2 new];
676    
677    objc_retain(ooo);
678    testassert(Retains == 0);
679    objc_release(ooo);
680    testassert(Releases == 0);
681    objc_autorelease(ooo);
682    testassert(Autoreleases == 0);
683    
684    objc_retain(oo2);
685    testassert(Retains == 0);
686    objc_release(oo2);
687    testassert(Releases == 0);
688    objc_autorelease(oo2);
689    testassert(Autoreleases == 0);
690
691    class_setSuperclass(ccc, [OverridingSub class]);
692
693    objc_retain(ooo);
694    testassert(SubRetains == 1);
695    objc_release(ooo);
696    testassert(SubReleases == 1);
697    objc_autorelease(ooo);
698    testassert(SubAutoreleases == 1);
699
700    objc_retain(oo2);
701    testassert(SubRetains == 2);
702    objc_release(oo2);
703    testassert(SubReleases == 2);
704    objc_autorelease(oo2);
705    testassert(SubAutoreleases == 2);
706
707
708    testprintf("category replacement of unrelated method does not clobber (InheritingSubCat)\n");
709    zero();
710
711    ccc = [InheritingSubCat class];
712    ooo = [ccc new];
713    cc2 = [InheritingSubCat_2 class];
714    oo2 = [cc2 new];
715    
716    objc_retain(ooo);
717    testassert(Retains == 0);
718    objc_release(ooo);
719    testassert(Releases == 0);
720    objc_autorelease(ooo);
721    testassert(Autoreleases == 0);
722
723    objc_retain(oo2);
724    testassert(Retains == 0);
725    objc_release(oo2);
726    testassert(Releases == 0);
727    objc_autorelease(oo2);
728    testassert(Autoreleases == 0);
729
730    dlh = dlopen("customrr-cat1.dylib", RTLD_LAZY);
731    testassert(dlh);
732
733    objc_retain(ooo);
734    testassert(Retains == 0);
735    objc_release(ooo);
736    testassert(Releases == 0);
737    objc_autorelease(ooo);
738    testassert(Autoreleases == 0);
739
740    objc_retain(oo2);
741    testassert(Retains == 0);
742    objc_release(oo2);
743    testassert(Releases == 0);
744    objc_autorelease(oo2);
745    testassert(Autoreleases == 0);
746
747
748    testprintf("category replacement clobbers (InheritingSubCat)\n");
749    zero();
750
751    ccc = [InheritingSubCat class];
752    ooo = [ccc new];
753    cc2 = [InheritingSubCat_2 class];
754    oo2 = [cc2 new];
755    
756    objc_retain(ooo);
757    testassert(Retains == 0);
758    objc_release(ooo);
759    testassert(Releases == 0);
760    objc_autorelease(ooo);
761    testassert(Autoreleases == 0);
762
763    objc_retain(oo2);
764    testassert(Retains == 0);
765    objc_release(oo2);
766    testassert(Releases == 0);
767    objc_autorelease(oo2);
768    testassert(Autoreleases == 0);
769
770    dlh = dlopen("customrr-cat2.dylib", RTLD_LAZY);
771    testassert(dlh);
772
773    objc_retain(ooo);
774    testassert(Retains == 1);
775    objc_release(ooo);
776    testassert(Releases == 1);
777    objc_autorelease(ooo);
778    testassert(Autoreleases == 1);
779
780    objc_retain(oo2);
781    testassert(Retains == 2);
782    objc_release(oo2);
783    testassert(Releases == 2);
784    objc_autorelease(oo2);
785    testassert(Autoreleases == 2);
786
787
788    testprintf("allocateClassPair with clean super does not clobber\n");
789    zero();
790
791    objc_retain(inh);
792    testassert(Retains == 0);
793    objc_release(inh);
794    testassert(Releases == 0);
795    objc_autorelease(inh);
796    testassert(Autoreleases == 0);
797
798    ccc = objc_allocateClassPair([InheritingSub class], "CleanClassPair", 0);
799    objc_registerClassPair(ccc);
800    ooo = [ccc new];
801
802    objc_retain(inh);
803    testassert(Retains == 0);
804    objc_release(inh);
805    testassert(Releases == 0);
806    objc_autorelease(inh);
807    testassert(Autoreleases == 0);
808    
809    objc_retain(ooo);
810    testassert(Retains == 0);
811    objc_release(ooo);
812    testassert(Releases == 0);
813    objc_autorelease(ooo);
814    testassert(Autoreleases == 0);
815
816
817    testprintf("allocateClassPair with clobbered super clobbers\n");
818    zero();
819
820    ccc = objc_allocateClassPair([OverridingSub class], "DirtyClassPair", 0);
821    objc_registerClassPair(ccc);
822    ooo = [ccc new];
823    
824    objc_retain(ooo);
825    testassert(SubRetains == 1);
826    objc_release(ooo);
827    testassert(SubReleases == 1);
828    objc_autorelease(ooo);
829    testassert(SubAutoreleases == 1);
830
831
832    testprintf("allocateClassPair with clean super and override clobbers\n");
833    zero();
834
835    ccc = objc_allocateClassPair([InheritingSub class], "Dirty2ClassPair", 0);
836    class_addMethod(ccc, @selector(autorelease), (IMP)imp_fn, "");
837    objc_registerClassPair(ccc);
838    ooo = [ccc new];
839    
840    objc_retain(ooo);
841    testassert(Retains == 1);
842    objc_release(ooo);
843    testassert(Releases == 1);
844    objc_autorelease(ooo);
845    testassert(Autoreleases == 0);
846    testassert(Imps == 1);
847
848
849    // method_setImplementation and method_exchangeImplementations only 
850    // clobber when manipulating NSObject. We can only test one at a time.
851    // To test both, we need two tests: customrr and customrr2.
852
853    // These tests also check recursive clobber.
854
855#if TEST_EXCHANGEIMPLEMENTATIONS
856    testprintf("exchangeImplementations clobbers (recursive)\n");
857#else
858    testprintf("setImplementation clobbers (recursive)\n");
859#endif
860    zero();
861
862    objc_retain(obj);
863    testassert(Retains == 0);
864    objc_release(obj);
865    testassert(Releases == 0);
866    objc_autorelease(obj);
867    testassert(Autoreleases == 0);
868
869    objc_retain(inh);
870    testassert(Retains == 0);
871    objc_release(inh);
872    testassert(Releases == 0);
873    objc_autorelease(inh);
874    testassert(Autoreleases == 0);
875
876    Method meth = class_getInstanceMethod(cls, @selector(retainCount));
877    testassert(meth);
878#if TEST_EXCHANGEIMPLEMENTATIONS
879    method_exchangeImplementations(meth, meth);
880#else
881    method_setImplementation(meth, (IMP)imp_fn);
882#endif
883    
884    objc_retain(obj);
885    testassert(Retains == 1);
886    objc_release(obj);
887    testassert(Releases == 1);
888    objc_autorelease(obj);
889    testassert(Autoreleases == 1);
890
891    objc_retain(inh);
892    testassert(Retains == 2);
893    objc_release(inh);
894    testassert(Releases == 2);
895    objc_autorelease(inh);
896    testassert(Autoreleases == 2);
897
898    
899    // do not add more tests here - the recursive test must be LAST
900
901    succeed(basename(argv[0]));
902}
903
904#endif
905