Index: src/OFIntrospection.h ================================================================== --- src/OFIntrospection.h +++ src/OFIntrospection.h @@ -17,10 +17,23 @@ #import "OFObject.h" @class OFString; @class OFArray; @class OFMutableArray; + +enum { + OF_PROPERTY_READONLY = 0x01, + OF_PROPERTY_ASSIGN = 0x04, + OF_PROPERTY_READWRITE = 0x08, + OF_PROPERTY_RETAIN = 0x10, + OF_PROPERTY_COPY = 0x20, + OF_PROPERTY_NONATOMIC = 0x40, + OF_PROPERTY_SYNTHESIZED = 0x100, + OF_PROPERTY_DYNAMIC = 0x200, + OF_PROPERTY_ATOMIC = 0x400, + OF_PROPERTY_WEAK = 0x800 +}; /*! * @brief A class for describing a method. */ @interface OFMethod: OFObject @@ -56,10 +69,69 @@ * @return The type encoding for the method */ - (const char*)typeEncoding; @end +/*! + * @brief A class for describing a property. + */ +@interface OFProperty: OFObject +{ + OFString *_name; + unsigned _attributes; + OFString *_getter, *_setter; +} + +#ifdef OF_HAVE_PROPERTIES +@property (readonly, copy) OFString *name; +@property (readonly) unsigned attributes; +@property (readonly, copy) OFString *getter, *setter; +#endif + +/*! + * @brief Returns the name of the property. + * + * @return The name of the property + */ +- (OFString*)name; + +/*! + * @brief Returns the attributes of the property. + * + * The attributes are a bitmask with the following possible flags:@n + * Flag | Description + * ------------------------------|------------------------------------- + * OF_PROPERTY_READONLY | The property is declared `readonly` + * OF_PROPERTY_READWRITE | The property is declared `readwrite` + * OF_PROPERTY_ASSIGN | The property is declared `assign` + * OF_PROPERTY_RETAIN | The property is declared `retain` + * OF_PROPERTY_COPY | The property is declared `copy` + * OF_PROPERTY_NONATOMIC | The property is declared `nonatomic` + * OF_PROPERTY_ATOMIC | The property is declared `atomic` + * OF_PROPERTY_WEAK | The property is declared `weak` + * OF_PROPERTY_SYNTHESIZED | The property is synthesized + * OF_PROPERTY_DYNAMIC | The property is dynamic + * + * @return The attributes of the property + */ +- (unsigned)attributes; + +/*! + * @brief Returns the name of the getter. + * + * @return The name of the getter + */ +- (OFString*)getter; + +/*! + * @brief Returns the name of the setter. + * + * @return The name of the setter + */ +- (OFString*)setter; +@end + /*! * @brief A class for describing an instance variable. */ @interface OFInstanceVariable: OFObject { @@ -101,19 +173,18 @@ */ @interface OFIntrospection: OFObject { OFMutableArray *_classMethods; OFMutableArray *_instanceMethods; - OFMutableArray *_instanceVariables; -#ifdef OF_HAVE_PROPERTIES OFMutableArray *_properties; -#endif + OFMutableArray *_instanceVariables; } #ifdef OF_HAVE_PROPERTIES @property (readonly, copy) OFArray *classMethods; @property (readonly, copy) OFArray *instanceMethods; +@property (readonly, copy) OFArray *properties; @property (readonly, copy) OFArray *instanceVariables; #endif /*! * @brief Creates a new introspection for the specified class. @@ -131,25 +202,49 @@ - initWithClass: (Class)class_; /*! * @brief Returns the class methods of the class. * - * @return An array of OFMethods + * @return An array of objects of class @ref OFMethod */ - (OFArray*)classMethods; /*! * @brief Returns the instance methods of the class. * - * @return An array of OFMethods + * @return An array of objects of class @ref OFMethod */ - (OFArray*)instanceMethods; +/*! + * @brief Returns the properties of the class. + * + * @warning **Do not rely on this, as this behaves differently depending on the + * compiler and ABI used!** + * + * @warning For the ObjFW ABI, Clang only emits data for property introspection + * if `@``synthesize` or `@``dynamic` has been used on the property, + * not if the property has only been implemented by methods. Using + * `@``synthesize` and manually implementing the methods works, + * though. + * + * @warning For the Apple ABI, Clang and GCC both emit data for property + * introspection for every property that has been declared using + * `@``property`, even if no `@``synchronize` or `@``dynamic` has been + * used. + * + * @warning GCC does not emit any data for property introspection for the GNU + * ABI. + * + * @return An array of objects of class @ref OFProperty + */ +- (OFArray*)properties; + /*! * @brief Returns the instance variables of the class. * - * @return An array of OFInstanceVariables + * @return An array of objects of class @ref OFInstanceVariable */ - (OFArray*)instanceVariables; /* TODO: protocols */ @end Index: src/OFIntrospection.m ================================================================== --- src/OFIntrospection.m +++ src/OFIntrospection.m @@ -15,18 +15,21 @@ */ #include "config.h" #include +#include #if defined(OF_APPLE_RUNTIME) # import #endif #import "OFIntrospection.h" #import "OFString.h" #import "OFArray.h" + +#import "OFInitializationFailedException.h" #import "autorelease.h" #import "macros.h" @implementation OFMethod @@ -62,10 +65,12 @@ @throw e; } return self; } +#else +# error Invalid ObjC runtime! #endif - init { OF_INVALID_INIT_METHOD @@ -93,12 +98,12 @@ return _typeEncoding; } - (OFString*)description { - return [OFString stringWithFormat: @"", - _name, _typeEncoding]; + return [OFString stringWithFormat: @"<%@: %@ [%s]>", + [self class], _name, _typeEncoding]; } - (bool)isEqual: (id)object { OFMethod *method; @@ -144,10 +149,252 @@ OF_HASH_FINALIZE(hash); return hash; } @end + +@implementation OFProperty +#if defined(OF_OBJFW_RUNTIME) +- (instancetype)OF_initWithProperty: (struct objc_property*)property +{ + self = [super init]; + + @try { + _name = [[OFString alloc] initWithUTF8String: property->name]; + _attributes = + property->attributes | (property->extended_attributes << 8); + + if (property->getter.name != NULL) + _getter = [[OFString alloc] + initWithUTF8String: property->getter.name]; + if (property->setter.name != NULL) + _setter = [[OFString alloc] + initWithUTF8String: property->setter.name]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} +#elif defined(OF_APPLE_RUNTIME) +- (instancetype)OF_initWithProperty: (objc_property_t)property +{ + self = [super init]; + + @try { + const char *attributes; + + _name = [[OFString alloc] + initWithUTF8String: property_getName(property)]; + + if ((attributes = property_getAttributes(property)) == NULL) + @throw [OFInitializationFailedException + exceptionWithClass: [self class]]; + + while (*attributes != '\0') { + const char *start; + + switch (*attributes) { + case 'R': + _attributes |= OF_PROPERTY_READONLY; + attributes++; + break; + case 'C': + _attributes |= OF_PROPERTY_COPY; + attributes++; + break; + case '&': + _attributes |= OF_PROPERTY_RETAIN; + attributes++; + break; + case 'N': + _attributes |= OF_PROPERTY_NONATOMIC; + attributes++; + break; + case 'G': + start = ++attributes; + + if (_getter != nil) + @throw [OFInitializationFailedException + exceptionWithClass: [self class]]; + + while (*attributes != ',' && + *attributes != '\0') + attributes++; + + _getter = [[OFString alloc] + initWithUTF8String: start + length: attributes - start]; + + break; + case 'S': + start = ++attributes; + + if (_setter != nil) + @throw [OFInitializationFailedException + exceptionWithClass: [self class]]; + + while (*attributes != ',' && + *attributes != '\0') + attributes++; + + _setter = [[OFString alloc] + initWithUTF8String: start + length: attributes - start]; + + break; + case 'D': + _attributes |= OF_PROPERTY_DYNAMIC; + attributes++; + break; + case 'W': + _attributes |= OF_PROPERTY_WEAK; + attributes++; + break; + case 'P': + attributes++; + break; + case 'T': + case 't': + while (*attributes != ',' && + *attributes != '\0') + attributes++; + break; + default: + @throw [OFInitializationFailedException + exceptionWithClass: [self class]]; + } + + if (*attributes != ',' && *attributes != '\0') + @throw [OFInitializationFailedException + exceptionWithClass: [self class]]; + + if (*attributes != '\0') + attributes++; + } + + if (!(_attributes & OF_PROPERTY_READONLY)) + _attributes |= OF_PROPERTY_READWRITE; + + if (!(_attributes & OF_PROPERTY_COPY) && + !(_attributes & OF_PROPERTY_RETAIN)) + _attributes |= OF_PROPERTY_ASSIGN; + + if (!(_attributes & OF_PROPERTY_NONATOMIC)) + _attributes |= OF_PROPERTY_ATOMIC; + + if (!(_attributes & OF_PROPERTY_DYNAMIC)) + _attributes |= OF_PROPERTY_SYNTHESIZED; + + if (_getter == nil) + _getter = [_name copy]; + + if ((_attributes & OF_PROPERTY_READWRITE) && _setter == nil) { + of_unichar_t first = [_name characterAtIndex: 0]; + OFMutableString *tmp = [_name mutableCopy]; + _setter = tmp; + + if (first < 0x80) { + [tmp setCharacter: toupper((int)first) + atIndex: 0]; + } + + [tmp prependString: @"set"]; + + [tmp makeImmutable]; + } + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} +#else +# error Invalid ObjC runtime! +#endif + +- (void)dealloc +{ + [_name release]; + [_getter release]; + [_setter release]; + + [super dealloc]; +} + +- (OFString*)name +{ + OF_GETTER(_name, true) +} + +- (unsigned)attributes +{ + return _attributes; +} + +- (OFString*)getter +{ + OF_GETTER(_getter, true) +} + +- (OFString*)setter +{ + OF_GETTER(_setter, true) +} + +- (OFString*)description +{ + return [OFString + stringWithFormat: @"<%@: %@\n" + @"\tAttributes = 0x%03X\n" + @"\tGetter = %@\n" + @"\tSetter = %@\n" + @">", + [self class], _name, _attributes, + _getter, _setter]; +} + +- (bool)isEqual: (id)object +{ + OFProperty *otherProperty; + + if ([object isKindOfClass: [OFProperty class]]) + return false; + + otherProperty = object; + + if (![otherProperty->_name isEqual: _name]) + return false; + if (otherProperty->_attributes != _attributes) + return false; + if (![otherProperty->_getter isEqual: _getter]) + return false; + if (![otherProperty->_setter isEqual: _setter]) + return false; + + return true; +} + +- (uint32_t)hash +{ + uint32_t hash; + + OF_HASH_INIT(hash); + + OF_HASH_ADD_HASH(hash, [_name hash]); + OF_HASH_ADD(hash, (_attributes & 0xFF00) >> 8); + OF_HASH_ADD(hash, _attributes & 0xFF); + OF_HASH_ADD_HASH(hash, [_getter hash]); + OF_HASH_ADD_HASH(hash, [_setter hash]); + + OF_HASH_FINALIZE(hash); + + return hash; +} +@end @implementation OFInstanceVariable #if defined(OF_OBJFW_RUNTIME) - (instancetype)OF_initWithIvar: (struct objc_ivar*)ivar { @@ -179,10 +426,12 @@ @throw e; } return self; } +#else +# error Invalid ObjC runtime! #endif - init { OF_INVALID_INIT_METHOD @@ -229,107 +478,137 @@ self = [super init]; @try { #if defined(OF_OBJFW_RUNTIME) struct objc_method_list *methodList; + struct objc_property_list *propertyList; #elif defined(OF_APPLE_RUNTIME) Method *methodList; + objc_property_t *propertyList; Ivar *ivarList; - unsigned i, count; + unsigned count; #endif + unsigned i; + void *pool; _classMethods = [[OFMutableArray alloc] init]; _instanceMethods = [[OFMutableArray alloc] init]; + _properties = [[OFMutableArray alloc] init]; _instanceVariables = [[OFMutableArray alloc] init]; #if defined(OF_OBJFW_RUNTIME) for (methodList = object_getClass(class)->methodlist; methodList != NULL; methodList = methodList->next) { - int i; - - for (i = 0; i < methodList->count; i++) { - void *pool = objc_autoreleasePoolPush(); - OFMethod *method = [[OFMethod alloc] - OF_initWithMethod: &methodList->methods[i]]; - [_classMethods addObject: [method autorelease]]; - objc_autoreleasePoolPop(pool); - } + pool = objc_autoreleasePoolPush(); + + for (i = 0; i < methodList->count; i++) + [_classMethods addObject: [[[OFMethod alloc] + OF_initWithMethod: + &methodList->methods[i]] autorelease]]; + + objc_autoreleasePoolPop(pool); } for (methodList = class->methodlist; methodList != NULL; methodList = methodList->next) { - int i; - - for (i = 0; i < methodList->count; i++) { - void *pool = objc_autoreleasePoolPush(); - OFMethod *method = [[OFMethod alloc] - OF_initWithMethod: &methodList->methods[i]]; - [_instanceMethods addObject: - [method autorelease]]; - objc_autoreleasePoolPop(pool); - } + pool = objc_autoreleasePoolPush(); + + for (i = 0; i < methodList->count; i++) + [_instanceMethods addObject: [[[OFMethod alloc] + OF_initWithMethod: + &methodList->methods[i]] autorelease]]; + + objc_autoreleasePoolPop(pool); + } + + for (propertyList = class->properties; propertyList != NULL; + propertyList = propertyList->next) { + pool = objc_autoreleasePoolPush(); + + for (i = 0; i < propertyList->count; i++) + [_properties addObject: [[[OFProperty alloc] + OF_initWithProperty: + &propertyList->properties[i]] autorelease]]; + + objc_autoreleasePoolPop(pool); } if (class->ivars != NULL) { - unsigned i; - - for (i = 0; i < class->ivars->count; i++) { - void *pool = objc_autoreleasePoolPush(); - OFInstanceVariable *ivar; - - ivar = [[OFInstanceVariable alloc] - OF_initWithIvar: &class->ivars->ivars[i]]; + pool = objc_autoreleasePoolPush(); + + for (i = 0; i < class->ivars->count; i++) [_instanceVariables addObject: - [ivar autorelease]]; + [[[OFInstanceVariable alloc] + OF_initWithIvar: + &class->ivars->ivars[i]] autorelease]]; - objc_autoreleasePoolPop(pool); - } + objc_autoreleasePoolPop(pool); } #elif defined(OF_APPLE_RUNTIME) methodList = class_copyMethodList(object_getClass(class), &count); @try { - for (i = 0; i < count; i++) { - void *pool = objc_autoreleasePoolPush(); + pool = objc_autoreleasePoolPush(); + + for (i = 0; i < count; i++) [_classMethods addObject: [[[OFMethod alloc] OF_initWithMethod: methodList[i]] autorelease]]; - objc_autoreleasePoolPop(pool); - } + + objc_autoreleasePoolPop(pool); } @finally { free(methodList); } methodList = class_copyMethodList(class, &count); @try { - for (i = 0; i < count; i++) { - void *pool = objc_autoreleasePoolPush(); + pool = objc_autoreleasePoolPush(); + + for (i = 0; i < count; i++) [_instanceMethods addObject: [[[OFMethod alloc] OF_initWithMethod: methodList[i]] autorelease]]; - objc_autoreleasePoolPop(pool); - } + + objc_autoreleasePoolPop(pool); } @finally { free(methodList); } + + propertyList = class_copyPropertyList(class, &count); + @try { + pool = objc_autoreleasePoolPush(); + + for (i = 0; i < count; i++) + [_properties addObject: [[[OFProperty alloc] + OF_initWithProperty: propertyList[i]] + autorelease]]; + + objc_autoreleasePoolPop(pool); + } @finally { + free(propertyList); + } ivarList = class_copyIvarList(class, &count); @try { - for (i = 0; i < count; i++) { - void *pool = objc_autoreleasePoolPush(); + pool = objc_autoreleasePoolPush(); + + for (i = 0; i < count; i++) [_instanceVariables addObject: [[[OFInstanceVariable alloc] OF_initWithIvar: ivarList[i]] autorelease]]; - objc_autoreleasePoolPop(pool); - } + + objc_autoreleasePoolPop(pool); } @finally { free(ivarList); } +#else +# error Invalid ObjC runtime! #endif [_classMethods makeImmutable]; [_instanceMethods makeImmutable]; + [_properties makeImmutable]; [_instanceVariables makeImmutable]; } @catch (id e) { [self release]; @throw e; } @@ -358,11 +637,16 @@ - (OFArray*)instanceMethods { OF_GETTER(_instanceMethods, true) } + +- (OFArray*)properties +{ + OF_GETTER(_properties, true) +} - (OFArray*)instanceVariables { OF_GETTER(_instanceVariables, true) } @end Index: src/runtime/runtime.h ================================================================== --- src/runtime/runtime.h +++ src/runtime/runtime.h @@ -133,15 +133,24 @@ OBJC_PROPERTY_RETAIN = 0x10, OBJC_PROPERTY_COPY = 0x20, OBJC_PROPERTY_NONATOMIC = 0x40, OBJC_PROPERTY_SETTER = 0x80 }; + +enum objc_property_extended_attributes { + OBJC_PROPERTY_SYNTHESIZE = 0x1, + OBJC_PROPERTY_DYNAMIC = 0x2, + OBJC_PROPERTY_PROTOCOL = 0x3, + OBJC_PROPERTY_ATOMIC = 0x4, + OBJC_PROPERTY_WEAK = 0x8, + OBJC_PROPERTY_STRONG = 0x10, + OBJC_PROPERTY_UNSAFE_UNRETAINED = 0x20 +}; struct objc_property { const char *name; - unsigned char attributes; - BOOL synthesized; + unsigned char attributes, extended_attributes; struct { const char *name; const char *type; } getter, setter; };