123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- //
- // FLEXProperty.m
- // FLEX
- //
- // Derived from MirrorKit.
- // Created by Tanner on 6/30/15.
- // Copyright (c) 2020 FLEX Team. All rights reserved.
- //
- #import "FLEXProperty.h"
- #import "FLEXPropertyAttributes.h"
- #import "FLEXMethodBase.h"
- #import "FLEXRuntimeUtility.h"
- #include <dlfcn.h>
- @interface FLEXProperty () {
- NSString *_flex_description;
- }
- @property (nonatomic ) BOOL uniqueCheckFlag;
- @property (nonatomic, readonly) Class cls;
- @end
- @implementation FLEXProperty
- @synthesize multiple = _multiple;
- @synthesize imageName = _imageName;
- @synthesize imagePath = _imagePath;
- #pragma mark Initializers
- - (id)init {
- [NSException
- raise:NSInternalInconsistencyException
- format:@"Class instance should not be created with -init"
- ];
- return nil;
- }
- + (instancetype)property:(objc_property_t)property {
- return [[self alloc] initWithProperty:property onClass:nil];
- }
- + (instancetype)property:(objc_property_t)property onClass:(Class)cls {
- return [[self alloc] initWithProperty:property onClass:cls];
- }
- + (instancetype)named:(NSString *)name onClass:(Class)cls {
- objc_property_t _Nullable property = class_getProperty(cls, name.UTF8String);
- NSAssert(property, @"Cannot find property with name %@ on class %@", name, cls);
- return [self property:property onClass:cls];
- }
- + (instancetype)propertyWithName:(NSString *)name attributes:(FLEXPropertyAttributes *)attributes {
- return [[self alloc] initWithName:name attributes:attributes];
- }
- - (id)initWithProperty:(objc_property_t)property onClass:(Class)cls {
- NSParameterAssert(property);
-
- self = [super init];
- if (self) {
- _objc_property = property;
- _attributes = [FLEXPropertyAttributes attributesForProperty:property];
- _name = @(property_getName(property) ?: "(nil)");
- _cls = cls;
-
- if (!_attributes) [NSException raise:NSInternalInconsistencyException format:@"Error retrieving property attributes"];
- if (!_name) [NSException raise:NSInternalInconsistencyException format:@"Error retrieving property name"];
-
- [self examine];
- }
-
- return self;
- }
- - (id)initWithName:(NSString *)name attributes:(FLEXPropertyAttributes *)attributes {
- NSParameterAssert(name); NSParameterAssert(attributes);
-
- self = [super init];
- if (self) {
- _attributes = attributes;
- _name = name;
-
- [self examine];
- }
-
- return self;
- }
- #pragma mark Private
- - (void)examine {
- if (self.attributes.typeEncoding.length) {
- _type = (FLEXTypeEncoding)[self.attributes.typeEncoding characterAtIndex:0];
- }
- // Return the given selector if the class responds to it
- Class cls = _cls;
- SEL (^selectorIfValid)() = ^SEL(SEL sel) {
- if (!sel || !cls) return nil;
- return [cls instancesRespondToSelector:sel] ? sel : nil;
- };
- SEL customGetter = self.attributes.customGetter;
- SEL customSetter = self.attributes.customSetter;
- SEL defaultGetter = NSSelectorFromString(self.name);
- SEL defaultSetter = NSSelectorFromString([NSString
- stringWithFormat:@"set%c%@:",
- (char)toupper([self.name characterAtIndex:0]),
- [self.name substringFromIndex:1]
- ]);
- // Check if the likely getters/setters exist
- SEL validGetter = selectorIfValid(customGetter) ?: selectorIfValid(defaultGetter);
- SEL validSetter = selectorIfValid(customSetter) ?: selectorIfValid(defaultSetter);
- _likelyGetterExists = validGetter != nil;
- _likelySetterExists = validSetter != nil;
- // Assign likely getters and setters to the valid one,
- // or the default, regardless of whether the default exists
- _likelyGetter = validGetter ?: defaultGetter;
- _likelySetter = validSetter ?: defaultSetter;
- _likelyGetterString = NSStringFromSelector(_likelyGetter);
- _likelySetterString = NSStringFromSelector(_likelySetter);
- _isClassProperty = _cls ? class_isMetaClass(_cls) : NO;
- }
- #pragma mark Overrides
- - (NSString *)description {
- if (!_flex_description) {
- NSString *readableType = [FLEXRuntimeUtility readableTypeForEncoding:self.attributes.typeEncoding];
- _flex_description = [FLEXRuntimeUtility appendName:self.name toType:readableType];
- }
- return _flex_description;
- }
- - (NSString *)debugDescription {
- return [NSString stringWithFormat:@"<%@ name=%@, property=%p, attributes:\n\t%@\n>",
- NSStringFromClass(self.class), self.name, self.objc_property, self.attributes];
- }
- #pragma mark Public
- - (objc_property_attribute_t *)copyAttributesList:(unsigned int *)attributesCount {
- if (self.objc_property) {
- return property_copyAttributeList(self.objc_property, attributesCount);
- } else {
- return [self.attributes copyAttributesList:attributesCount];
- }
- }
- - (void)replacePropertyOnClass:(Class)cls {
- class_replaceProperty(cls, self.name.UTF8String, self.attributes.list, (unsigned int)self.attributes.count);
- }
- - (void)computeSymbolInfo:(BOOL)forceBundle {
- Dl_info exeInfo;
- if (dladdr(_objc_property, &exeInfo)) {
- _imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : nil;
- }
-
- if ((!_multiple || !_uniqueCheckFlag) && _cls) {
- _multiple = _objc_property != class_getProperty(_cls, self.name.UTF8String);
- if (_multiple || forceBundle) {
- NSString *path = _imagePath.stringByDeletingLastPathComponent;
- _imageName = [NSBundle bundleWithPath:path].executablePath.lastPathComponent;
- }
- }
- }
- - (BOOL)multiple {
- [self computeSymbolInfo:NO];
- return _multiple;
- }
- - (NSString *)imagePath {
- [self computeSymbolInfo:YES];
- return _imagePath;
- }
- - (NSString *)imageName {
- [self computeSymbolInfo:YES];
- return _imageName;
- }
- - (NSString *)fullDescription {
- NSMutableArray<NSString *> *attributesStrings = [NSMutableArray new];
- FLEXPropertyAttributes *attributes = self.attributes;
- // Atomicity
- if (attributes.isNonatomic) {
- [attributesStrings addObject:@"nonatomic"];
- } else {
- [attributesStrings addObject:@"atomic"];
- }
- // Storage
- if (attributes.isRetained) {
- [attributesStrings addObject:@"strong"];
- } else if (attributes.isCopy) {
- [attributesStrings addObject:@"copy"];
- } else if (attributes.isWeak) {
- [attributesStrings addObject:@"weak"];
- } else {
- [attributesStrings addObject:@"assign"];
- }
- // Mutability
- if (attributes.isReadOnly) {
- [attributesStrings addObject:@"readonly"];
- } else {
- [attributesStrings addObject:@"readwrite"];
- }
-
- // Class or not
- if (self.isClassProperty) {
- [attributesStrings addObject:@"class"];
- }
- // Custom getter/setter
- SEL customGetter = attributes.customGetter;
- SEL customSetter = attributes.customSetter;
- if (customGetter) {
- [attributesStrings addObject:[NSString stringWithFormat:@"getter=%s", sel_getName(customGetter)]];
- }
- if (customSetter) {
- [attributesStrings addObject:[NSString stringWithFormat:@"setter=%s", sel_getName(customSetter)]];
- }
- NSString *attributesString = [attributesStrings componentsJoinedByString:@", "];
- return [NSString stringWithFormat:@"@property (%@) %@", attributesString, self.description];
- }
- - (id)getValue:(id)target {
- if (!target) return nil;
-
- // We don't care about checking dynamically whether the getter
- // _now_ exists on this object. If the getter doesn't exist
- // when this property is initialized, it will never call it.
- // Just re-create the property object if you need to call it.
- if (self.likelyGetterExists) {
- BOOL objectIsClass = object_isClass(target);
- BOOL instanceAndInstanceProperty = !objectIsClass && !self.isClassProperty;
- BOOL classAndClassProperty = objectIsClass && self.isClassProperty;
- if (instanceAndInstanceProperty || classAndClassProperty) {
- return [FLEXRuntimeUtility performSelector:self.likelyGetter onObject:target];
- }
- }
- return nil;
- }
- - (id)getPotentiallyUnboxedValue:(id)target {
- if (!target) return nil;
- return [FLEXRuntimeUtility
- potentiallyUnwrapBoxedPointer:[self getValue:target]
- type:self.attributes.typeEncoding.UTF8String
- ];
- }
- #pragma mark Suggested getters and setters
- - (FLEXMethodBase *)getterWithImplementation:(IMP)implementation {
- NSString *types = [NSString stringWithFormat:@"%@%s%s", self.attributes.typeEncoding, @encode(id), @encode(SEL)];
- NSString *name = [NSString stringWithFormat:@"%@", self.name];
- FLEXMethodBase *getter = [FLEXMethodBase buildMethodNamed:name withTypes:types implementation:implementation];
- return getter;
- }
- - (FLEXMethodBase *)setterWithImplementation:(IMP)implementation {
- NSString *types = [NSString stringWithFormat:@"%s%s%s%@", @encode(void), @encode(id), @encode(SEL), self.attributes.typeEncoding];
- NSString *name = [NSString stringWithFormat:@"set%@:", self.name.capitalizedString];
- FLEXMethodBase *setter = [FLEXMethodBase buildMethodNamed:name withTypes:types implementation:implementation];
- return setter;
- }
- @end
|