--- /dev/null
+// 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__);
+}