]> git.saurik.com Git - apple/objc4.git/blobdiff - test/addMethods.m
objc4-756.2.tar.gz
[apple/objc4.git] / test / addMethods.m
diff --git a/test/addMethods.m b/test/addMethods.m
new file mode 100644 (file)
index 0000000..953ada4
--- /dev/null
@@ -0,0 +1,323 @@
+// TEST_CONFIG
+
+#include "test.h"
+#include "testroot.i"
+#include <objc/runtime.h>
+#include <objc/objc-internal.h>
+
+// Macros for array construction.
+// ten IMPs
+#define IMPS10 fn0, fn1, fn2, fn3, fn4, fn5, fn6, fn7, fn8, fn9
+// ten method types
+#define TYPES10 "", "", "", "", "", "", "", "", "", ""
+// ten selectors of the form name0..name9
+#define SELS10(name)                                                \
+    @selector(name##0), @selector(name##1), @selector(name##2),     \
+    @selector(name##3), @selector(name##4), @selector(name##5),     \
+    @selector(name##6), @selector(name##7), @selector(name##8),     \
+    @selector(name##9)
+
+
+@interface Super : TestRoot @end
+@implementation Super 
+-(int)superMethod0 { return 0; }
+-(int)superMethod1 { return 0; }
+-(int)superMethod2 { return 0; }
+-(int)superMethod3 { return 0; }
+-(int)superMethod4 { return 0; }
+-(int)superMethod5 { return 0; }
+-(int)superMethod6 { return 0; }
+-(int)superMethod7 { return 0; }
+-(int)superMethod8 { return 0; }
+-(int)superMethod9 { return 0; }
+-(int)bothMethod0 { return 0; }
+-(int)bothMethod1 { return 0; }
+-(int)bothMethod2 { return 0; }
+-(int)bothMethod3 { return 0; }
+-(int)bothMethod4 { return 0; }
+-(int)bothMethod5 { return 0; }
+-(int)bothMethod6 { return 0; }
+-(int)bothMethod7 { return 0; }
+-(int)bothMethod8 { return 0; }
+-(int)bothMethod9 { return 0; }
+@end
+
+@interface Sub : Super @end
+@implementation Sub
+-(int)subMethod0 { return 0; }
+-(int)subMethod1 { return 0; }
+-(int)subMethod2 { return 0; }
+-(int)subMethod3 { return 0; }
+-(int)subMethod4 { return 0; }
+-(int)subMethod5 { return 0; }
+-(int)subMethod6 { return 0; }
+-(int)subMethod7 { return 0; }
+-(int)subMethod8 { return 0; }
+-(int)subMethod9 { return 0; }
+-(int)bothMethod0 { return 0; }
+-(int)bothMethod1 { return 0; }
+-(int)bothMethod2 { return 0; }
+-(int)bothMethod3 { return 0; }
+-(int)bothMethod4 { return 0; }
+-(int)bothMethod5 { return 0; }
+-(int)bothMethod6 { return 0; }
+-(int)bothMethod7 { return 0; }
+-(int)bothMethod8 { return 0; }
+-(int)bothMethod9 { return 0; }
+@end
+
+@interface Sub2 : Super @end
+@implementation Sub2
+-(int)subMethod0 { return 0; }
+-(int)subMethod1 { return 0; }
+-(int)subMethod2 { return 0; }
+-(int)subMethod3 { return 0; }
+-(int)subMethod4 { return 0; }
+-(int)subMethod5 { return 0; }
+-(int)subMethod6 { return 0; }
+-(int)subMethod7 { return 0; }
+-(int)subMethod8 { return 0; }
+-(int)subMethod9 { return 0; }
+-(int)bothMethod0 { return 0; }
+-(int)bothMethod1 { return 0; }
+-(int)bothMethod2 { return 0; }
+-(int)bothMethod3 { return 0; }
+-(int)bothMethod4 { return 0; }
+-(int)bothMethod5 { return 0; }
+-(int)bothMethod6 { return 0; }
+-(int)bothMethod7 { return 0; }
+-(int)bothMethod8 { return 0; }
+-(int)bothMethod9 { return 0; }
+@end
+
+
+id fn0(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) {
+    return nil;
+}
+id fn1(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) {
+    return nil;
+}
+id fn2(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) {
+    return nil;
+}
+id fn3(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) {
+    return nil;
+}
+id fn4(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) {
+    return nil;
+}
+id fn5(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) {
+    return nil;
+}
+id fn6(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) {
+    return nil;
+}
+id fn7(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) {
+    return nil;
+}
+id fn8(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) {
+    return nil;
+}
+id fn9(id self __attribute__((unused)), SEL cmd __attribute__((unused)), ...) {
+    return nil;
+}
+
+void testBulkMemoryOnce(void)
+{
+    Class c = objc_allocateClassPair([TestRoot class], "c", 0);
+    objc_registerClassPair(c);
+    
+    SEL sels[10] = {
+        SELS10(method)
+    };
+    IMP imps[10] = {
+        IMPS10
+    };
+    const char *types[10] = {
+        TYPES10
+    };
+    
+    uint32_t failureCount = 0;
+    SEL *failed;
+    
+    // Test all successes.
+    failed = class_addMethodsBulk(c, sels, imps, types, 4, &failureCount);
+    testassert(failed == NULL);
+    testassert(failureCount == 0);
+    
+    // Test mixed success and failure (this overlaps the previous one, so there
+    // will be one of each).
+    failed = class_addMethodsBulk(c, sels + 3, imps + 3, types + 3, 2,
+                                  &failureCount);
+    testassert(failed != NULL);
+    testassert(failureCount == 1);
+    testassert(failed[0] == sels[3]);
+    free(failed);
+    
+    // Test total failure.
+    failed = class_addMethodsBulk(c, sels, imps, types, 5, &failureCount);
+    testassert(failed != NULL);
+    testassert(failureCount == 5);
+    for(int i = 0; i < 5; i++) {
+        testassert(failed[i] == sels[i]);
+    }
+    free(failed);
+    
+    class_replaceMethodsBulk(c, sels, imps, types, 10);
+    
+    for(int i = 0; i < 10; i++) {
+        testassert(class_getMethodImplementation(c, sels[i]) == imps[i]);
+    }
+    
+    objc_disposeClassPair(c);
+}
+
+int main()
+{
+    IMP dummyIMPs[130] = {
+        IMPS10, IMPS10, IMPS10, IMPS10, IMPS10,
+        IMPS10, IMPS10, IMPS10, IMPS10, IMPS10,
+        IMPS10, IMPS10, IMPS10,
+    };
+    
+    // similar to dummyIMPs but with different values in each slot
+    IMP dummyIMPs2[130] = {
+        fn5, fn6, fn7, fn8, fn9,
+        IMPS10, IMPS10, IMPS10, IMPS10, IMPS10,
+        IMPS10, IMPS10, IMPS10, IMPS10, IMPS10,
+        IMPS10, IMPS10,
+        fn0, fn1, fn2, fn3, fn4,
+    };
+    
+    const char *dummyTypes[130] = {
+        TYPES10, TYPES10, TYPES10, TYPES10, TYPES10,
+        TYPES10, TYPES10, TYPES10, TYPES10, TYPES10,
+        TYPES10, TYPES10, TYPES10,
+    };
+    
+    SEL addSELs[20] = {
+        SELS10(superMethod),
+        SELS10(superMethodAddNew)
+    };
+    
+    uint32_t failedCount = 0;
+    SEL *failed;
+    
+    failed = class_addMethodsBulk([Super class], addSELs, dummyIMPs, dummyTypes,
+                                  20, &failedCount);
+    
+    // class_addMethodsBulk reports failures for all methods that already exist
+    testassert(failed != NULL);
+    testassert(failedCount == 10);
+    
+    // class_addMethodsBulk failed for existing implementations
+    for(int i = 0; i < 10; i++) {
+        testassert(failed[i] == addSELs[i]);
+        testassert(class_getMethodImplementation([Super class], addSELs[i])
+                   != dummyIMPs[i]);
+    }
+    
+    free(failed);
+
+    // class_addMethodsBulk does add root implementations
+    for(int i = 10; i < 20; i++) {
+        testassert(class_getMethodImplementation([Super class], addSELs[i])
+                   == dummyIMPs[i]);
+    }
+    
+    // class_addMethod does override superclass implementations
+    failed = class_addMethodsBulk([Sub class], addSELs, dummyIMPs, dummyTypes,
+                                  10, &failedCount);
+    testassert(failedCount == 0);
+    testassert(failed == NULL);
+    for(int i = 0; i < 10; i++) {
+        testassert(class_getMethodImplementation([Sub class], addSELs[i])
+                   == dummyIMPs[i]);
+    }
+    
+    SEL subReplaceSELs[40] = {
+        SELS10(superMethod),
+        SELS10(subMethodNew),
+        SELS10(subMethod),
+        SELS10(bothMethod),
+    };
+    
+    // class_replaceMethodsBulk adds new implementations or replaces existing
+    // ones for methods that exist on the superclass, the subclass, both, or
+    // neither
+    class_replaceMethodsBulk([Sub2 class], subReplaceSELs, dummyIMPs,
+                             dummyTypes, 40);
+    for(int i = 0; i < 40; i++) {
+        IMP newIMP = class_getMethodImplementation([Sub2 class],
+                                                   subReplaceSELs[i]);
+        testassert(newIMP == dummyIMPs[i]);
+    }
+    
+    SEL superReplaceSELs[20] = {
+        SELS10(superMethod),
+        SELS10(superMethodNew),
+    };
+    
+    // class_replaceMethodsBulk adds new implementations or replaces existing
+    // ones in the superclass
+    class_replaceMethodsBulk([Super class], superReplaceSELs, dummyIMPs,
+                             dummyTypes, 20);
+    for(int i = 0; i < 20; i++) {
+        IMP newIMP = class_getMethodImplementation([Super class],
+                                                   superReplaceSELs[i]);
+        testassert(newIMP == dummyIMPs[i]);
+    }
+
+
+    // class_addMethodsBulk, where almost all of the requested additions
+    // already exist and thus can't be added. (They were already added
+    // above by class_replaceMethodsBulk([Sub2 class], subReplaceSELs, ...).)
+
+    // This list is large in the hope of provoking any realloc() of the
+    // new method list inside addMethods().
+    // The runtime doesn't care that the list contains lots of duplicates.
+    SEL subAddMostlyExistingSELs[130] = {
+        SELS10(superMethod), SELS10(subMethodNew), SELS10(subMethod),
+        SELS10(superMethod), SELS10(subMethodNew), SELS10(subMethod),
+        SELS10(superMethod), SELS10(subMethodNew), SELS10(subMethod),
+        SELS10(superMethod), SELS10(subMethodNew), SELS10(subMethod),
+        SELS10(bothMethod),
+    };
+    subAddMostlyExistingSELs[16] = @selector(INDEX_16_IS_DIFFERENT);
+
+    failed = class_addMethodsBulk([Sub2 class], subAddMostlyExistingSELs,
+                                  dummyIMPs2, dummyTypes, 130, &failedCount);
+    testassert(failedCount == 129);
+    testassert(failed != NULL);
+
+    for(int i = 0; i < 130; i++) {
+        IMP newIMP = class_getMethodImplementation([Sub2 class],
+                                                   subAddMostlyExistingSELs[i]);
+        if (i == 16) {
+            // the only one that was actually added
+            testassert(newIMP != dummyIMPs[i]);
+            testassert(newIMP == dummyIMPs2[i]);
+        } else {
+            // the others should all have failed
+            testassert(newIMP == dummyIMPs[i]);
+            testassert(newIMP != dummyIMPs2[i]);
+        }
+    }
+    for (uint32_t i = 0; i < failedCount; i++) {
+        testassert(failed[i] != NULL);
+        testassert(failed[i] != subAddMostlyExistingSELs[16]);
+    }
+
+    
+    // fixme actually try calling them
+    
+    // make sure the Bulk functions aren't leaking
+    testBulkMemoryOnce();
+    leak_mark();
+    for(int i = 0; i < 10; i++) {
+        testBulkMemoryOnce();
+    }
+    leak_check(0);
+    
+    succeed(__FILE__);
+}