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