]> git.saurik.com Git - apple/objc4.git/blobdiff - test/classpair.m
objc4-437.tar.gz
[apple/objc4.git] / test / classpair.m
diff --git a/test/classpair.m b/test/classpair.m
new file mode 100644 (file)
index 0000000..3897674
--- /dev/null
@@ -0,0 +1,250 @@
+#include "test.h"
+#include <objc/runtime.h>
+#include <string.h>
+#ifndef OBJC_NO_GC
+#include <objc/objc-auto.h>
+#include <auto_zone.h>
+#endif
+
+@protocol Proto
+-(void) instanceMethod;
++(void) classMethod;
+@optional
+-(void) instanceMethod2;
++(void) classMethod2;
+@end
+
+@protocol Proto2
+-(void) instanceMethod;
++(void) classMethod;
+@optional
+-(void) instanceMethod2;
++(void) classMethod_that_does_not_exist;
+@end
+
+@protocol Proto3
+-(void) instanceMethod;
++(void) classMethod_that_does_not_exist;
+@optional
+-(void) instanceMethod2;
++(void) classMethod2;
+@end
+
+@interface Super { @public id isa; } @end
+@implementation Super 
++(void)initialize { } 
++class { return self; }
++(id) new { return class_createInstance(self, 0); }
+-(void) free { object_dispose(self); }
+
++(void) classMethod { fail("+[Super classMethod] called"); }
++(void) classMethod2 { fail("+[Super classMethod2] called"); }
+-(void) instanceMethod { fail("-[Super instanceMethod] called"); }
+-(void) instanceMethod2 { fail("-[Super instanceMethod2] called"); }
+@end
+
+@interface WeakSuper : Super { __weak id weakIvar; } @end
+@implementation WeakSuper @end
+
+static int state;
+
+static void instance_fn(id self, SEL _cmd __attribute__((unused)))
+{
+    testassert(!class_isMetaClass(self->isa));
+    state++;
+}
+
+static void class_fn(id self, SEL _cmd __attribute__((unused)))
+{
+    testassert(class_isMetaClass(self->isa));
+    state++;
+}
+
+static void cycle(void)
+{    
+    Class cls;
+    BOOL ok;
+    
+    testassert(!objc_getClass("Sub"));
+    testassert([Super class]);
+
+    // Test subclass with bells and whistles
+    
+    cls = objc_allocateClassPair([Super class], "Sub", 0);
+    testassert(cls);
+#ifndef OBJC_NO_GC
+    if (objc_collecting_enabled()) {
+        testassert(auto_zone_size(auto_zone(), cls));
+        testassert(auto_zone_size(auto_zone(), cls->isa));
+    }
+#endif
+    
+    class_addMethod(cls, @selector(instanceMethod), 
+                    (IMP)&instance_fn, "v@:");
+    class_addMethod(cls->isa, @selector(classMethod), 
+                    (IMP)&class_fn, "v@:");
+
+    ok = class_addProtocol(cls, @protocol(Proto));
+    testassert(ok);
+    ok = class_addProtocol(cls, @protocol(Proto));
+    testassert(!ok);
+
+#ifndef __LP64__
+# define size 4
+# define align 2
+#else
+#define size 8
+# define align 3
+#endif
+
+    ok = class_addIvar(cls, "ivar", 4, 2, "i");
+    testassert(ok);
+    ok = class_addIvar(cls, "ivarid", size, align, "@");
+    testassert(ok);
+    ok = class_addIvar(cls, "ivaridstar", size, align, "^@");
+    testassert(ok);
+    ok = class_addIvar(cls, "ivar", 4, 2, "i");
+    testassert(!ok);
+    ok = class_addIvar(cls->isa, "classvar", 4, 2, "i");
+    testassert(!ok);
+
+    objc_registerClassPair(cls);
+
+    
+    testassert(cls == [cls class]);
+    testassert(cls == objc_getClass("Sub"));
+
+    testassert(!class_isMetaClass(cls));
+    testassert(class_isMetaClass(cls->isa));
+
+    testassert(class_getSuperclass(cls) == [Super class]);
+    testassert(class_getSuperclass(cls->isa) == [Super class]->isa);
+
+    testassert(class_getInstanceSize(cls) >= sizeof(Class) + 4 + 2*size);
+    testassert(class_conformsToProtocol(cls, @protocol(Proto)));
+
+    if (objc_collecting_enabled()) {
+        testassert(0 == strcmp(class_getIvarLayout(cls), "\x01\x12"));
+        testassert(NULL == class_getWeakIvarLayout(cls));
+    }
+
+    class_addMethod(cls, @selector(instanceMethod2), 
+                    (IMP)&instance_fn, "v@:");
+    class_addMethod(cls->isa, @selector(classMethod2), 
+                    (IMP)&class_fn, "v@:");
+
+    ok = class_addIvar(cls, "ivar2", 4, 4, "i");
+    testassert(!ok);
+    ok = class_addIvar(cls->isa, "classvar2", 4, 4, "i");
+    testassert(!ok);
+
+    ok = class_addProtocol(cls, @protocol(Proto2));
+    testassert(ok);
+    ok = class_addProtocol(cls, @protocol(Proto2));
+    testassert(!ok);
+    ok = class_addProtocol(cls, @protocol(Proto));
+    testassert(!ok);
+
+    // note: adding more methods here causes a false leak check failure
+    state = 0;
+    [cls classMethod];
+    [cls classMethod2];
+    testassert(state == 2);
+
+    id obj = [cls new];
+    state = 0;
+    [obj instanceMethod];
+    [obj instanceMethod2];
+    testassert(state == 2);
+    [obj free];
+
+
+    // Test ivar layouts of sub-subclass
+    Class cls2 = objc_allocateClassPair(cls, "SubSub", 0);
+    testassert(cls2);
+    
+    ok = class_addIvar(cls2, "ivarid2", size, align, "@");
+    testassert(ok);
+    ok = class_addIvar(cls2, "idarray", 16*sizeof(id), align, "[16@]");
+    testassert(ok);
+    ok = class_addIvar(cls2, "intarray", 16*sizeof(void*), align, "[16^]");
+    testassert(ok);    
+
+    objc_registerClassPair(cls2);
+
+    if (objc_collecting_enabled()) {
+        testassert(0 == strcmp((char *)class_getIvarLayout(cls2), "\x01\x1f\x04\xf0\x10"));
+        testassert(NULL == class_getWeakIvarLayout(cls2));
+    }
+
+    objc_disposeClassPair(cls2);
+    
+    objc_disposeClassPair(cls);
+    
+    testassert(!objc_getClass("Sub"));
+
+
+    // Test unmodified ivar layouts
+
+    cls = objc_allocateClassPair([Super class], "Sub2", 0);
+    testassert(cls);
+    objc_registerClassPair(cls);
+    if (objc_collecting_enabled()) {
+        const char *l1, *l2;
+        l1 = class_getIvarLayout([Super class]);
+        l2 = class_getIvarLayout(cls);
+        testassert(l1 == l2  ||  0 == strcmp(l1, l2));
+        l1 = class_getWeakIvarLayout([Super class]);
+        l2 = class_getWeakIvarLayout(cls);
+        testassert(l1 == l2  ||  0 == strcmp(l1, l2));
+    }
+    objc_disposeClassPair(cls);
+
+    cls = objc_allocateClassPair([WeakSuper class], "Sub3", 0);
+    testassert(cls);
+    objc_registerClassPair(cls);
+    if (objc_collecting_enabled()) {
+        const char *l1, *l2;
+        l1 = class_getIvarLayout([WeakSuper class]);
+        l2 = class_getIvarLayout(cls);
+        testassert(l1 == l2  ||  0 == strcmp(l1, l2));
+        l1 = class_getWeakIvarLayout([WeakSuper class]);
+        l2 = class_getWeakIvarLayout(cls);
+        testassert(l1 == l2  ||  0 == strcmp(l1, l2));
+    }
+    objc_disposeClassPair(cls);
+
+    // Test layout setters
+    if (objc_collecting_enabled()) {
+        cls = objc_allocateClassPair([Super class], "Sub4", 0);
+        testassert(cls);
+        class_setIvarLayout(cls, "foo");
+        class_setWeakIvarLayout(cls, NULL);
+        objc_registerClassPair(cls);
+        testassert(0 == strcmp("foo", class_getIvarLayout(cls)));
+        testassert(NULL == class_getWeakIvarLayout(cls));
+        objc_disposeClassPair(cls);
+
+        cls = objc_allocateClassPair([Super class], "Sub5", 0);
+        testassert(cls);
+        class_setIvarLayout(cls, NULL);
+        class_setWeakIvarLayout(cls, "bar");
+        objc_registerClassPair(cls);
+        testassert(NULL == class_getIvarLayout(cls));
+        testassert(0 == strcmp("bar", class_getWeakIvarLayout(cls)));
+        objc_disposeClassPair(cls);
+    }
+}
+
+int main()
+{
+    int count = 100;
+    cycle();
+    leak_mark();
+    while (count--) {
+        cycle();
+    }
+    leak_check(0);
+
+    succeed(__FILE__);
+}