category.m   [plain text]


// TEST_CFLAGS -Wl,-no_objc_category_merging

#include "test.h"
#include "testroot.i"
#include <string.h>
#include <objc/runtime.h>

static int state = 0;

@interface Super : TestRoot @end
@implementation Super
-(void)instancemethod { fail("-instancemethod not overridden by category"); }
+(void)method { fail("+method not overridden by category"); } 
@end

@interface Super (Category) @end
@implementation Super (Category) 
+(void)method { 
    testprintf("in [Super(Category) method]\n"); 
    testassert(self == [Super class]);
    testassert(state == 0);
    state = 1;
}
-(void)instancemethod { 
    testprintf("in [Super(Category) instancemethod]\n"); 
    testassert(object_getClass(self) == [Super class]);
    testassert(state == 1);
    state = 2;
}
@end

@interface Super (PropertyCategory) 
@property int i;
@property(class) int i;
@end
@implementation Super (PropertyCategory) 
- (int)i { return 0; }
- (void)setI:(int)value { (void)value; }
+ (int)i { return 0; }
+ (void)setI:(int)value { (void)value; }
@end

// rdar://5086110  memory smasher in category with class method and property
@interface Super (r5086110) 
@property int property5086110;
@end
@implementation Super (r5086110) 
+(void)method5086110 { 
    fail("method method5086110 called!");
}
- (int)property5086110 { fail("property5086110 called!"); return 0; }
- (void)setProperty5086110:(int)value { fail("setProperty5086110 called!"); (void)value; }
@end

// rdar://25605427 incorrect handling of class properties in 10.11 and earlier
@interface Super25605427 : TestRoot
@property(class, readonly) int i;
@end
@implementation Super25605427
+(int)i { return 0; }
@end

@interface Super25605427 (r25605427a)
@property(readonly) int r25605427a1;
@end
@implementation Super25605427 (r25605427a)
-(int)r25605427a1 { return 0; }
+(int)r25605427a2 { return 0; }
@end

@interface Super25605427 (r25605427b)
@property(readonly) int r25605427b1;
@end
@implementation Super25605427 (r25605427b)
-(int)r25605427b1 { return 0; }
+(int)r25605427b2 { return 0; }
@end

@interface Super25605427 (r25605427c)
@property(readonly) int r25605427c1;
@end
@implementation Super25605427 (r25605427c)
-(int)r25605427c1 { return 0; }
+(int)r25605427c2 { return 0; }
@end

@interface Super25605427 (r25605427d)
@property(readonly) int r25605427d1;
@end
@implementation Super25605427 (r25605427d)
-(int)r25605427d1 { return 0; }
+(int)r25605427d2 { return 0; }
@end


@interface PropertyClass : Super {
    int q;
}
@property(readonly) int q;
@end
@implementation PropertyClass
@synthesize q;
@end

@interface PropertyClass (PropertyCategory)
@property int q;
@end
@implementation PropertyClass (PropertyCategory)
@dynamic q;
@end


int main()
{
    {
        // rdar://25605427 bugs in 10.11 and earlier when metaclass
        // has a property and category has metaclass additions.
        // Memory smasher in buildPropertyList (caught by guard malloc)
        Class cls = [Super25605427 class];
        // Incorrect attachment of instance properties from category to metacls
        testassert(class_getProperty(cls, "r25605427d1"));
        testassert(! class_getProperty(object_getClass(cls), "r25605427d1"));
    }

    // methods introduced by category
    state = 0;
    [Super method];
    [[Super new] instancemethod];
    testassert(state == 2);

    // property introduced by category
    objc_property_t p = class_getProperty([Super class], "i");
    testassert(p);
    testassert(0 == strcmp(property_getName(p), "i"));
    testassert(property_getAttributes(p));

    objc_property_t p2 = class_getProperty(object_getClass([Super class]), "i");
    testassert(p2);
    testassert(p != p2);
    testassert(0 == strcmp(property_getName(p2), "i"));
    testassert(property_getAttributes(p2));

    // methods introduced by category's property
    Method m;
    m = class_getInstanceMethod([Super class], @selector(i));
    testassert(m);
    m = class_getInstanceMethod([Super class], @selector(setI:));
    testassert(m);

    m = class_getClassMethod([Super class], @selector(i));
    testassert(m);
    m = class_getClassMethod([Super class], @selector(setI:));
    testassert(m);

    // class's property shadowed by category's property
    objc_property_t *plist = class_copyPropertyList([PropertyClass class], NULL);
    testassert(plist);
    testassert(plist[0]);
    testassert(0 == strcmp(property_getName(plist[0]), "q"));
    testassert(0 == strcmp(property_getAttributes(plist[0]), "Ti,D"));
    testassert(plist[1]);
    testassert(0 == strcmp(property_getName(plist[1]), "q"));
    testassert(0 == strcmp(property_getAttributes(plist[1]), "Ti,R,Vq"));
    testassert(!plist[2]);
    free(plist);
    
    succeed(__FILE__);
}