// TEST_CONFIG MEM=mrc,gc // TEST_CFLAGS -Wno-deprecated-declarations #include "test.h" #include <objc/runtime.h> #include <objc/message.h> #include <objc/objc-auto.h> static int state = 0; OBJC_ROOT_CLASS @interface Super { id isa; } @end @implementation Super +(id)class { return self; } +(void)initialize { } +(id)ordinary { state = 1; return self; } +(id)ordinary2 { testassert(0); } +(id)retain { state = 2; return self; } +(void)release { state = 3; } +(id)autorelease { state = 4; return self; } +(void)dealloc { state = 5; } +(uintptr_t)retainCount { state = 6; return 6; } @end @interface Sub : Super @end @implementation Sub @end @interface Sub2 : Super @end @implementation Sub2 @end OBJC_ROOT_CLASS @interface Empty { id isa; } @end @implementation Empty +(id)class { return self; } +(void)initialize { } +(id)forward:(SEL)sel :(marg_list)margs { (void)sel; (void)margs; state = 1; return nil; } @end @interface Empty (Unimplemented) +(id)ordinary; +(id)retain; +(void)release; +(id)autorelease; +(void)dealloc; +(uintptr_t)retainCount; @end #define getImp(sel) \ do { \ sel##Method = class_getClassMethod(cls, @selector(sel)); \ testassert(sel##Method); \ testassert(@selector(sel) == method_getName(sel##Method)); \ sel = method_getImplementation(sel##Method); \ } while (0) static IMP ordinary, ordinary2, retain, release, autorelease, dealloc, retainCount; static Method ordinaryMethod, ordinary2Method, retainMethod, releaseMethod, autoreleaseMethod, deallocMethod, retainCountMethod; void cycle(Class cls) { id idVal; uintptr_t intVal; #if defined(__i386__) if (objc_collectingEnabled()) { // i386 GC: all ignored selectors are identical testassert(@selector(retain) == @selector(release) && @selector(retain) == @selector(autorelease) && @selector(retain) == @selector(dealloc) && @selector(retain) == @selector(retainCount) ); } else #endif { // x86_64 GC or no GC: all ignored selectors are distinct testassert(@selector(retain) != @selector(release) && @selector(retain) != @selector(autorelease) && @selector(retain) != @selector(dealloc) && @selector(retain) != @selector(retainCount) ); } // no ignored selector matches a real selector testassert(@selector(ordinary) != @selector(retain) && @selector(ordinary) != @selector(release) && @selector(ordinary) != @selector(autorelease) && @selector(ordinary) != @selector(dealloc) && @selector(ordinary) != @selector(retainCount) ); getImp(ordinary); getImp(ordinary2); getImp(retain); getImp(release); getImp(autorelease); getImp(dealloc); getImp(retainCount); if (objc_collectingEnabled()) { // GC: all ignored selector IMPs are identical testassert(retain == release && retain == autorelease && retain == dealloc && retain == retainCount ); } else { // no GC: all ignored selector IMPs are distinct testassert(retain != release && retain != autorelease && retain != dealloc && retain != retainCount ); } // no ignored selector IMP matches a real selector IMP testassert(ordinary != retain && ordinary != release && ordinary != autorelease && ordinary != dealloc && ordinary != retainCount ); // Test calls via method_invoke idVal = ((id(*)(id, Method))method_invoke)(cls, ordinaryMethod); testassert(state == 1); testassert(idVal == cls); state = 0; idVal = ((id(*)(id, Method))method_invoke)(cls, retainMethod); testassert(state == (objc_collectingEnabled() ? 0 : 2)); testassert(idVal == cls); (void) ((void(*)(id, Method))method_invoke)(cls, releaseMethod); testassert(state == (objc_collectingEnabled() ? 0 : 3)); idVal = ((id(*)(id, Method))method_invoke)(cls, autoreleaseMethod); testassert(state == (objc_collectingEnabled() ? 0 : 4)); testassert(idVal == cls); (void) ((void(*)(id, Method))method_invoke)(cls, deallocMethod); testassert(state == (objc_collectingEnabled() ? 0 : 5)); intVal = ((uintptr_t(*)(id, Method))method_invoke)(cls, retainCountMethod); testassert(state == (objc_collectingEnabled() ? 0 : 6)); testassert(intVal == (objc_collectingEnabled() ? (uintptr_t)cls : 6)); // Test calls via compiled objc_msgSend state = 0; idVal = [cls ordinary]; testassert(state == 1); testassert(idVal == cls); state = 0; idVal = [cls retain]; testassert(state == (objc_collectingEnabled() ? 0 : 2)); testassert(idVal == cls); (void) [cls release]; testassert(state == (objc_collectingEnabled() ? 0 : 3)); idVal = [cls autorelease]; testassert(state == (objc_collectingEnabled() ? 0 : 4)); testassert(idVal == cls); (void) [cls dealloc]; testassert(state == (objc_collectingEnabled() ? 0 : 5)); intVal = [cls retainCount]; testassert(state == (objc_collectingEnabled() ? 0 : 6)); testassert(intVal == (objc_collectingEnabled() ? (uintptr_t)cls : 6)); // Test calls via handwritten objc_msgSend state = 0; idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(ordinary)); testassert(state == 1); testassert(idVal == cls); state = 0; idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(retain)); testassert(state == (objc_collectingEnabled() ? 0 : 2)); testassert(idVal == cls); (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(release)); testassert(state == (objc_collectingEnabled() ? 0 : 3)); idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(autorelease)); testassert(state == (objc_collectingEnabled() ? 0 : 4)); testassert(idVal == cls); (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(dealloc)); testassert(state == (objc_collectingEnabled() ? 0 : 5)); intVal = ((uintptr_t(*)(id,SEL))objc_msgSend)(cls, @selector(retainCount)); testassert(state == (objc_collectingEnabled() ? 0 : 6)); testassert(intVal == (objc_collectingEnabled() ? (uintptr_t)cls : 6)); } int main() { Class cls; // Test selector API testassert(sel_registerName("retain") == @selector(retain)); testassert(sel_getUid("retain") == @selector(retain)); #if defined(__i386__) if (objc_collectingEnabled()) { // only i386's GC currently remaps these testassert(0 == strcmp(sel_getName(@selector(retain)), "<ignored selector>")); } else #endif { testassert(0 == strcmp(sel_getName(@selector(retain)), "retain")); } #if !__OBJC2__ testassert(sel_isMapped(@selector(retain))); #endif cls = [Sub class]; testassert(cls); cycle(cls); cls = [Super class]; testassert(cls); cycle(cls); if (objc_collectingEnabled()) { // rdar://6200570 Method manipulation shouldn't affect ignored methods. cls = [Super class]; testassert(cls); cycle(cls); method_setImplementation(retainMethod, (IMP)1); method_setImplementation(releaseMethod, (IMP)1); method_setImplementation(autoreleaseMethod, (IMP)1); method_setImplementation(deallocMethod, (IMP)1); method_setImplementation(retainCountMethod, (IMP)1); cycle(cls); testassert(ordinary2 != retainCount); method_exchangeImplementations(retainMethod, autoreleaseMethod); method_exchangeImplementations(deallocMethod, releaseMethod); method_exchangeImplementations(retainCountMethod, ordinary2Method); cycle(cls); // ordinary2 exchanged with ignored method is now ignored too testassert(ordinary2 == retainCount); // replace == replace existing class_replaceMethod(cls, @selector(retain), (IMP)1, ""); class_replaceMethod(cls, @selector(release), (IMP)1, ""); class_replaceMethod(cls, @selector(autorelease), (IMP)1, ""); class_replaceMethod(cls, @selector(dealloc), (IMP)1, ""); class_replaceMethod(cls, @selector(retainCount), (IMP)1, ""); cycle(cls); cls = [Sub class]; testassert(cls); cycle(cls); // replace == add override class_replaceMethod(cls, @selector(retain), (IMP)1, ""); class_replaceMethod(cls, @selector(release), (IMP)1, ""); class_replaceMethod(cls, @selector(autorelease), (IMP)1, ""); class_replaceMethod(cls, @selector(dealloc), (IMP)1, ""); class_replaceMethod(cls, @selector(retainCount), (IMP)1, ""); cycle(cls); cls = [Sub2 class]; testassert(cls); cycle(cls); class_addMethod(cls, @selector(retain), (IMP)1, ""); class_addMethod(cls, @selector(release), (IMP)1, ""); class_addMethod(cls, @selector(autorelease), (IMP)1, ""); class_addMethod(cls, @selector(dealloc), (IMP)1, ""); class_addMethod(cls, @selector(retainCount), (IMP)1, ""); cycle(cls); } // Test calls via objc_msgSend - ignored selectors are ignored // under GC even if the class provides no implementation for them if (objc_collectingEnabled()) { Class cls; id idVal; uintptr_t intVal; cls = [Empty class]; state = 0; idVal = [Empty retain]; testassert(state == 0); testassert(idVal == cls); (void) [Empty release]; testassert(state == 0); idVal = [Empty autorelease]; testassert(state == 0); testassert(idVal == cls); (void) [Empty dealloc]; testassert(state == 0); intVal = [Empty retainCount]; testassert(state == 0); testassert(intVal == (uintptr_t)cls); idVal = [Empty ordinary]; testassert(state == 1); testassert(idVal == nil); state = 0; idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(retain)); testassert(state == 0); testassert(idVal == cls); (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(release)); testassert(state == 0); idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(autorelease)); testassert(state == 0); testassert(idVal == cls); (void) ((void(*)(id,SEL))objc_msgSend)(cls, @selector(dealloc)); testassert(state == 0); intVal = ((uintptr_t(*)(id,SEL))objc_msgSend)(cls, @selector(retainCount)); testassert(state == 0); testassert(intVal == (uintptr_t)cls); idVal = ((id(*)(id,SEL))objc_msgSend)(cls, @selector(ordinary)); testassert(state == 1); testassert(idVal == nil); } succeed(__FILE__); }