diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index 2e7052d..e02f178 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -15,6 +15,7 @@ set(TESTS NestedExceptions.m PropertyAttributeTest.m PropertyIntrospectionTest.m + PropertyIntrospectionTest2.m ProtocolCreation.m RuntimeTest.m objc_msgSend.m diff --git a/Test/PropertyIntrospectionTest2.m b/Test/PropertyIntrospectionTest2.m new file mode 100644 index 0000000..5a69b1d --- /dev/null +++ b/Test/PropertyIntrospectionTest2.m @@ -0,0 +1,695 @@ +#if __APPLE__ +// Compile command: clang -o PropertyAttributeTest2 -framework Foundation PropertyAttributeTest2.m +#include +#include + +__attribute__((objc_root_class)) +@interface Test { id isa; } +@end +@implementation Test ++ (id)class { return self; } ++ (id)new +{ + return class_createInstance(self, 0); +} +- (void)dealloc +{ + object_dispose(self); +} +- (id)retain +{ + return self; +} +- (oneway void)release +{ +} +@end +#else +#include "Test.h" +#endif +#include +#include +#include +#include + +enum FooManChu { FOO, MAN, CHU }; +struct YorkshireTeaStruct { int pot; char lady; }; +typedef struct YorkshireTeaStruct YorkshireTeaStructType; +union MoneyUnion { float alone; double down; }; + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#if __has_attribute(objc_root_class) +__attribute__((objc_root_class)) +#endif +@interface PropertyTest +{ +@public + Class isa; + char charDefault; + double doubleDefault; + enum FooManChu enumDefault; + float floatDefault; + int intDefault; + long longDefault; + short shortDefault; + signed signedDefault; + struct YorkshireTeaStruct structDefault; + YorkshireTeaStructType typedefDefault; + union MoneyUnion unionDefault; + unsigned unsignedDefault; + int (*functionPointerDefault)(char *); + int *intPointer; + void *voidPointerDefault; + int intSynthEquals; + int intSetterGetter; + int intReadonly; + int intReadonlyGetter; + int intReadwrite; + int intAssign; + id idDefault; + id idRetain; + id idCopy; + id idWeak; + id idStrong; + int intNonatomic; + id idReadonlyCopyNonatomic; + id idReadonlyRetainNonatomic; + id idReadonlyWeakNonatomic; + id _idOther; +} +@property char charDefault; +@property double doubleDefault; +@property enum FooManChu enumDefault; +@property float floatDefault; +@property int intDefault; +@property long longDefault; +@property short shortDefault; +@property signed signedDefault; +@property struct YorkshireTeaStruct structDefault; +@property YorkshireTeaStructType typedefDefault; +@property union MoneyUnion unionDefault; +@property unsigned unsignedDefault; +@property int (*functionPointerDefault)(char *); +@property int *intPointer; +@property void *voidPointerDefault; +@property(getter=intGetFoo, setter=intSetFoo:) int intSetterGetter; +@property(readonly) int intReadonly; +@property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter; +@property(readwrite) int intReadwrite; +@property(assign) int intAssign; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wobjc-property-no-attribute" +@property id idDefault; +#pragma GCC diagnostic pop +@property(retain) id idRetain; +@property(copy) id idCopy; +@property(weak) id idWeak; +@property(strong) id idStrong; +@property(nonatomic) int intNonatomic; +@property(nonatomic, readonly, copy) id idReadonlyCopyNonatomic; +@property(nonatomic, readonly, retain) id idReadonlyRetainNonatomic; +@property(nonatomic, readonly, weak) id idReadonlyWeakNonatomic; +@property(retain) id idOther; +@property(retain) id idDynamic; +@property(retain, nonatomic, getter=dynamicGetterSetter, setter=setDynamicGetterSetter:) id idDynamicGetterSetter; +@end + +@implementation PropertyTest +@synthesize charDefault; +@synthesize doubleDefault; +@synthesize enumDefault; +@synthesize floatDefault; +@synthesize intDefault; +@synthesize longDefault; +@synthesize shortDefault; +@synthesize signedDefault; +@synthesize structDefault; +@synthesize typedefDefault; +@synthesize unionDefault; +@synthesize unsignedDefault; +@synthesize functionPointerDefault; +@synthesize intPointer; +@synthesize voidPointerDefault; +@synthesize intSetterGetter; +@synthesize intReadonly; +@synthesize intReadonlyGetter; +@synthesize intReadwrite; +@synthesize intAssign; +@synthesize idDefault; +@synthesize idRetain; +@synthesize idCopy; +@synthesize idWeak; +@synthesize idStrong; +@synthesize intNonatomic; +@synthesize idReadonlyCopyNonatomic; +@synthesize idReadonlyRetainNonatomic; +@synthesize idReadonlyWeakNonatomic; +@synthesize idOther = _idOther; +@dynamic idDynamic; +@dynamic idDynamicGetterSetter; +@end + +@protocol ProtocolTest +@property char charDefault; +@property double doubleDefault; +@property enum FooManChu enumDefault; +@property float floatDefault; +@property int intDefault; +@property long longDefault; +@property short shortDefault; +@property signed signedDefault; +@property struct YorkshireTeaStruct structDefault; +@property YorkshireTeaStructType typedefDefault; +@property union MoneyUnion unionDefault; +@property unsigned unsignedDefault; +@property int (*functionPointerDefault)(char *); +@property int *intPointer; +@property void *voidPointerDefault; +@property(getter=intGetFoo, setter=intSetFoo:) int intSetterGetter; +@property(readonly) int intReadonly; +@property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter; +@property(readwrite) int intReadwrite; +@property(assign) int intAssign; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wobjc-property-no-attribute" +@property id idDefault; +#pragma GCC diagnostic pop +@property(retain) id idRetain; +@property(copy) id idCopy; +@property(weak) id idWeak; +@property(strong) id idStrong; +@property(nonatomic) int intNonatomic; +@property(nonatomic, readonly, copy) id idReadonlyCopyNonatomic; +@property(nonatomic, readonly, retain) id idReadonlyRetainNonatomic; +@property(nonatomic, readonly, weak) id idReadonlyWeakNonatomic; +@property(retain) id idOther; +@property(retain) id idDynamic; +@property(retain, nonatomic, getter=dynamicGetterSetter, setter=setDynamicGetterSetter:) id idDynamicGetterSetter; +@end + +#if __has_attribute(objc_root_class) +__attribute__((objc_root_class)) +#endif +@interface PropertyProtocolTest +{ + Class isa; + char charDefault; + double doubleDefault; + enum FooManChu enumDefault; + float floatDefault; + int intDefault; + long longDefault; + short shortDefault; + signed signedDefault; + struct YorkshireTeaStruct structDefault; + YorkshireTeaStructType typedefDefault; + union MoneyUnion unionDefault; + unsigned unsignedDefault; + int (*functionPointerDefault)(char *); + int *intPointer; + void *voidPointerDefault; + int intSynthEquals; + int intSetterGetter; + int intReadonly; + int intReadonlyGetter; + int intReadwrite; + int intAssign; + id idDefault; + id idRetain; + id idCopy; + id idWeak; + id idStrong; + int intNonatomic; + id idReadonlyCopyNonatomic; + id idReadonlyRetainNonatomic; + id idReadonlyWeakNonatomic; + id _idOther; +} +@end + +@implementation PropertyProtocolTest +@synthesize charDefault; +@synthesize doubleDefault; +@synthesize enumDefault; +@synthesize floatDefault; +@synthesize intDefault; +@synthesize longDefault; +@synthesize shortDefault; +@synthesize signedDefault; +@synthesize structDefault; +@synthesize typedefDefault; +@synthesize unionDefault; +@synthesize unsignedDefault; +@synthesize functionPointerDefault; +@synthesize intPointer; +@synthesize voidPointerDefault; +@synthesize intSetterGetter; +@synthesize intReadonly; +@synthesize intReadonlyGetter; +@synthesize intReadwrite; +@synthesize intAssign; +@synthesize idDefault; +@synthesize idRetain; +@synthesize idCopy; +@synthesize idWeak; +@synthesize idStrong; +@synthesize intNonatomic; +@synthesize idReadonlyCopyNonatomic; +@synthesize idReadonlyRetainNonatomic; +@synthesize idReadonlyWeakNonatomic; +@synthesize idOther = _idOther; +@dynamic idDynamic; +@dynamic idDynamicGetterSetter; +@end + +#define ATTR(n, v) (objc_property_attribute_t){(n), (v)} +#define ATTRS(...) (objc_property_attribute_t[]){ __VA_ARGS__ }, \ + sizeof((objc_property_attribute_t[]){ __VA_ARGS__ }) / sizeof(objc_property_attribute_t) + +static void testPropertyForProperty(objc_property_t p, + const char *name, + const char *types, + objc_property_attribute_t* list, + unsigned int size) +{ + assert(0 != p); + assert(strcmp(name, property_getName(p)) == 0); + const char *attrs = property_getAttributes(p); + assert(0 != attrs); + assert(strcmp(types, attrs) == 0); + unsigned int attrsCount = 0; + objc_property_attribute_t *attrsList = property_copyAttributeList(p, &attrsCount); + assert(0 != attrsList); + assert(attrsCount == size); + for (unsigned int index=0; indexname != NULL; attrsCount++, ra++) {} + assert(attrsCount == size); + free(attrsList); + for (unsigned int index=0; indexidRetain == testValue); + assert(t->_idOther == nil); + +#if __APPLE__ + // why does this test fail with gnu runtime? + Method idRetainSetter = class_getInstanceMethod(testClass, @selector(setIdRetain:)); + Method idOtherSetter = class_getInstanceMethod(testClass, @selector(setIdOther:)); + method_setImplementation(idRetainSetter, method_getImplementation(idOtherSetter)); + + id testValue2 = [Test new]; + t.idRetain = testValue2; + assert(t->idRetain == testValue); + assert(t->_idOther == testValue2); +#endif // __APPLE__ + return 0; +#endif // __has_feature(objc_property_clean_abi) +} diff --git a/Test/ProtocolCreation.m b/Test/ProtocolCreation.m index 204021d..e115015 100644 --- a/Test/ProtocolCreation.m +++ b/Test/ProtocolCreation.m @@ -11,8 +11,8 @@ int main(void) protocol_addMethodDescription(p, @selector(someMethod), "@:", YES, NO); assert(objc_getProtocol("Test2")); protocol_addProtocol(p, objc_getProtocol("Test2")); - objc_property_attribute_t attrs[] = { {"T", "@" } }; - protocol_addProperty(p, "foo", attrs, 1, YES, YES); + objc_property_attribute_t attrs[] = { {"T", "@" }, {"V", "foo"} }; + protocol_addProperty(p, "foo", attrs, 2, YES, YES); objc_registerProtocol(p); Protocol *p1 = objc_getProtocol("Test"); assert(p == p1); diff --git a/properties.m b/properties.m index f26a2a4..36930a9 100644 --- a/properties.m +++ b/properties.m @@ -14,6 +14,11 @@ PRIVATE int spinlocks[spinlock_count]; +static inline BOOL checkAttribute(char field, int attr) +{ + return (field & attr) == attr; +} + /** * Public function for getting a property. */ @@ -291,12 +296,28 @@ objc_property_t* class_copyPropertyList(Class cls, unsigned int *outCount) } return list; } +static const char* property_getIVar(objc_property_t property) { + const char *iVar = property_getAttributes(property); + if (iVar != 0) + { + while ((*iVar != 0) && (*iVar != 'V')) + { + iVar++; + } + if (*iVar == 'V') + { + return iVar+1; + } + } + return 0; +} const char *property_getName(objc_property_t property) { if (NULL == property) { return NULL; } const char *name = property->name; + if (NULL == name) { return NULL; } if (name[0] == 0) { name += name[1]; @@ -344,39 +365,37 @@ PRIVATE const char *constructPropertyAttributes(objc_property_t property, // Encoding is T{type},V{name}, so 4 bytes for the "T,V" that we always // need. We also need two bytes for the leading null and the length. size_t encodingSize = typeSize + nameSize + 6; - char flags[16]; + char flags[20]; size_t i = 0; // Flags that are a comma then a character - if ((property->attributes & OBJC_PR_readonly) == OBJC_PR_readonly) + if (checkAttribute(property->attributes, OBJC_PR_readonly)) { - if (i > 0) - { - flags[i++] = ','; - } + flags[i++] = ','; flags[i++] = 'R'; } - if ((property->attributes & OBJC_PR_copy) == OBJC_PR_copy) + if (checkAttribute(property->attributes, OBJC_PR_retain)) { - if (i > 0) - { - flags[i++] = ','; - } + flags[i++] = ','; + flags[i++] = '&'; + } + if (checkAttribute(property->attributes, OBJC_PR_copy)) + { + flags[i++] = ','; flags[i++] = 'C'; } - if ((property->attributes & OBJC_PR_retain) == OBJC_PR_retain) + if (checkAttribute(property->attributes2, OBJC_PR_weak)) { - if (i > 0) - { - flags[i++] = ','; - } - flags[i++] = '&'; + flags[i++] = ','; + flags[i++] = 'W'; + } + if (checkAttribute(property->attributes2, OBJC_PR_dynamic)) + { + flags[i++] = ','; + flags[i++] = 'D'; } if ((property->attributes & OBJC_PR_nonatomic) == OBJC_PR_nonatomic) { - if (i > 0) - { - flags[i++] = ','; - } + flags[i++] = ','; flags[i++] = 'N'; } encodingSize += i; @@ -438,20 +457,20 @@ PRIVATE const char *constructPropertyAttributes(objc_property_t property, memcpy(insert, property->setter_name, setterLength); insert += setterLength; } - if (needsComma) - { - *(insert++) = ','; - } - *(insert++) = 'V'; // If the instance variable name is the same as the property name, then we // use the same string for both, otherwise we write the ivar name in the // attributes string and then a null and then the name. if (NULL != iVarName) { + if (needsComma) + { + *(insert++) = ','; + } + *(insert++) = 'V'; memcpy(insert, iVarName, iVarNameSize); insert += iVarNameSize; - *(insert++) = '\0'; } + *(insert++) = '\0'; encoding[1] = (unsigned char)(uintptr_t)(insert - encoding); memcpy(insert, name, nameSize); insert += nameSize; @@ -479,11 +498,12 @@ const char *property_getAttributes(objc_property_t property) return constructPropertyAttributes(property, NULL); } + objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount) { if (NULL == property) { return NULL; } - objc_property_attribute_t attrs[10]; + objc_property_attribute_t attrs[12]; int count = 0; const char *types = property_getTypeEncoding(property); @@ -493,18 +513,38 @@ objc_property_attribute_t *property_copyAttributeList(objc_property_t property, attrs[count].value = types; count++; } - if ((property->attributes & OBJC_PR_copy) == OBJC_PR_copy) + if (checkAttribute(property->attributes, OBJC_PR_readonly)) + { + attrs[count].name = "R"; + attrs[count].value = ""; + count++; + } + if (checkAttribute(property->attributes, OBJC_PR_copy)) { attrs[count].name = "C"; attrs[count].value = ""; count++; } - if ((property->attributes & OBJC_PR_retain) == OBJC_PR_retain) + if (checkAttribute(property->attributes, OBJC_PR_retain) || + checkAttribute(property->attributes2, OBJC_PR_strong)) { attrs[count].name = "&"; attrs[count].value = ""; count++; } + if (checkAttribute(property->attributes2, OBJC_PR_dynamic) && + !checkAttribute(property->attributes2, OBJC_PR_synthesized)) + { + attrs[count].name = "D"; + attrs[count].value = ""; + count++; + } + if (checkAttribute(property->attributes2, OBJC_PR_weak)) + { + attrs[count].name = "W"; + attrs[count].value = ""; + count++; + } if ((property->attributes & OBJC_PR_nonatomic) == OBJC_PR_nonatomic) { attrs[count].name = "N"; @@ -523,9 +563,13 @@ objc_property_attribute_t *property_copyAttributeList(objc_property_t property, attrs[count].value = property->setter_name; count++; } - attrs[count].name = "V"; - attrs[count].value = property_getName(property); - count++; + const char *name = property_getIVar(property); + if (name != NULL) + { + attrs[count].name = "V"; + attrs[count].value = name; + count++; + } objc_property_attribute_t *propAttrs = calloc(sizeof(objc_property_attribute_t), count); memcpy(propAttrs, attrs, count * sizeof(objc_property_attribute_t)); @@ -573,14 +617,32 @@ PRIVATE struct objc_property propertyFromAttrs(const objc_property_attribute_t * case 'C': { p.attributes |= OBJC_PR_copy; + break; + } + case 'R': + { + p.attributes |= OBJC_PR_readonly; + break; + } + case 'W': + { + p.attributes2 |= OBJC_PR_weak; + break; } case '&': { p.attributes |= OBJC_PR_retain; + break; } case 'N': { p.attributes |= OBJC_PR_nonatomic; + break; + } + case 'D': + { + p.attributes2 |= OBJC_PR_dynamic; + break; } } } @@ -597,15 +659,8 @@ BOOL class_addProperty(Class cls, struct objc_property p = propertyFromAttrs(attributes, attributeCount, &iVarname); // If the iVar name is not the same as the name, then we need to construct // the attributes string now, otherwise we can construct it lazily. - if (iVarname) - { - p.name = name; - constructPropertyAttributes(&p, iVarname); - } - else - { - p.name = strdup(name); - } + p.name = name; + constructPropertyAttributes(&p, iVarname); struct objc_property_list *l = calloc(1, sizeof(struct objc_property_list) + sizeof(struct objc_property)); @@ -629,15 +684,11 @@ void class_replaceProperty(Class cls, class_addProperty(cls, name, attributes, attributeCount); return; } - const char *iVarname = name; + const char *iVarname = 0; struct objc_property p = propertyFromAttrs(attributes, attributeCount, &iVarname); + p.name = name; LOCK_RUNTIME_FOR_SCOPE(); - // If the iVar name is not the same as the name, then we need to construct - // the attributes string now, otherwise we can construct it lazily. - if (iVarname != name) - { - constructPropertyAttributes(&p, iVarname); - } + constructPropertyAttributes(&p, iVarname); memcpy(old, &p, sizeof(struct objc_property)); } char *property_copyAttributeValue(objc_property_t property, @@ -651,9 +702,14 @@ char *property_copyAttributeValue(objc_property_t property, const char *types = property_getTypeEncoding(property); return (NULL == types) ? NULL : strdup(types); } + case 'D': + { + return checkAttribute(property->attributes2, OBJC_PR_dynamic) && + !checkAttribute(property->attributes2, OBJC_PR_synthesized) ? strdup("") : 0; + } case 'V': { - return strdup(property_getName(property)); + return strdup(property_getIVar(property)); } case 'S': { @@ -663,17 +719,26 @@ char *property_copyAttributeValue(objc_property_t property, { return strdup(property->getter_name); } + case 'R': + { + return checkAttribute(property->attributes, OBJC_PR_readonly) ? strdup("") : 0; + } + case 'W': + { + return checkAttribute(property->attributes2, OBJC_PR_weak) ? strdup("") : 0; + } case 'C': { - return ((property->attributes |= OBJC_PR_copy) == OBJC_PR_copy) ? strdup("") : 0; + return checkAttribute(property->attributes, OBJC_PR_copy) ? strdup("") : 0; } case '&': { - return ((property->attributes |= OBJC_PR_retain) == OBJC_PR_retain) ? strdup("") : 0; + return checkAttribute(property->attributes, OBJC_PR_retain) || + checkAttribute(property->attributes2, OBJC_PR_strong) ? strdup("") : 0; } case 'N': { - return ((property->attributes |= OBJC_PR_nonatomic) == OBJC_PR_nonatomic) ? strdup("") : 0; + return checkAttribute(property->attributes, OBJC_PR_nonatomic) ? strdup("") : 0; } } return 0; diff --git a/protocol.c b/protocol.c index 987463b..63e5c92 100644 --- a/protocol.c +++ b/protocol.c @@ -409,7 +409,7 @@ objc_property_t protocol_getProperty(Protocol *protocol, for (int i=0 ; icount ; i++) { objc_property_t prop = &properties->properties[i]; - if (strcmp(prop->name, name) == 0) + if (strcmp(property_getName(prop), name) == 0) { return prop; } @@ -610,11 +610,8 @@ void protocol_addProperty(Protocol *aProtocol, int index = list->count-1; const char *iVarName = NULL; struct objc_property p = propertyFromAttrs(attributes, attributeCount, &iVarName); - if (iVarName) - { - constructPropertyAttributes(&p, iVarName); - } - p.name = strdup(name); + p.name = name; + constructPropertyAttributes(&p, iVarName); memcpy(&(list->properties[index]), &p, sizeof(p)); }