]> git.saurik.com Git - apple/objc4.git/blobdiff - test/ignoredSelector.m
objc4-437.tar.gz
[apple/objc4.git] / test / ignoredSelector.m
diff --git a/test/ignoredSelector.m b/test/ignoredSelector.m
new file mode 100644 (file)
index 0000000..f0f87b7
--- /dev/null
@@ -0,0 +1,287 @@
+#include "test.h"
+#include <objc/runtime.h>
+#include <objc/message.h>
+#include <objc/objc-auto.h>
+
+static int state = 0;
+
+@interface Super { id isa; } @end
+@implementation Super 
++class { return self; }
++(void)initialize { } 
+
++normal { state = 1; return self; } 
++normal2 { testassert(0); } 
++retain { state = 2; return self; } 
++release { state = 3; return self; } 
++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
+
+@interface Empty { id isa; } @end
+@implementation Empty
++class { return self; }
++(void)initialize { }
++forward:(SEL)sel :(marg_list)margs { 
+    (void)sel; (void)margs; 
+    state = 1; 
+    return nil; 
+} 
+@end
+
+@interface Empty (Unimplemented)
++normal;
++retain;
++release;
++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 normal, normal2, retain, release, autorelease, dealloc, retainCount;
+static Method normalMethod, normal2Method, retainMethod, releaseMethod, autoreleaseMethod, deallocMethod, retainCountMethod;
+
+void cycle(Class cls)
+{
+    id idVal;
+    uintptr_t intVal;
+
+    if (objc_collecting_enabled()) {
+        // GC: all ignored selectors are identical
+        testassert(@selector(retain) == @selector(release)      &&  
+                   @selector(retain) == @selector(autorelease)  &&  
+                   @selector(retain) == @selector(dealloc)      &&  
+                   @selector(retain) == @selector(retainCount)  );
+    }
+    else {
+        // 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(normal) != @selector(retain)       &&  
+               @selector(normal) != @selector(release)      &&  
+               @selector(normal) != @selector(autorelease)  &&  
+               @selector(normal) != @selector(dealloc)      &&  
+               @selector(normal) != @selector(retainCount)  );
+
+    getImp(normal);
+    getImp(normal2);
+    getImp(retain);
+    getImp(release);
+    getImp(autorelease);
+    getImp(dealloc);
+    getImp(retainCount);
+
+    if (objc_collecting_enabled()) {
+        // 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(normal != retain       &&  
+               normal != release      &&  
+               normal != autorelease  &&  
+               normal != dealloc      &&  
+               normal != retainCount  );
+    
+    // Test calls via method_invoke
+
+    idVal =         ((id(*)(id, Method))method_invoke)(cls, normalMethod);
+    testassert(state == 1);
+    testassert(idVal == cls);
+
+    state = 0;
+    idVal =         ((id(*)(id, Method))method_invoke)(cls, retainMethod);
+    testassert(state == (objc_collecting_enabled() ? 0 : 2));
+    testassert(idVal == cls);
+
+    idVal =         ((id(*)(id, Method))method_invoke)(cls, releaseMethod);
+    testassert(state == (objc_collecting_enabled() ? 0 : 3));
+    testassert(idVal == cls);
+
+    idVal =         ((id(*)(id, Method))method_invoke)(cls, autoreleaseMethod);
+    testassert(state == (objc_collecting_enabled() ? 0 : 4));
+    testassert(idVal == cls);
+
+    (void)        ((void(*)(id, Method))method_invoke)(cls, deallocMethod);
+    testassert(state == (objc_collecting_enabled() ? 0 : 5));
+
+    intVal = ((uintptr_t(*)(id, Method))method_invoke)(cls, retainCountMethod);
+    testassert(state == (objc_collecting_enabled() ? 0 : 6));
+    testassert(intVal == (objc_collecting_enabled() ? (uintptr_t)cls : 6));
+
+
+    // Test calls via objc_msgSend
+
+    state = 0;
+    idVal  = [cls normal];
+    testassert(state == 1);
+    testassert(idVal == cls);
+
+    state = 0;
+    idVal  = [cls retain];
+    testassert(state == (objc_collecting_enabled() ? 0 : 2));
+    testassert(idVal == cls);
+
+    idVal  = [cls release];
+    testassert(state == (objc_collecting_enabled() ? 0 : 3));
+    testassert(idVal == cls);
+
+    idVal  = [cls autorelease];
+    testassert(state == (objc_collecting_enabled() ? 0 : 4));
+    testassert(idVal == cls);
+
+    (void)   [cls dealloc];
+    testassert(state == (objc_collecting_enabled() ? 0 : 5));
+
+    intVal = [cls retainCount];
+    testassert(state == (objc_collecting_enabled() ? 0 : 6));
+    testassert(intVal == (objc_collecting_enabled() ? (uintptr_t)cls : 6));
+}
+
+int main()
+{
+    Class cls;
+
+    // Test selector API
+
+    testassert(sel_registerName("retain") == @selector(retain));
+    testassert(sel_getUid("retain") == @selector(retain));
+    if (objc_collecting_enabled()) {
+        testassert(0 == strcmp(sel_getName(@selector(retain)), "<ignored selector>"));
+    } else {
+        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_collecting_enabled()) {
+        // 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(normal2 != dealloc);
+        method_exchangeImplementations(retainMethod, releaseMethod);
+        method_exchangeImplementations(autoreleaseMethod, retainCountMethod);
+        method_exchangeImplementations(deallocMethod, normal2Method);
+        cycle(cls);
+        // normal2 exchanged with ignored method is now ignored too
+        testassert(normal2 == dealloc);
+
+        // 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_collecting_enabled()) {
+        Class cls;
+        id idVal;
+        uintptr_t intVal;
+
+        cls = [Empty class];
+        state = 0;
+
+        idVal  = [Empty retain];
+        testassert(state == 0);
+        testassert(idVal == cls);
+
+        idVal  = [Empty release];
+        testassert(state == 0);
+        testassert(idVal == cls);
+
+        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 normal];
+        testassert(state == 1);
+        testassert(idVal == nil);
+    }    
+
+    succeed(__FILE__);
+}