added files via upload

This commit is contained in:
Balackburn
2023-06-27 09:54:41 +02:00
commit 2ff6aac218
1420 changed files with 88898 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
//
// FLEXBlockDescription.h
// FLEX
//
// Created by Oliver Letterer on 2012-09-01
// Forked from CTObjectiveCRuntimeAdditions (MIT License)
// https://github.com/ebf/CTObjectiveCRuntimeAdditions
//
// Copyright (c) 2020 FLEX Team-EDV Beratung Föllmer GmbH
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
typedef NS_OPTIONS(NSUInteger, FLEXBlockOptions) {
FLEXBlockOptionHasCopyDispose = (1 << 25),
FLEXBlockOptionHasCtor = (1 << 26), // helpers have C++ code
FLEXBlockOptionIsGlobal = (1 << 28),
FLEXBlockOptionHasStret = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
FLEXBlockOptionHasSignature = (1 << 30),
};
NS_ASSUME_NONNULL_BEGIN
#pragma mark -
@interface FLEXBlockDescription : NSObject
+ (instancetype)describing:(id)block;
@property (nonatomic, readonly, nullable) NSMethodSignature *signature;
@property (nonatomic, readonly, nullable) NSString *signatureString;
@property (nonatomic, readonly, nullable) NSString *sourceDeclaration;
@property (nonatomic, readonly) FLEXBlockOptions flags;
@property (nonatomic, readonly) NSUInteger size;
@property (nonatomic, readonly) NSString *summary;
@property (nonatomic, readonly) id block;
- (BOOL)isCompatibleForBlockSwizzlingWithMethodSignature:(NSMethodSignature *)methodSignature;
@end
#pragma mark -
@interface NSBlock : NSObject
- (void)invoke;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,157 @@
//
// FLEXBlockDescription.m
// FLEX
//
// Created by Oliver Letterer on 2012-09-01
// Forked from CTObjectiveCRuntimeAdditions (MIT License)
// https://github.com/ebf/CTObjectiveCRuntimeAdditions
//
// Copyright (c) 2020 FLEX Team-EDV Beratung Föllmer GmbH
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "FLEXBlockDescription.h"
#import "FLEXRuntimeUtility.h"
struct block_object {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct block_descriptor {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
// optional helper functions
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};
@implementation FLEXBlockDescription
+ (instancetype)describing:(id)block {
return [[self alloc] initWithObjcBlock:block];
}
- (id)initWithObjcBlock:(id)block {
self = [super init];
if (self) {
_block = block;
struct block_object *blockRef = (__bridge struct block_object *)block;
_flags = blockRef->flags;
_size = blockRef->descriptor->size;
if (_flags & FLEXBlockOptionHasSignature) {
void *signatureLocation = blockRef->descriptor;
signatureLocation += sizeof(unsigned long int);
signatureLocation += sizeof(unsigned long int);
if (_flags & FLEXBlockOptionHasCopyDispose) {
signatureLocation += sizeof(void(*)(void *dst, void *src));
signatureLocation += sizeof(void (*)(void *src));
}
const char *signature = (*(const char **)signatureLocation);
_signatureString = @(signature);
@try {
_signature = [NSMethodSignature signatureWithObjCTypes:signature];
} @catch (NSException *exception) { }
}
NSMutableString *summary = [NSMutableString stringWithFormat:
@"Type signature: %@\nSize: %@\nIs global: %@\nHas constructor: %@\nIs stret: %@",
self.signatureString ?: @"nil", @(self.size),
@((BOOL)(_flags & FLEXBlockOptionIsGlobal)),
@((BOOL)(_flags & FLEXBlockOptionHasCtor)),
@((BOOL)(_flags & FLEXBlockOptionHasStret))
];
if (!self.signature) {
[summary appendFormat:@"\nNumber of arguments: %@", @(self.signature.numberOfArguments)];
}
_summary = summary.copy;
_sourceDeclaration = [self buildLikelyDeclaration];
}
return self;
}
- (BOOL)isCompatibleForBlockSwizzlingWithMethodSignature:(NSMethodSignature *)methodSignature {
if (!self.signature) {
return NO;
}
if (self.signature.numberOfArguments != methodSignature.numberOfArguments + 1) {
return NO;
}
if (strcmp(self.signature.methodReturnType, methodSignature.methodReturnType) != 0) {
return NO;
}
for (int i = 0; i < methodSignature.numberOfArguments; i++) {
if (i == 1) {
// SEL in method, IMP in block
if (strcmp([methodSignature getArgumentTypeAtIndex:i], ":") != 0) {
return NO;
}
if (strcmp([self.signature getArgumentTypeAtIndex:i + 1], "^?") != 0) {
return NO;
}
} else {
if (strcmp([self.signature getArgumentTypeAtIndex:i], [self.signature getArgumentTypeAtIndex:i + 1]) != 0) {
return NO;
}
}
}
return YES;
}
- (NSString *)buildLikelyDeclaration {
NSMethodSignature *signature = self.signature;
NSUInteger numberOfArguments = signature.numberOfArguments;
const char *returnType = signature.methodReturnType;
// Return type
NSMutableString *decl = [NSMutableString stringWithString:@"^"];
if (returnType[0] != FLEXTypeEncodingVoid) {
[decl appendString:[FLEXRuntimeUtility readableTypeForEncoding:@(returnType)]];
[decl appendString:@" "];
}
// Arguments
if (numberOfArguments) {
[decl appendString:@"("];
for (NSUInteger i = 1; i < numberOfArguments; i++) {
const char *argType = [self.signature getArgumentTypeAtIndex:i] ?: "?";
NSString *readableArgType = [FLEXRuntimeUtility readableTypeForEncoding:@(argType)];
[decl appendFormat:@"%@ arg%@, ", readableArgType, @(i)];
}
[decl deleteCharactersInRange:NSMakeRange(decl.length-2, 2)];
[decl appendString:@")"];
}
return decl.copy;
}
@end

View File

@@ -0,0 +1,80 @@
//
// FLEXClassBuilder.h
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 7/3/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import <Foundation/Foundation.h>
@class FLEXIvarBuilder, FLEXMethodBase, FLEXProperty, FLEXProtocol;
#pragma mark FLEXClassBuilder
@interface FLEXClassBuilder : NSObject
@property (nonatomic, readonly) Class workingClass;
/// Begins constructing a class with the given name.
///
/// This new class will implicitly inherits from \c NSObject with \c 0 extra bytes.
/// Classes created this way must be registered with \c -registerClass before being used.
+ (instancetype)allocateClass:(NSString *)name;
/// Begins constructing a class with the given name and superclass.
/// @discussion Calls \c -allocateClass:superclass:extraBytes: with \c 0 extra bytes.
/// Classes created this way must be registered with \c -registerClass before being used.
+ (instancetype)allocateClass:(NSString *)name superclass:(Class)superclass;
/// Begins constructing a new class object with the given name and superclass.
/// @discussion Pass \c nil to \e superclass to create a new root class.
/// Classes created this way must be registered with \c -registerClass before being used.
+ (instancetype)allocateClass:(NSString *)name superclass:(Class)superclass extraBytes:(size_t)bytes;
/// Begins constructing a new root class object with the given name and \c 0 extra bytes.
/// @discussion Classes created this way must be registered with \c -registerClass before being used.
+ (instancetype)allocateRootClass:(NSString *)name;
/// Use this to modify existing classes. @warning You cannot add instance variables to existing classes.
+ (instancetype)builderForClass:(Class)cls;
/// @return Any methods that failed to be added.
- (NSArray<FLEXMethodBase *> *)addMethods:(NSArray<FLEXMethodBase *> *)methods;
/// @return Any properties that failed to be added.
- (NSArray<FLEXProperty *> *)addProperties:(NSArray<FLEXProperty *> *)properties;
/// @return Any protocols that failed to be added.
- (NSArray<FLEXProtocol *> *)addProtocols:(NSArray<FLEXProtocol *> *)protocols;
/// @warning Adding Ivars to existing classes is not supported and will always fail.
- (NSArray<FLEXIvarBuilder *> *)addIvars:(NSArray<FLEXIvarBuilder *> *)ivars;
/// Finalizes construction of a new class.
/// @discussion Once a class is registered, instance variables cannot be added.
/// @note Raises an exception if called on a previously registered class.
- (Class)registerClass;
/// Uses \c objc_lookupClass to determine if the working class is registered.
@property (nonatomic, readonly) BOOL isRegistered;
@end
#pragma mark FLEXIvarBuilder
@interface FLEXIvarBuilder : NSObject
/// Consider using the \c FLEXIvarBuilderWithNameAndType() macro below.
/// @param name The name of the Ivar, such as \c \@"_value".
/// @param size The size of the Ivar. Usually \c sizeof(type). For objects, this is \c sizeof(id).
/// @param alignment The alignment of the Ivar. Usually \c log2(sizeof(type)).
/// @param encoding The type encoding of the Ivar. For objects, this is \c \@(\@encode(id)), and for others it is \c \@(\@encode(type)).
+ (instancetype)name:(NSString *)name size:(size_t)size alignment:(uint8_t)alignment typeEncoding:(NSString *)encoding;
@property (nonatomic, readonly) NSString *name;
@property (nonatomic, readonly) NSString *encoding;
@property (nonatomic, readonly) size_t size;
@property (nonatomic, readonly) uint8_t alignment;
@end
#define FLEXIvarBuilderWithNameAndType(nameString, type) [FLEXIvarBuilder \
name:nameString \
size:sizeof(type) \
alignment:log2(sizeof(type)) \
typeEncoding:@(@encode(type)) \
]

View File

@@ -0,0 +1,168 @@
//
// FLEXClassBuilder.m
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 7/3/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import "FLEXClassBuilder.h"
#import "FLEXProperty.h"
#import "FLEXMethodBase.h"
#import "FLEXProtocol.h"
#import <objc/runtime.h>
#pragma mark FLEXClassBuilder
@interface FLEXClassBuilder ()
@property (nonatomic) NSString *name;
@end
@implementation FLEXClassBuilder
- (id)init {
[NSException
raise:NSInternalInconsistencyException
format:@"Class instance should not be created with -init"
];
return nil;
}
#pragma mark Initializers
+ (instancetype)allocateClass:(NSString *)name {
return [self allocateClass:name superclass:NSObject.class];
}
+ (instancetype)allocateClass:(NSString *)name superclass:(Class)superclass {
return [self allocateClass:name superclass:superclass extraBytes:0];
}
+ (instancetype)allocateClass:(NSString *)name superclass:(Class)superclass extraBytes:(size_t)bytes {
NSParameterAssert(name);
return [[self alloc] initWithClass:objc_allocateClassPair(superclass, name.UTF8String, bytes)];
}
+ (instancetype)allocateRootClass:(NSString *)name {
NSParameterAssert(name);
return [[self alloc] initWithClass:objc_allocateClassPair(Nil, name.UTF8String, 0)];
}
+ (instancetype)builderForClass:(Class)cls {
return [[self alloc] initWithClass:cls];
}
- (id)initWithClass:(Class)cls {
NSParameterAssert(cls);
self = [super init];
if (self) {
_workingClass = cls;
_name = NSStringFromClass(_workingClass);
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@ name=%@, registered=%d>",
NSStringFromClass(self.class), self.name, self.isRegistered];
}
#pragma mark Building
- (NSArray *)addMethods:(NSArray *)methods {
NSParameterAssert(methods.count);
NSMutableArray *failed = [NSMutableArray new];
for (FLEXMethodBase *m in methods) {
if (!class_addMethod(self.workingClass, m.selector, m.implementation, m.typeEncoding.UTF8String)) {
[failed addObject:m];
}
}
return failed;
}
- (NSArray *)addProperties:(NSArray *)properties {
NSParameterAssert(properties.count);
NSMutableArray *failed = [NSMutableArray new];
for (FLEXProperty *p in properties) {
unsigned int pcount;
objc_property_attribute_t *attributes = [p copyAttributesList:&pcount];
if (!class_addProperty(self.workingClass, p.name.UTF8String, attributes, pcount)) {
[failed addObject:p];
}
free(attributes);
}
return failed;
}
- (NSArray *)addProtocols:(NSArray *)protocols {
NSParameterAssert(protocols.count);
NSMutableArray *failed = [NSMutableArray new];
for (FLEXProtocol *p in protocols) {
if (!class_addProtocol(self.workingClass, p.objc_protocol)) {
[failed addObject:p];
}
}
return failed;
}
- (NSArray *)addIvars:(NSArray *)ivars {
NSParameterAssert(ivars.count);
NSMutableArray *failed = [NSMutableArray new];
for (FLEXIvarBuilder *ivar in ivars) {
if (!class_addIvar(self.workingClass, ivar.name.UTF8String, ivar.size, ivar.alignment, ivar.encoding.UTF8String)) {
[failed addObject:ivar];
}
}
return failed;
}
- (Class)registerClass {
if (self.isRegistered) {
[NSException raise:NSInternalInconsistencyException format:@"Class is already registered"];
}
objc_registerClassPair(self.workingClass);
return self.workingClass;
}
- (BOOL)isRegistered {
return objc_lookUpClass(self.name.UTF8String) != nil;
}
@end
#pragma mark FLEXIvarBuilder
@implementation FLEXIvarBuilder
+ (instancetype)name:(NSString *)name size:(size_t)size alignment:(uint8_t)alignment typeEncoding:(NSString *)encoding {
return [[self alloc] initWithName:name size:size alignment:alignment typeEncoding:encoding];
}
- (id)initWithName:(NSString *)name size:(size_t)size alignment:(uint8_t)alignment typeEncoding:(NSString *)encoding {
NSParameterAssert(name); NSParameterAssert(encoding);
NSParameterAssert(size > 0); NSParameterAssert(alignment > 0);
self = [super init];
if (self) {
_name = name;
_encoding = encoding;
_size = size;
_alignment = alignment;
}
return self;
}
@end

View File

@@ -0,0 +1,51 @@
//
// FLEXIvar.h
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 6/30/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import "FLEXRuntimeConstants.h"
NS_ASSUME_NONNULL_BEGIN
@interface FLEXIvar : NSObject
+ (instancetype)ivar:(Ivar)ivar;
+ (instancetype)named:(NSString *)name onClass:(Class)cls;
/// The underlying \c Ivar data structure.
@property (nonatomic, readonly) Ivar objc_ivar;
/// The name of the instance variable.
@property (nonatomic, readonly) NSString *name;
/// The type of the instance variable.
@property (nonatomic, readonly) FLEXTypeEncoding type;
/// The type encoding string of the instance variable.
@property (nonatomic, readonly) NSString *typeEncoding;
/// The offset of the instance variable.
@property (nonatomic, readonly) NSInteger offset;
/// The size of the instance variable. 0 if unknown.
@property (nonatomic, readonly) NSUInteger size;
/// Describes the type encoding, size, offset, and objc_ivar
@property (nonatomic, readonly) NSString *details;
/// The full path of the image that contains this ivar definition,
/// or \c nil if this ivar was probably defined at runtime.
@property (nonatomic, readonly, nullable) NSString *imagePath;
/// For internal use
@property (nonatomic) id tag;
- (nullable id)getValue:(id)target;
- (void)setValue:(nullable id)value onObject:(id)target;
/// Calls into -getValue: and passes that value into
/// -[FLEXRuntimeUtility potentiallyUnwrapBoxedPointer:type:]
/// and returns the result
- (nullable id)getPotentiallyUnboxedValue:(id)target;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,158 @@
//
// FLEXIvar.m
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 6/30/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import "FLEXIvar.h"
#import "FLEXRuntimeUtility.h"
#import "FLEXRuntimeSafety.h"
#import "FLEXTypeEncodingParser.h"
#import "NSString+FLEX.h"
#include "FLEXObjcInternal.h"
#include <dlfcn.h>
@interface FLEXIvar () {
NSString *_flex_description;
}
@end
@implementation FLEXIvar
#pragma mark Initializers
+ (instancetype)ivar:(Ivar)ivar {
return [[self alloc] initWithIvar:ivar];
}
+ (instancetype)named:(NSString *)name onClass:(Class)cls {
Ivar _Nullable ivar = class_getInstanceVariable(cls, name.UTF8String);
NSAssert(ivar, @"Cannot find ivar with name %@ on class %@", name, cls);
return [self ivar:ivar];
}
- (id)initWithIvar:(Ivar)ivar {
NSParameterAssert(ivar);
self = [super init];
if (self) {
_objc_ivar = ivar;
[self examine];
}
return self;
}
#pragma mark Other
- (NSString *)description {
if (!_flex_description) {
NSString *readableType = [FLEXRuntimeUtility readableTypeForEncoding:self.typeEncoding];
_flex_description = [FLEXRuntimeUtility appendName:self.name toType:readableType];
}
return _flex_description;
}
- (NSString *)debugDescription {
return [NSString stringWithFormat:@"<%@ name=%@, encoding=%@, offset=%ld>",
NSStringFromClass(self.class), self.name, self.typeEncoding, (long)self.offset];
}
- (void)examine {
_name = @(ivar_getName(self.objc_ivar) ?: "(nil)");
_offset = ivar_getOffset(self.objc_ivar);
_typeEncoding = @(ivar_getTypeEncoding(self.objc_ivar) ?: "");
NSString *typeForDetails = _typeEncoding;
NSString *sizeForDetails = nil;
if (_typeEncoding.length) {
_type = (FLEXTypeEncoding)[_typeEncoding characterAtIndex:0];
FLEXGetSizeAndAlignment(_typeEncoding.UTF8String, &_size, nil);
sizeForDetails = [@(_size).stringValue stringByAppendingString:@" bytes"];
} else {
_type = FLEXTypeEncodingNull;
typeForDetails = @"no type info";
sizeForDetails = @"unknown size";
}
Dl_info exeInfo;
if (dladdr(_objc_ivar, &exeInfo)) {
_imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : nil;
}
_details = [NSString stringWithFormat:
@"%@, offset %@ — %@",
sizeForDetails, @(_offset), typeForDetails
];
}
- (id)getValue:(id)target {
id value = nil;
if (!FLEXIvarIsSafe(_objc_ivar) ||
_type == FLEXTypeEncodingNull ||
FLEXPointerIsTaggedPointer(target)) {
return nil;
}
#ifdef __arm64__
// See http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html
if (self.type == FLEXTypeEncodingObjcClass && [self.name isEqualToString:@"isa"]) {
value = object_getClass(target);
} else
#endif
if (self.type == FLEXTypeEncodingObjcObject || self.type == FLEXTypeEncodingObjcClass) {
value = object_getIvar(target, self.objc_ivar);
} else {
void *pointer = (__bridge void *)target + self.offset;
value = [FLEXRuntimeUtility
valueForPrimitivePointer:pointer
objCType:self.typeEncoding.UTF8String
];
}
return value;
}
- (void)setValue:(id)value onObject:(id)target {
const char *typeEncodingCString = self.typeEncoding.UTF8String;
if (self.type == FLEXTypeEncodingObjcObject) {
object_setIvar(target, self.objc_ivar, value);
} else if ([value isKindOfClass:[NSValue class]]) {
// Primitive - unbox the NSValue.
NSValue *valueValue = (NSValue *)value;
// Make sure that the box contained the correct type.
NSAssert(
strcmp(valueValue.objCType, typeEncodingCString) == 0,
@"Type encoding mismatch (value: %s; ivar: %s) in setting ivar named: %@ on object: %@",
valueValue.objCType, typeEncodingCString, self.name, target
);
NSUInteger bufferSize = 0;
if (FLEXGetSizeAndAlignment(typeEncodingCString, &bufferSize, NULL)) {
void *buffer = calloc(bufferSize, 1);
[valueValue getValue:buffer];
void *pointer = (__bridge void *)target + self.offset;
memcpy(pointer, buffer, bufferSize);
free(buffer);
}
}
}
- (id)getPotentiallyUnboxedValue:(id)target {
NSString *type = self.typeEncoding;
if (type.flex_typeIsNonObjcPointer && type.flex_pointeeType != FLEXTypeEncodingVoid) {
return [self getValue:target];
}
return [FLEXRuntimeUtility
potentiallyUnwrapBoxedPointer:[self getValue:target]
type:type.UTF8String
];
}
@end

View File

@@ -0,0 +1,96 @@
//
// FLEXMethod.h
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 6/30/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import "FLEXRuntimeConstants.h"
#import "FLEXMethodBase.h"
NS_ASSUME_NONNULL_BEGIN
/// A class representing a concrete method which already exists in a class.
/// This class contains helper methods for swizzling or invoking the method.
///
/// Any of the initializers will return nil if the type encoding
/// of the method is unsupported by `NSMethodSignature`. In general,
/// any method whose return type or parameters involve a struct with
/// bitfields or arrays is unsupported.
///
/// I do not remember why I didn't include \c signature in the base class
/// when I originally wrote this, but I probably had a good reason. We can
/// always go back and move it to \c FLEXMethodBase if we find we need to.
@interface FLEXMethod : FLEXMethodBase
/// Defaults to instance method
+ (nullable instancetype)method:(Method)method;
+ (nullable instancetype)method:(Method)method isInstanceMethod:(BOOL)isInstanceMethod;
/// Constructs an \c FLEXMethod for the given method on the given class.
/// @param cls the class, or metaclass if this is a class method
/// @return The newly constructed \c FLEXMethod object, or \c nil if the
/// specified class or its superclasses do not contain a method with the specified selector.
+ (nullable instancetype)selector:(SEL)selector class:(Class)cls;
/// Constructs an \c FLEXMethod for the given method on the given class,
/// only if the given class itself defines or overrides the desired method.
/// @param cls the class, or metaclass if this is a class method
/// @return The newly constructed \c FLEXMethod object, or \c nil \e if the
/// specified class does not define or override, or if the specified class
/// or its superclasses do not contain, a method with the specified selector.
+ (nullable instancetype)selector:(SEL)selector implementedInClass:(Class)cls;
@property (nonatomic, readonly) Method objc_method;
/// The implementation of the method.
/// @discussion Setting \c implementation will change the implementation of this method
/// for the entire class which implements said method. It will also not modify the selector of said method.
@property (nonatomic ) IMP implementation;
/// Whether the method is an instance method or not.
@property (nonatomic, readonly) BOOL isInstanceMethod;
/// The number of arguments to the method.
@property (nonatomic, readonly) NSUInteger numberOfArguments;
/// The \c NSMethodSignature object corresponding to the method's type encoding.
@property (nonatomic, readonly) NSMethodSignature *signature;
/// Same as \e typeEncoding but with parameter sizes up front and offsets after the types.
@property (nonatomic, readonly) NSString *signatureString;
/// The return type of the method.
@property (nonatomic, readonly) FLEXTypeEncoding *returnType;
/// The return size of the method.
@property (nonatomic, readonly) NSUInteger returnSize;
/// The full path of the image that contains this method definition,
/// or \c nil if this ivar was probably defined at runtime.
@property (nonatomic, readonly) NSString *imagePath;
/// Like @code - (void)foo:(int)bar @endcode
@property (nonatomic, readonly) NSString *description;
/// Like @code -[Class foo:] @endcode
- (NSString *)debugNameGivenClassName:(NSString *)name;
/// Swizzles the recieving method with the given method.
- (void)swapImplementations:(FLEXMethod *)method;
#define FLEXMagicNumber 0xdeadbeef
#define FLEXArg(expr) FLEXMagicNumber,/// @encode(__typeof__(expr)), (__typeof__(expr) []){ expr }
/// Sends a message to \e target, and returns it's value, or \c nil if not applicable.
/// @discussion You may send any message with this method. Primitive return values will be wrapped
/// in instances of \c NSNumber and \c NSValue. \c void and bitfield returning methods return \c nil.
/// \c SEL return types are converted to strings using \c NSStringFromSelector.
/// @return The object returned by this method, or an instance of \c NSValue or \c NSNumber containing
/// the primitive return type, or a string for \c SEL return types.
- (id)sendMessage:(id)target, ...;
/// Used internally by \c sendMessage:target,. Pass \c NULL to the first parameter for void methods.
- (void)getReturnValue:(void *)retPtr forMessageSend:(id)target, ...;
@end
@interface FLEXMethod (Comparison)
- (NSComparisonResult)compare:(FLEXMethod *)method;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,430 @@
//
// FLEXMethod.m
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 6/30/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import "FLEXMethod.h"
#import "FLEXMirror.h"
#import "FLEXTypeEncodingParser.h"
#import "FLEXRuntimeUtility.h"
#include <dlfcn.h>
@implementation FLEXMethod
@synthesize imagePath = _imagePath;
@dynamic implementation;
+ (instancetype)buildMethodNamed:(NSString *)name withTypes:(NSString *)typeEncoding implementation:(IMP)implementation {
[NSException raise:NSInternalInconsistencyException format:@"Class instance should not be created with +buildMethodNamed:withTypes:implementation"]; return nil;
}
- (id)init {
[NSException
raise:NSInternalInconsistencyException
format:@"Class instance should not be created with -init"
];
return nil;
}
#pragma mark Initializers
+ (instancetype)method:(Method)method {
return [[self alloc] initWithMethod:method isInstanceMethod:YES];
}
+ (instancetype)method:(Method)method isInstanceMethod:(BOOL)isInstanceMethod {
return [[self alloc] initWithMethod:method isInstanceMethod:isInstanceMethod];
}
+ (instancetype)selector:(SEL)selector class:(Class)cls {
BOOL instance = !class_isMetaClass(cls);
// class_getInstanceMethod will return an instance method if not given
// not given a metaclass, or a class method if given a metaclass, but
// this isn't documented so we just want to be safe here.
Method m = instance ? class_getInstanceMethod(cls, selector) : class_getClassMethod(cls, selector);
if (m == NULL) return nil;
return [self method:m isInstanceMethod:instance];
}
+ (instancetype)selector:(SEL)selector implementedInClass:(Class)cls {
if (![cls superclass]) { return [self selector:selector class:cls]; }
BOOL unique = [cls methodForSelector:selector] != [[cls superclass] methodForSelector:selector];
if (unique) {
return [self selector:selector class:cls];
}
return nil;
}
- (id)initWithMethod:(Method)method isInstanceMethod:(BOOL)isInstanceMethod {
NSParameterAssert(method);
self = [super init];
if (self) {
_objc_method = method;
_isInstanceMethod = isInstanceMethod;
_signatureString = @(method_getTypeEncoding(method) ?: "?@:");
NSString *cleanSig = nil;
if ([FLEXTypeEncodingParser methodTypeEncodingSupported:_signatureString cleaned:&cleanSig]) {
_signature = [NSMethodSignature signatureWithObjCTypes:cleanSig.UTF8String];
}
[self examine];
}
return self;
}
#pragma mark Other
- (NSString *)description {
if (!_flex_description) {
_flex_description = [self prettyName];
}
return _flex_description;
}
- (NSString *)debugNameGivenClassName:(NSString *)name {
NSMutableString *string = [NSMutableString stringWithString:_isInstanceMethod ? @"-[" : @"+["];
[string appendString:name];
[string appendString:@" "];
[string appendString:self.selectorString];
[string appendString:@"]"];
return string;
}
- (NSString *)prettyName {
NSString *methodTypeString = self.isInstanceMethod ? @"-" : @"+";
NSString *readableReturnType = [FLEXRuntimeUtility readableTypeForEncoding:@(self.signature.methodReturnType ?: "")];
NSString *prettyName = [NSString stringWithFormat:@"%@ (%@)", methodTypeString, readableReturnType];
NSArray *components = [self prettyArgumentComponents];
if (components.count) {
return [prettyName stringByAppendingString:[components componentsJoinedByString:@" "]];
} else {
return [prettyName stringByAppendingString:self.selectorString];
}
}
- (NSArray *)prettyArgumentComponents {
// NSMethodSignature can't handle some type encodings
// like ^AI@:ir* which happen to very much exist
if (self.signature.numberOfArguments < self.numberOfArguments) {
return nil;
}
NSMutableArray *components = [NSMutableArray new];
NSArray *selectorComponents = [self.selectorString componentsSeparatedByString:@":"];
NSUInteger numberOfArguments = self.numberOfArguments;
for (NSUInteger argIndex = 2; argIndex < numberOfArguments; argIndex++) {
assert(argIndex < self.signature.numberOfArguments);
const char *argType = [self.signature getArgumentTypeAtIndex:argIndex] ?: "?";
NSString *readableArgType = [FLEXRuntimeUtility readableTypeForEncoding:@(argType)];
NSString *prettyComponent = [NSString
stringWithFormat:@"%@:(%@) ",
selectorComponents[argIndex - 2],
readableArgType
];
[components addObject:prettyComponent];
}
return components;
}
- (NSString *)debugDescription {
return [NSString stringWithFormat:@"<%@ selector=%@, signature=%@>",
NSStringFromClass(self.class), self.selectorString, self.signatureString];
}
- (void)examine {
_implementation = method_getImplementation(_objc_method);
_selector = method_getName(_objc_method);
_numberOfArguments = method_getNumberOfArguments(_objc_method);
_name = NSStringFromSelector(_selector);
_returnType = (FLEXTypeEncoding *)_signature.methodReturnType ?: "";
_returnSize = _signature.methodReturnLength;
}
#pragma mark Public
- (void)setImplementation:(IMP)implementation {
NSParameterAssert(implementation);
method_setImplementation(self.objc_method, implementation);
[self examine];
}
- (NSString *)typeEncoding {
if (!_typeEncoding) {
_typeEncoding = [_signatureString
stringByReplacingOccurrencesOfString:@"[0-9]"
withString:@""
options:NSRegularExpressionSearch
range:NSMakeRange(0, _signatureString.length)
];
}
return _typeEncoding;
}
- (NSString *)imagePath {
if (!_imagePath) {
Dl_info exeInfo;
if (dladdr(_implementation, &exeInfo)) {
_imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : @"";
}
}
return _imagePath;
}
#pragma mark Misc
- (void)swapImplementations:(FLEXMethod *)method {
method_exchangeImplementations(self.objc_method, method.objc_method);
[self examine];
[method examine];
}
// Some code borrowed from MAObjcRuntime, by Mike Ash.
- (id)sendMessage:(id)target, ... {
id ret = nil;
va_list args;
va_start(args, target);
switch (self.returnType[0]) {
case FLEXTypeEncodingUnknown: {
[self getReturnValue:NULL forMessageSend:target arguments:args];
break;
}
case FLEXTypeEncodingChar: {
char val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingInt: {
int val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingShort: {
short val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingLong: {
long val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingLongLong: {
long long val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingUnsignedChar: {
unsigned char val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingUnsignedInt: {
unsigned int val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingUnsignedShort: {
unsigned short val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingUnsignedLong: {
unsigned long val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingUnsignedLongLong: {
unsigned long long val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingFloat: {
float val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingDouble: {
double val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingLongDouble: {
long double val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = [NSValue value:&val withObjCType:self.returnType];
break;
}
case FLEXTypeEncodingCBool: {
bool val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingVoid: {
[self getReturnValue:NULL forMessageSend:target arguments:args];
return nil;
break;
}
case FLEXTypeEncodingCString: {
char *val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = @(val);
break;
}
case FLEXTypeEncodingObjcObject: {
id val = nil;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = val;
break;
}
case FLEXTypeEncodingObjcClass: {
Class val = Nil;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = val;
break;
}
case FLEXTypeEncodingSelector: {
SEL val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = NSStringFromSelector(val);
break;
}
case FLEXTypeEncodingArrayBegin: {
void *val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = [NSValue valueWithBytes:val objCType:self.signature.methodReturnType];
break;
}
case FLEXTypeEncodingUnionBegin:
case FLEXTypeEncodingStructBegin: {
if (self.signature.methodReturnLength) {
void * val = malloc(self.signature.methodReturnLength);
[self getReturnValue:val forMessageSend:target arguments:args];
ret = [NSValue valueWithBytes:val objCType:self.signature.methodReturnType];
} else {
[self getReturnValue:NULL forMessageSend:target arguments:args];
}
break;
}
case FLEXTypeEncodingBitField: {
[self getReturnValue:NULL forMessageSend:target arguments:args];
break;
}
case FLEXTypeEncodingPointer: {
void * val = 0;
[self getReturnValue:&val forMessageSend:target arguments:args];
ret = [NSValue valueWithPointer:val];
break;
}
default: {
[NSException raise:NSInvalidArgumentException
format:@"Unsupported type encoding: %s", (char *)self.returnType];
}
}
va_end(args);
return ret;
}
// Code borrowed from MAObjcRuntime, by Mike Ash.
- (void)getReturnValue:(void *)retPtr forMessageSend:(id)target, ... {
va_list args;
va_start(args, target);
[self getReturnValue:retPtr forMessageSend:target arguments:args];
va_end(args);
}
// Code borrowed from MAObjcRuntime, by Mike Ash.
- (void)getReturnValue:(void *)retPtr forMessageSend:(id)target arguments:(va_list)args {
if (!_signature) {
return;
}
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:_signature];
NSUInteger argumentCount = _signature.numberOfArguments;
invocation.target = target;
for (NSUInteger i = 2; i < argumentCount; i++) {
int cookie = va_arg(args, int);
if (cookie != FLEXMagicNumber) {
[NSException
raise:NSInternalInconsistencyException
format:@"%s: incorrect magic cookie %08x; make sure you didn't forget "
"any arguments and that all arguments are wrapped in FLEXArg().", __func__, cookie
];
}
const char *typeString = va_arg(args, char *);
void *argPointer = va_arg(args, void *);
NSUInteger inSize, sigSize;
NSGetSizeAndAlignment(typeString, &inSize, NULL);
NSGetSizeAndAlignment([_signature getArgumentTypeAtIndex:i], &sigSize, NULL);
if (inSize != sigSize) {
[NSException
raise:NSInternalInconsistencyException
format:@"%s:size mismatch between passed-in argument and "
"required argument; in type:%s (%lu) requested:%s (%lu)",
__func__, typeString, (long)inSize, [_signature getArgumentTypeAtIndex:i], (long)sigSize
];
}
[invocation setArgument:argPointer atIndex:i];
}
// Hack to make NSInvocation invoke the desired implementation
IMP imp = [invocation methodForSelector:NSSelectorFromString(@"invokeUsingIMP:")];
void (*invokeWithIMP)(id, SEL, IMP) = (void *)imp;
invokeWithIMP(invocation, 0, _implementation);
if (_signature.methodReturnLength && retPtr) {
[invocation getReturnValue:retPtr];
}
}
@end
@implementation FLEXMethod (Comparison)
- (NSComparisonResult)compare:(FLEXMethod *)method {
return [self.selectorString compare:method.selectorString];
}
@end

View File

@@ -0,0 +1,43 @@
//
// FLEXMethodBase.h
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 7/5/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import <Foundation/Foundation.h>
/// A base class for methods which encompasses those that may not
/// have been added to a class yet. Useful on it's own for adding
/// methods to a class, or building a new class from the ground up.
@interface FLEXMethodBase : NSObject {
@protected
SEL _selector;
NSString *_name;
NSString *_typeEncoding;
IMP _implementation;
NSString *_flex_description;
}
/// Constructs and returns an \c FLEXSimpleMethod instance with the given name, type encoding, and implementation.
+ (instancetype)buildMethodNamed:(NSString *)name withTypes:(NSString *)typeEncoding implementation:(IMP)implementation;
/// The selector of the method.
@property (nonatomic, readonly) SEL selector;
/// The selector string of the method.
@property (nonatomic, readonly) NSString *selectorString;
/// Same as selectorString.
@property (nonatomic, readonly) NSString *name;
/// The type encoding of the method.
@property (nonatomic, readonly) NSString *typeEncoding;
/// The implementation of the method.
@property (nonatomic, readonly) IMP implementation;
/// For internal use
@property (nonatomic) id tag;
@end

View File

@@ -0,0 +1,49 @@
//
// FLEXMethodBase.m
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 7/5/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import "FLEXMethodBase.h"
@implementation FLEXMethodBase
#pragma mark Initializers
+ (instancetype)buildMethodNamed:(NSString *)name withTypes:(NSString *)typeEncoding implementation:(IMP)implementation {
return [[self alloc] initWithSelector:sel_registerName(name.UTF8String) types:typeEncoding imp:implementation];
}
- (id)initWithSelector:(SEL)selector types:(NSString *)types imp:(IMP)imp {
NSParameterAssert(selector); NSParameterAssert(types); NSParameterAssert(imp);
self = [super init];
if (self) {
_selector = selector;
_typeEncoding = types;
_implementation = imp;
_name = NSStringFromSelector(self.selector);
}
return self;
}
- (NSString *)selectorString {
return _name;
}
#pragma mark Overrides
- (NSString *)description {
if (!_flex_description) {
_flex_description = [NSString stringWithFormat:@"%@ '%@'", _name, _typeEncoding];
}
return _flex_description;
}
@end

View File

@@ -0,0 +1,97 @@
//
// FLEXMirror.h
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 6/29/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@class FLEXMethod, FLEXProperty, FLEXIvar, FLEXProtocol;
NS_ASSUME_NONNULL_BEGIN
#pragma mark FLEXMirror Protocol
NS_SWIFT_NAME(FLEXMirrorProtocol)
@protocol FLEXMirror <NSObject>
/// Swift initializer
/// @throws If a metaclass object is passed in.
- (instancetype)initWithSubject:(id)objectOrClass NS_SWIFT_NAME(init(reflecting:));
/// The underlying object or \c Class used to create this \c FLEXMirror.
@property (nonatomic, readonly) id value;
/// Whether \c value was a class or a class instance.
@property (nonatomic, readonly) BOOL isClass;
/// The name of the \c Class of the \c value property.
@property (nonatomic, readonly) NSString *className;
@property (nonatomic, readonly) NSArray<FLEXProperty *> *properties;
@property (nonatomic, readonly) NSArray<FLEXProperty *> *classProperties;
@property (nonatomic, readonly) NSArray<FLEXIvar *> *ivars;
@property (nonatomic, readonly) NSArray<FLEXMethod *> *methods;
@property (nonatomic, readonly) NSArray<FLEXMethod *> *classMethods;
@property (nonatomic, readonly) NSArray<FLEXProtocol *> *protocols;
/// Super mirrors are initialized with the class that corresponds to the value passed in.
/// If you passed in an instance of a class, it's superclass is used to create this mirror.
/// If you passed in a class, then that class's superclass is used.
///
/// @note This property should be computed, not cached.
@property (nonatomic, readonly, nullable) id<FLEXMirror> superMirror NS_SWIFT_NAME(superMirror);
@end
#pragma mark FLEXMirror Class
@interface FLEXMirror : NSObject <FLEXMirror>
/// Reflects an instance of an object or \c Class.
/// @discussion \c FLEXMirror will immediately gather all useful information. Consider using the
/// \c NSObject categories provided if your code will only use a few pieces of information,
/// or if your code needs to run faster.
///
/// Regardless of whether you reflect an instance or a class object, \c methods and \c properties
/// will be populated with instance methods and properties, and \c classMethods and \c classProperties
/// will be populated with class methods and properties.
///
/// @param objectOrClass An instance of an objct or a \c Class object.
/// @throws If a metaclass object is passed in.
/// @return An instance of \c FLEXMirror.
+ (instancetype)reflect:(id)objectOrClass;
@property (nonatomic, readonly) id value;
@property (nonatomic, readonly) BOOL isClass;
@property (nonatomic, readonly) NSString *className;
@property (nonatomic, readonly) NSArray<FLEXProperty *> *properties;
@property (nonatomic, readonly) NSArray<FLEXProperty *> *classProperties;
@property (nonatomic, readonly) NSArray<FLEXIvar *> *ivars;
@property (nonatomic, readonly) NSArray<FLEXMethod *> *methods;
@property (nonatomic, readonly) NSArray<FLEXMethod *> *classMethods;
@property (nonatomic, readonly) NSArray<FLEXProtocol *> *protocols;
@property (nonatomic, readonly, nullable) FLEXMirror *superMirror NS_SWIFT_NAME(superMirror);
@end
@interface FLEXMirror (ExtendedMirror)
/// @return The instance method with the given name, or \c nil if one does not exist.
- (nullable FLEXMethod *)methodNamed:(nullable NSString *)name;
/// @return The class method with the given name, or \c nil if one does not exist.
- (nullable FLEXMethod *)classMethodNamed:(nullable NSString *)name;
/// @return The instance property with the given name, or \c nil if one does not exist.
- (nullable FLEXProperty *)propertyNamed:(nullable NSString *)name;
/// @return The class property with the given name, or \c nil if one does not exist.
- (nullable FLEXProperty *)classPropertyNamed:(nullable NSString *)name;
/// @return The instance variable with the given name, or \c nil if one does not exist.
- (nullable FLEXIvar *)ivarNamed:(nullable NSString *)name;
/// @return The protocol with the given name, or \c nil if one does not exist.
- (nullable FLEXProtocol *)protocolNamed:(nullable NSString *)name;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,145 @@
//
// FLEXMirror.m
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 6/29/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import "FLEXMirror.h"
#import "FLEXProperty.h"
#import "FLEXMethod.h"
#import "FLEXIvar.h"
#import "FLEXProtocol.h"
#import "FLEXUtility.h"
#pragma mark FLEXMirror
@implementation FLEXMirror
- (id)init {
[NSException
raise:NSInternalInconsistencyException
format:@"Class instance should not be created with -init"
];
return nil;
}
#pragma mark Initialization
+ (instancetype)reflect:(id)objectOrClass {
return [[self alloc] initWithSubject:objectOrClass];
}
- (id)initWithSubject:(id)objectOrClass {
NSParameterAssert(objectOrClass);
self = [super init];
if (self) {
_value = objectOrClass;
[self examine];
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@ %@=%@>",
NSStringFromClass(self.class),
self.isClass ? @"metaclass" : @"class",
self.className
];
}
- (void)examine {
BOOL isClass = object_isClass(self.value);
Class cls = isClass ? self.value : object_getClass(self.value);
Class meta = object_getClass(cls);
_className = NSStringFromClass(cls);
_isClass = isClass;
unsigned int pcount, cpcount, mcount, cmcount, ivcount, pccount;
Ivar *objcIvars = class_copyIvarList(cls, &ivcount);
Method *objcMethods = class_copyMethodList(cls, &mcount);
Method *objcClsMethods = class_copyMethodList(meta, &cmcount);
objc_property_t *objcProperties = class_copyPropertyList(cls, &pcount);
objc_property_t *objcClsProperties = class_copyPropertyList(meta, &cpcount);
Protocol *__unsafe_unretained *protos = class_copyProtocolList(cls, &pccount);
_ivars = [NSArray flex_forEachUpTo:ivcount map:^id(NSUInteger i) {
return [FLEXIvar ivar:objcIvars[i]];
}];
_methods = [NSArray flex_forEachUpTo:mcount map:^id(NSUInteger i) {
return [FLEXMethod method:objcMethods[i] isInstanceMethod:YES];
}];
_classMethods = [NSArray flex_forEachUpTo:cmcount map:^id(NSUInteger i) {
return [FLEXMethod method:objcClsMethods[i] isInstanceMethod:NO];
}];
_properties = [NSArray flex_forEachUpTo:pcount map:^id(NSUInteger i) {
return [FLEXProperty property:objcProperties[i] onClass:cls];
}];
_classProperties = [NSArray flex_forEachUpTo:cpcount map:^id(NSUInteger i) {
return [FLEXProperty property:objcClsProperties[i] onClass:meta];
}];
_protocols = [NSArray flex_forEachUpTo:pccount map:^id(NSUInteger i) {
return [FLEXProtocol protocol:protos[i]];
}];
// Cleanup
free(objcClsProperties);
free(objcProperties);
free(objcClsMethods);
free(objcMethods);
free(objcIvars);
free(protos);
protos = NULL;
}
#pragma mark Misc
- (FLEXMirror *)superMirror {
Class cls = _isClass ? _value : object_getClass(_value);
return [FLEXMirror reflect:class_getSuperclass(cls)];
}
@end
#pragma mark ExtendedMirror
@implementation FLEXMirror (ExtendedMirror)
- (id)filter:(NSArray *)array forName:(NSString *)name {
NSPredicate *filter = [NSPredicate predicateWithFormat:@"%K = %@", @"name", name];
return [array filteredArrayUsingPredicate:filter].firstObject;
}
- (FLEXMethod *)methodNamed:(NSString *)name {
return [self filter:self.methods forName:name];
}
- (FLEXMethod *)classMethodNamed:(NSString *)name {
return [self filter:self.classMethods forName:name];
}
- (FLEXProperty *)propertyNamed:(NSString *)name {
return [self filter:self.properties forName:name];
}
- (FLEXProperty *)classPropertyNamed:(NSString *)name {
return [self filter:self.classProperties forName:name];
}
- (FLEXIvar *)ivarNamed:(NSString *)name {
return [self filter:self.ivars forName:name];
}
- (FLEXProtocol *)protocolNamed:(NSString *)name {
return [self filter:self.protocols forName:name];
}
@end

View File

@@ -0,0 +1,138 @@
//
// FLEXProperty.h
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 6/30/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import "FLEXRuntimeConstants.h"
@class FLEXPropertyAttributes, FLEXMethodBase;
#pragma mark FLEXProperty
@interface FLEXProperty : NSObject
/// You may use this initializer instead of \c property:onClass: if you don't need
/// to know anything about the uniqueness of this property or where it comes from.
+ (instancetype)property:(objc_property_t)property;
/// This initializer can be used to access additional information
/// in an efficient manner. That information being whether this property
/// is certainly not unique and the name of the binary image which declares it.
/// @param cls the class, or metaclass if this is a class property.
+ (instancetype)property:(objc_property_t)property onClass:(Class)cls;
/// @param cls the class, or metaclass if this is a class property
+ (instancetype)named:(NSString *)name onClass:(Class)cls;
/// Constructs a new property with the given name and attributes.
+ (instancetype)propertyWithName:(NSString *)name attributes:(FLEXPropertyAttributes *)attributes;
/// \c 0 if the instance was created via \c +propertyWithName:attributes,
/// otherwise this is the first property in \c objc_properties
@property (nonatomic, readonly) objc_property_t objc_property;
@property (nonatomic, readonly) objc_property_t *objc_properties;
@property (nonatomic, readonly) NSInteger objc_propertyCount;
@property (nonatomic, readonly) BOOL isClassProperty;
/// The name of the property.
@property (nonatomic, readonly) NSString *name;
/// The type of the property. Get the full type from the attributes.
@property (nonatomic, readonly) FLEXTypeEncoding type;
/// The property's attributes.
@property (nonatomic ) FLEXPropertyAttributes *attributes;
/// The (likely) setter, regardless of whether the property is readonly.
/// For example, this might be the custom setter.
@property (nonatomic, readonly) SEL likelySetter;
@property (nonatomic, readonly) NSString *likelySetterString;
/// Not valid unless initialized with the owning class.
@property (nonatomic, readonly) BOOL likelySetterExists;
/// The (likely) getter. For example, this might be the custom getter.
@property (nonatomic, readonly) SEL likelyGetter;
@property (nonatomic, readonly) NSString *likelyGetterString;
/// Not valid unless initialized with the owning class.
@property (nonatomic, readonly) BOOL likelyGetterExists;
/// Always \c nil for class properties.
@property (nonatomic, readonly) NSString *likelyIvarName;
/// Not valid unless initialized with the owning class.
@property (nonatomic, readonly) BOOL likelyIvarExists;
/// Whether there are certainly multiple definitions of this property,
/// such as in categories in other binary images or something.
/// @return Whether \c objc_property matches the return value of \c class_getProperty,
/// or \c NO if this property was not created with \c property:onClass
@property (nonatomic, readonly) BOOL multiple;
/// @return The bundle of the image that contains this property definition,
/// or \c nil if this property was not created with \c property:onClass or
/// if this property was probably defined at runtime.
@property (nonatomic, readonly) NSString *imageName;
/// The full path of the image that contains this property definition,
/// or \c nil if this property was not created with \c property:onClass or
/// if this property was probably defined at runtime.
@property (nonatomic, readonly) NSString *imagePath;
/// For internal use
@property (nonatomic) id tag;
/// @return The value of this property on \c target as given by \c -valueForKey:
/// A source-like description of the property, with all of its attributes.
@property (nonatomic, readonly) NSString *fullDescription;
/// If this is a class property, you must class the class object.
- (id)getValue:(id)target;
/// Calls into -getValue: and passes that value into
/// -[FLEXRuntimeUtility potentiallyUnwrapBoxedPointer:type:]
/// and returns the result.
///
/// If this is a class property, you must class the class object.
- (id)getPotentiallyUnboxedValue:(id)target;
/// Safe to use regardless of how the \c FLEXProperty instance was initialized.
///
/// This uses \c self.objc_property if it exists, otherwise it uses \c self.attributes
- (objc_property_attribute_t *)copyAttributesList:(unsigned int *)attributesCount;
/// Replace the attributes of the current property in the given class,
/// using the attributes in \c self.attributes
///
/// What happens when the property does not exist is undocumented.
- (void)replacePropertyOnClass:(Class)cls;
#pragma mark Convenience getters and setters
/// @return A getter for the property with the given implementation.
/// @discussion Consider using the \c FLEXPropertyGetter macros.
- (FLEXMethodBase *)getterWithImplementation:(IMP)implementation;
/// @return A setter for the property with the given implementation.
/// @discussion Consider using the \c FLEXPropertySetter macros.
- (FLEXMethodBase *)setterWithImplementation:(IMP)implementation;
#pragma mark FLEXMethod property getter / setter macros
// Easier than using the above methods yourself in most cases
/// Takes a \c FLEXProperty and a type (ie \c NSUInteger or \c id) and
/// uses the \c FLEXProperty's \c attribute's \c backingIvarName to get the Ivar.
#define FLEXPropertyGetter(FLEXProperty, type) [FLEXProperty \
getterWithImplementation:imp_implementationWithBlock(^(id self) { \
return *(type *)[self getIvarAddressByName:FLEXProperty.attributes.backingIvar]; \
}) \
];
/// Takes a \c FLEXProperty and a type (ie \c NSUInteger or \c id) and
/// uses the \c FLEXProperty's \c attribute's \c backingIvarName to set the Ivar.
#define FLEXPropertySetter(FLEXProperty, type) [FLEXProperty \
setterWithImplementation:imp_implementationWithBlock(^(id self, type value) { \
[self setIvarByName:FLEXProperty.attributes.backingIvar value:&value size:sizeof(type)]; \
}) \
];
/// Takes a \c FLEXProperty and a type (ie \c NSUInteger or \c id) and an Ivar name string to get the Ivar.
#define FLEXPropertyGetterWithIvar(FLEXProperty, ivarName, type) [FLEXProperty \
getterWithImplementation:imp_implementationWithBlock(^(id self) { \
return *(type *)[self getIvarAddressByName:ivarName]; \
}) \
];
/// Takes a \c FLEXProperty and a type (ie \c NSUInteger or \c id) and an Ivar name string to set the Ivar.
#define FLEXPropertySetterWithIvar(FLEXProperty, ivarName, type) [FLEXProperty \
setterWithImplementation:imp_implementationWithBlock(^(id self, type value) { \
[self setIvarByName:ivarName value:&value size:sizeof(type)]; \
}) \
];
@end

View File

@@ -0,0 +1,295 @@
//
// 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 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;
_likelyIvarName = _isClassProperty ? nil : (
self.attributes.backingIvar ?: [@"_" stringByAppendingString:_name]
);
}
#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;
}
- (BOOL)likelyIvarExists {
if (_likelyIvarName && _cls) {
return class_getInstanceVariable(_cls, _likelyIvarName.UTF8String) != nil;
}
return NO;
}
- (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

View File

@@ -0,0 +1,110 @@
//
// FLEXPropertyAttributes.h
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 7/5/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
NS_ASSUME_NONNULL_BEGIN
#pragma mark FLEXPropertyAttributes
/// See \e FLEXRuntimeUtilitiy.h for valid string tokens.
/// See this link on how to construct a proper attributes string:
/// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
@interface FLEXPropertyAttributes : NSObject <NSCopying, NSMutableCopying> {
// These are necessary for the mutable subclass to function
@protected
NSUInteger _count;
NSString *_string, *_backingIvar, *_typeEncoding, *_oldTypeEncoding, *_fullDeclaration;
NSDictionary *_dictionary;
objc_property_attribute_t *_list;
SEL _customGetter, _customSetter;
BOOL _isReadOnly, _isCopy, _isRetained, _isNonatomic, _isDynamic, _isWeak, _isGarbageCollectable;
}
+ (instancetype)attributesForProperty:(objc_property_t)property;
/// @warning Raises an exception if \e attributes is invalid, \c nil, or contains unsupported keys.
+ (instancetype)attributesFromDictionary:(NSDictionary *)attributes;
/// Copies the attributes list to a buffer you must \c free() yourself.
/// Use \c list instead if you do not need more control over the lifetime of the list.
/// @param attributesCountOut the number of attributes is returned in this parameter.
- (objc_property_attribute_t *)copyAttributesList:(nullable unsigned int *)attributesCountOut;
/// The number of property attributes.
@property (nonatomic, readonly) NSUInteger count;
/// For use with \c class_replaceProperty and the like.
@property (nonatomic, readonly) objc_property_attribute_t *list;
/// The string value of the property attributes.
@property (nonatomic, readonly) NSString *string;
/// A human-readable version of the property attributes.
@property (nonatomic, readonly) NSString *fullDeclaration;
/// A dictionary of the property attributes.
/// Values are either a string or \c YES. Boolean attributes
/// which are false will not be present in the dictionary.
@property (nonatomic, readonly) NSDictionary *dictionary;
/// The name of the instance variable backing the property.
@property (nonatomic, readonly, nullable) NSString *backingIvar;
/// The type encoding of the property.
@property (nonatomic, readonly, nullable) NSString *typeEncoding;
/// The \e old type encoding of the property.
@property (nonatomic, readonly, nullable) NSString *oldTypeEncoding;
/// The property's custom getter, if any.
@property (nonatomic, readonly, nullable) SEL customGetter;
/// The property's custom setter, if any.
@property (nonatomic, readonly, nullable) SEL customSetter;
/// The property's custom getter as a string, if any.
@property (nonatomic, readonly, nullable) NSString *customGetterString;
/// The property's custom setter as a string, if any.
@property (nonatomic, readonly, nullable) NSString *customSetterString;
@property (nonatomic, readonly) BOOL isReadOnly;
@property (nonatomic, readonly) BOOL isCopy;
@property (nonatomic, readonly) BOOL isRetained;
@property (nonatomic, readonly) BOOL isNonatomic;
@property (nonatomic, readonly) BOOL isDynamic;
@property (nonatomic, readonly) BOOL isWeak;
@property (nonatomic, readonly) BOOL isGarbageCollectable;
@end
#pragma mark FLEXPropertyAttributes
@interface FLEXMutablePropertyAttributes : FLEXPropertyAttributes
/// Creates and returns an empty property attributes object.
+ (instancetype)attributes;
/// The name of the instance variable backing the property.
@property (nonatomic, nullable) NSString *backingIvar;
/// The type encoding of the property.
@property (nonatomic, nullable) NSString *typeEncoding;
/// The \e old type encoding of the property.
@property (nonatomic, nullable) NSString *oldTypeEncoding;
/// The property's custom getter, if any.
@property (nonatomic, nullable) SEL customGetter;
/// The property's custom setter, if any.
@property (nonatomic, nullable) SEL customSetter;
@property (nonatomic) BOOL isReadOnly;
@property (nonatomic) BOOL isCopy;
@property (nonatomic) BOOL isRetained;
@property (nonatomic) BOOL isNonatomic;
@property (nonatomic) BOOL isDynamic;
@property (nonatomic) BOOL isWeak;
@property (nonatomic) BOOL isGarbageCollectable;
/// A more convenient method of setting the \c typeEncoding property.
/// @discussion This will not work for complex types like structs and primitive pointers.
- (void)setTypeEncodingChar:(char)type;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,376 @@
//
// FLEXPropertyAttributes.m
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 7/5/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import "FLEXPropertyAttributes.h"
#import "FLEXRuntimeUtility.h"
#import "NSString+ObjcRuntime.h"
#import "NSDictionary+ObjcRuntime.h"
#pragma mark FLEXPropertyAttributes
@interface FLEXPropertyAttributes ()
@property (nonatomic) NSString *backingIvar;
@property (nonatomic) NSString *typeEncoding;
@property (nonatomic) NSString *oldTypeEncoding;
@property (nonatomic) SEL customGetter;
@property (nonatomic) SEL customSetter;
@property (nonatomic) BOOL isReadOnly;
@property (nonatomic) BOOL isCopy;
@property (nonatomic) BOOL isRetained;
@property (nonatomic) BOOL isNonatomic;
@property (nonatomic) BOOL isDynamic;
@property (nonatomic) BOOL isWeak;
@property (nonatomic) BOOL isGarbageCollectable;
- (NSString *)buildFullDeclaration;
@end
@implementation FLEXPropertyAttributes
@synthesize list = _list;
#pragma mark Initializers
+ (instancetype)attributesForProperty:(objc_property_t)property {
return [self attributesFromDictionary:[NSDictionary attributesDictionaryForProperty:property]];
}
+ (instancetype)attributesFromDictionary:(NSDictionary *)attributes {
return [[self alloc] initWithAttributesDictionary:attributes];
}
- (id)initWithAttributesDictionary:(NSDictionary *)attributes {
NSParameterAssert(attributes);
self = [super init];
if (self) {
_dictionary = attributes;
_string = attributes.propertyAttributesString;
_count = attributes.count;
_typeEncoding = attributes[kFLEXPropertyAttributeKeyTypeEncoding];
_backingIvar = attributes[kFLEXPropertyAttributeKeyBackingIvarName];
_oldTypeEncoding = attributes[kFLEXPropertyAttributeKeyOldStyleTypeEncoding];
_customGetterString = attributes[kFLEXPropertyAttributeKeyCustomGetter];
_customSetterString = attributes[kFLEXPropertyAttributeKeyCustomSetter];
_customGetter = NSSelectorFromString(_customGetterString);
_customSetter = NSSelectorFromString(_customSetterString);
_isReadOnly = attributes[kFLEXPropertyAttributeKeyReadOnly] != nil;
_isCopy = attributes[kFLEXPropertyAttributeKeyCopy] != nil;
_isRetained = attributes[kFLEXPropertyAttributeKeyRetain] != nil;
_isNonatomic = attributes[kFLEXPropertyAttributeKeyNonAtomic] != nil;
_isWeak = attributes[kFLEXPropertyAttributeKeyWeak] != nil;
_isGarbageCollectable = attributes[kFLEXPropertyAttributeKeyGarbageCollectable] != nil;
_fullDeclaration = [self buildFullDeclaration];
}
return self;
}
#pragma mark Misc
- (NSString *)description {
return [NSString
stringWithFormat:@"<%@ \"%@\", ivar=%@, readonly=%d, nonatomic=%d, getter=%@, setter=%@>",
NSStringFromClass(self.class),
self.string,
self.backingIvar ?: @"none",
self.isReadOnly,
self.isNonatomic,
NSStringFromSelector(self.customGetter) ?: @"none",
NSStringFromSelector(self.customSetter) ?: @"none"
];
}
- (objc_property_attribute_t *)copyAttributesList:(unsigned int *)attributesCount {
NSDictionary *attrs = self.string.propertyAttributes;
objc_property_attribute_t *propertyAttributes = malloc(attrs.count * sizeof(objc_property_attribute_t));
if (attributesCount) {
*attributesCount = (unsigned int)attrs.count;
}
NSUInteger i = 0;
for (NSString *key in attrs.allKeys) {
FLEXPropertyAttribute c = (FLEXPropertyAttribute)[key characterAtIndex:0];
switch (c) {
case FLEXPropertyAttributeTypeEncoding: {
objc_property_attribute_t pa = {
kFLEXPropertyAttributeKeyTypeEncoding.UTF8String,
self.typeEncoding.UTF8String
};
propertyAttributes[i] = pa;
break;
}
case FLEXPropertyAttributeBackingIvarName: {
objc_property_attribute_t pa = {
kFLEXPropertyAttributeKeyBackingIvarName.UTF8String,
self.backingIvar.UTF8String
};
propertyAttributes[i] = pa;
break;
}
case FLEXPropertyAttributeCopy: {
objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyCopy.UTF8String, ""};
propertyAttributes[i] = pa;
break;
}
case FLEXPropertyAttributeCustomGetter: {
objc_property_attribute_t pa = {
kFLEXPropertyAttributeKeyCustomGetter.UTF8String,
NSStringFromSelector(self.customGetter).UTF8String ?: ""
};
propertyAttributes[i] = pa;
break;
}
case FLEXPropertyAttributeCustomSetter: {
objc_property_attribute_t pa = {
kFLEXPropertyAttributeKeyCustomSetter.UTF8String,
NSStringFromSelector(self.customSetter).UTF8String ?: ""
};
propertyAttributes[i] = pa;
break;
}
case FLEXPropertyAttributeDynamic: {
objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyDynamic.UTF8String, ""};
propertyAttributes[i] = pa;
break;
}
case FLEXPropertyAttributeGarbageCollectible: {
objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyGarbageCollectable.UTF8String, ""};
propertyAttributes[i] = pa;
break;
}
case FLEXPropertyAttributeNonAtomic: {
objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyNonAtomic.UTF8String, ""};
propertyAttributes[i] = pa;
break;
}
case FLEXPropertyAttributeOldTypeEncoding: {
objc_property_attribute_t pa = {
kFLEXPropertyAttributeKeyOldStyleTypeEncoding.UTF8String,
self.oldTypeEncoding.UTF8String ?: ""
};
propertyAttributes[i] = pa;
break;
}
case FLEXPropertyAttributeReadOnly: {
objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyReadOnly.UTF8String, ""};
propertyAttributes[i] = pa;
break;
}
case FLEXPropertyAttributeRetain: {
objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyRetain.UTF8String, ""};
propertyAttributes[i] = pa;
break;
}
case FLEXPropertyAttributeWeak: {
objc_property_attribute_t pa = {kFLEXPropertyAttributeKeyWeak.UTF8String, ""};
propertyAttributes[i] = pa;
break;
}
}
i++;
}
return propertyAttributes;
}
- (objc_property_attribute_t *)list {
if (!_list) {
_list = [self copyAttributesList:nil];
}
return _list;
}
- (NSString *)buildFullDeclaration {
NSMutableString *decl = [NSMutableString new];
[decl appendFormat:@"%@, ", _isNonatomic ? @"nonatomic" : @"atomic"];
[decl appendFormat:@"%@, ", _isReadOnly ? @"readonly" : @"readwrite"];
BOOL noExplicitMemorySemantics = YES;
if (_isCopy) { noExplicitMemorySemantics = NO;
[decl appendString:@"copy, "];
}
if (_isRetained) { noExplicitMemorySemantics = NO;
[decl appendString:@"strong, "];
}
if (_isWeak) { noExplicitMemorySemantics = NO;
[decl appendString:@"weak, "];
}
if ([_typeEncoding hasPrefix:@"@"] && noExplicitMemorySemantics) {
// *probably* strong if this is an object; strong is the default.
[decl appendString:@"strong, "];
} else if (noExplicitMemorySemantics) {
// *probably* assign if this is not an object
[decl appendString:@"assign, "];
}
if (_customGetter) {
[decl appendFormat:@"getter=%@, ", NSStringFromSelector(_customGetter)];
}
if (_customSetter) {
[decl appendFormat:@"setter=%@, ", NSStringFromSelector(_customSetter)];
}
[decl deleteCharactersInRange:NSMakeRange(decl.length-2, 2)];
return decl.copy;
}
- (void)dealloc {
if (_list) {
free(_list);
_list = nil;
}
}
#pragma mark Copying
- (id)copyWithZone:(NSZone *)zone {
return [[FLEXPropertyAttributes class] attributesFromDictionary:self.dictionary];
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return [[FLEXMutablePropertyAttributes class] attributesFromDictionary:self.dictionary];
}
@end
#pragma mark FLEXMutablePropertyAttributes
@interface FLEXMutablePropertyAttributes ()
@property (nonatomic) BOOL countDelta;
@property (nonatomic) BOOL stringDelta;
@property (nonatomic) BOOL dictDelta;
@property (nonatomic) BOOL listDelta;
@property (nonatomic) BOOL declDelta;
@end
#define PropertyWithDeltaFlag(type, name, Name) @dynamic name; \
- (void)set ## Name:(type)name { \
if (name != _ ## name) { \
_countDelta = _stringDelta = _dictDelta = _listDelta = _declDelta = YES; \
_ ## name = name; \
} \
}
@implementation FLEXMutablePropertyAttributes
PropertyWithDeltaFlag(NSString *, backingIvar, BackingIvar);
PropertyWithDeltaFlag(NSString *, typeEncoding, TypeEncoding);
PropertyWithDeltaFlag(NSString *, oldTypeEncoding, OldTypeEncoding);
PropertyWithDeltaFlag(SEL, customGetter, CustomGetter);
PropertyWithDeltaFlag(SEL, customSetter, CustomSetter);
PropertyWithDeltaFlag(BOOL, isReadOnly, IsReadOnly);
PropertyWithDeltaFlag(BOOL, isCopy, IsCopy);
PropertyWithDeltaFlag(BOOL, isRetained, IsRetained);
PropertyWithDeltaFlag(BOOL, isNonatomic, IsNonatomic);
PropertyWithDeltaFlag(BOOL, isDynamic, IsDynamic);
PropertyWithDeltaFlag(BOOL, isWeak, IsWeak);
PropertyWithDeltaFlag(BOOL, isGarbageCollectable, IsGarbageCollectable);
+ (instancetype)attributes {
return [self new];
}
- (void)setTypeEncodingChar:(char)type {
self.typeEncoding = [NSString stringWithFormat:@"%c", type];
}
- (NSUInteger)count {
// Recalculate attribute count after mutations
if (self.countDelta) {
self.countDelta = NO;
_count = self.dictionary.count;
}
return _count;
}
- (objc_property_attribute_t *)list {
// Regenerate list after mutations
if (self.listDelta) {
self.listDelta = NO;
if (_list) {
free(_list);
_list = nil;
}
}
// Super will generate the list if it isn't set
return super.list;
}
- (NSString *)string {
// Regenerate string after mutations
if (self.stringDelta || !_string) {
self.stringDelta = NO;
_string = self.dictionary.propertyAttributesString;
}
return _string;
}
- (NSDictionary *)dictionary {
// Regenerate dictionary after mutations
if (self.dictDelta || !_dictionary) {
// _stringa nd _dictionary depend on each other,
// so we must generate ONE by hand using our properties.
// We arbitrarily choose to generate the dictionary.
NSMutableDictionary *attrs = [NSMutableDictionary new];
if (self.typeEncoding)
attrs[kFLEXPropertyAttributeKeyTypeEncoding] = self.typeEncoding;
if (self.backingIvar)
attrs[kFLEXPropertyAttributeKeyBackingIvarName] = self.backingIvar;
if (self.oldTypeEncoding)
attrs[kFLEXPropertyAttributeKeyOldStyleTypeEncoding] = self.oldTypeEncoding;
if (self.customGetter)
attrs[kFLEXPropertyAttributeKeyCustomGetter] = NSStringFromSelector(self.customGetter);
if (self.customSetter)
attrs[kFLEXPropertyAttributeKeyCustomSetter] = NSStringFromSelector(self.customSetter);
if (self.isReadOnly) attrs[kFLEXPropertyAttributeKeyReadOnly] = @YES;
if (self.isCopy) attrs[kFLEXPropertyAttributeKeyCopy] = @YES;
if (self.isRetained) attrs[kFLEXPropertyAttributeKeyRetain] = @YES;
if (self.isNonatomic) attrs[kFLEXPropertyAttributeKeyNonAtomic] = @YES;
if (self.isDynamic) attrs[kFLEXPropertyAttributeKeyDynamic] = @YES;
if (self.isWeak) attrs[kFLEXPropertyAttributeKeyWeak] = @YES;
if (self.isGarbageCollectable) attrs[kFLEXPropertyAttributeKeyGarbageCollectable] = @YES;
_dictionary = attrs.copy;
}
return _dictionary;
}
- (NSString *)fullDeclaration {
if (self.declDelta || !_fullDeclaration) {
_declDelta = NO;
_fullDeclaration = [self buildFullDeclaration];
}
return _fullDeclaration;
}
- (NSString *)customGetterString {
return _customGetter ? NSStringFromSelector(_customGetter) : nil;
}
- (NSString *)customSetterString {
return _customSetter ? NSStringFromSelector(_customSetter) : nil;
}
@end

View File

@@ -0,0 +1,73 @@
//
// FLEXProtocol.h
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 6/30/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import "FLEXRuntimeConstants.h"
@class FLEXProperty, FLEXMethodDescription;
NS_ASSUME_NONNULL_BEGIN
#pragma mark FLEXProtocol
@interface FLEXProtocol : NSObject
/// Every protocol registered with the runtime.
+ (NSArray<FLEXProtocol *> *)allProtocols;
+ (instancetype)protocol:(Protocol *)protocol;
/// The underlying protocol data structure.
@property (nonatomic, readonly) Protocol *objc_protocol;
/// The name of the protocol.
@property (nonatomic, readonly) NSString *name;
/// The required methods of the protocol, if any. This includes property getters and setters.
@property (nonatomic, readonly) NSArray<FLEXMethodDescription *> *requiredMethods;
/// The optional methods of the protocol, if any. This includes property getters and setters.
@property (nonatomic, readonly) NSArray<FLEXMethodDescription *> *optionalMethods;
/// All protocols that this protocol conforms to, if any.
@property (nonatomic, readonly) NSArray<FLEXProtocol *> *protocols;
/// The full path of the image that contains this protocol definition,
/// or \c nil if this protocol was probably defined at runtime.
@property (nonatomic, readonly, nullable) NSString *imagePath;
/// The properties in the protocol, if any. \c nil on iOS 10+
@property (nonatomic, readonly, nullable) NSArray<FLEXProperty *> *properties API_DEPRECATED("Use the more specific accessors below", ios(2.0, 10.0));
/// The required properties in the protocol, if any.
@property (nonatomic, readonly) NSArray<FLEXProperty *> *requiredProperties API_AVAILABLE(ios(10.0));
/// The optional properties in the protocol, if any.
@property (nonatomic, readonly) NSArray<FLEXProperty *> *optionalProperties API_AVAILABLE(ios(10.0));
/// For internal use
@property (nonatomic) id tag;
/// Not to be confused with \c -conformsToProtocol:, which refers to the current
/// \c FLEXProtocol instance and not the underlying \c Protocol object.
- (BOOL)conformsTo:(Protocol *)protocol;
@end
#pragma mark Method descriptions
@interface FLEXMethodDescription : NSObject
+ (instancetype)description:(struct objc_method_description)description;
+ (instancetype)description:(struct objc_method_description)description instance:(BOOL)isInstance;
/// The underlying method description data structure.
@property (nonatomic, readonly) struct objc_method_description objc_description;
/// The method's selector.
@property (nonatomic, readonly) SEL selector;
/// The method's type encoding.
@property (nonatomic, readonly) NSString *typeEncoding;
/// The method's return type.
@property (nonatomic, readonly) FLEXTypeEncoding returnType;
/// \c YES if this is an instance method, \c NO if it is a class method, or \c nil if unspecified
@property (nonatomic, readonly) NSNumber *instance;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,212 @@
//
// FLEXProtocol.m
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 6/30/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import "FLEXProtocol.h"
#import "FLEXProperty.h"
#import "FLEXRuntimeUtility.h"
#import "NSArray+FLEX.h"
#include <dlfcn.h>
@implementation FLEXProtocol
#pragma mark Initializers
+ (NSArray *)allProtocols {
unsigned int prcount;
Protocol *__unsafe_unretained*protocols = objc_copyProtocolList(&prcount);
NSMutableArray *all = [NSMutableArray new];
for(NSUInteger i = 0; i < prcount; i++)
[all addObject:[self protocol:protocols[i]]];
free(protocols);
return all;
}
+ (instancetype)protocol:(Protocol *)protocol {
return [[self alloc] initWithProtocol:protocol];
}
- (id)initWithProtocol:(Protocol *)protocol {
NSParameterAssert(protocol);
self = [super init];
if (self) {
_objc_protocol = protocol;
[self examine];
}
return self;
}
#pragma mark Other
- (NSString *)description {
return self.name;
}
- (NSString *)debugDescription {
return [NSString stringWithFormat:@"<%@ name=%@, %lu properties, %lu required methods, %lu optional methods, %lu protocols>",
NSStringFromClass(self.class), self.name, (unsigned long)self.properties.count,
(unsigned long)self.requiredMethods.count, (unsigned long)self.optionalMethods.count, (unsigned long)self.protocols.count];
}
- (void)examine {
_name = @(protocol_getName(self.objc_protocol));
// imagePath
Dl_info exeInfo;
if (dladdr((__bridge const void *)(_objc_protocol), &exeInfo)) {
_imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : nil;
}
// Conformances and methods //
unsigned int pccount, mdrcount, mdocount;
struct objc_method_description *objcrMethods, *objcoMethods;
Protocol *protocol = _objc_protocol;
Protocol * __unsafe_unretained *protocols = protocol_copyProtocolList(protocol, &pccount);
// Protocols
_protocols = [NSArray flex_forEachUpTo:pccount map:^id(NSUInteger i) {
return [FLEXProtocol protocol:protocols[i]];
}];
free(protocols);
// Required instance methods
objcrMethods = protocol_copyMethodDescriptionList(protocol, YES, YES, &mdrcount);
NSArray *rMethods = [NSArray flex_forEachUpTo:mdrcount map:^id(NSUInteger i) {
return [FLEXMethodDescription description:objcrMethods[i] instance:YES];
}];
free(objcrMethods);
// Required class methods
objcrMethods = protocol_copyMethodDescriptionList(protocol, YES, NO, &mdrcount);
_requiredMethods = [[NSArray flex_forEachUpTo:mdrcount map:^id(NSUInteger i) {
return [FLEXMethodDescription description:objcrMethods[i] instance:NO];
}] arrayByAddingObjectsFromArray:rMethods];
free(objcrMethods);
// Optional instance methods
objcoMethods = protocol_copyMethodDescriptionList(protocol, NO, YES, &mdocount);
NSArray *oMethods = [NSArray flex_forEachUpTo:mdocount map:^id(NSUInteger i) {
return [FLEXMethodDescription description:objcoMethods[i] instance:YES];
}];
free(objcoMethods);
// Optional class methods
objcoMethods = protocol_copyMethodDescriptionList(protocol, NO, NO, &mdocount);
_optionalMethods = [[NSArray flex_forEachUpTo:mdocount map:^id(NSUInteger i) {
return [FLEXMethodDescription description:objcoMethods[i] instance:NO];
}] arrayByAddingObjectsFromArray:oMethods];
free(objcoMethods);
// Properties is a hassle because they didn't fix the API until iOS 10 //
if (@available(iOS 10.0, *)) {
unsigned int prrcount, procount;
Class instance = [NSObject class], meta = objc_getMetaClass("NSObject");
// Required class and instance properties //
// Instance first
objc_property_t *rProps = protocol_copyPropertyList2(protocol, &prrcount, YES, YES);
NSArray *rProperties = [NSArray flex_forEachUpTo:prrcount map:^id(NSUInteger i) {
return [FLEXProperty property:rProps[i] onClass:instance];
}];
free(rProps);
// Then class
rProps = protocol_copyPropertyList2(protocol, &prrcount, NO, YES);
_requiredProperties = [[NSArray flex_forEachUpTo:prrcount map:^id(NSUInteger i) {
return [FLEXProperty property:rProps[i] onClass:instance];
}] arrayByAddingObjectsFromArray:rProperties];
free(rProps);
// Optional class and instance properties //
// Instance first
objc_property_t *oProps = protocol_copyPropertyList2(protocol, &procount, YES, YES);
NSArray *oProperties = [NSArray flex_forEachUpTo:prrcount map:^id(NSUInteger i) {
return [FLEXProperty property:oProps[i] onClass:meta];
}];
free(oProps);
// Then class
oProps = protocol_copyPropertyList2(protocol, &procount, NO, YES);
_optionalProperties = [[NSArray flex_forEachUpTo:procount map:^id(NSUInteger i) {
return [FLEXProperty property:oProps[i] onClass:meta];
}] arrayByAddingObjectsFromArray:oProperties];
free(oProps);
} else {
unsigned int prcount;
objc_property_t *objcproperties = protocol_copyPropertyList(protocol, &prcount);
_properties = [NSArray flex_forEachUpTo:prcount map:^id(NSUInteger i) {
return [FLEXProperty property:objcproperties[i]];
}];
_requiredProperties = @[];
_optionalProperties = @[];
free(objcproperties);
}
}
- (BOOL)conformsTo:(Protocol *)protocol {
return protocol_conformsToProtocol(self.objc_protocol, protocol);
}
@end
#pragma mark FLEXMethodDescription
@implementation FLEXMethodDescription
- (id)init {
[NSException
raise:NSInternalInconsistencyException
format:@"Class instance should not be created with -init"
];
return nil;
}
+ (instancetype)description:(struct objc_method_description)description {
return [[self alloc] initWithDescription:description instance:nil];
}
+ (instancetype)description:(struct objc_method_description)description instance:(BOOL)isInstance {
return [[self alloc] initWithDescription:description instance:@(isInstance)];
}
- (id)initWithDescription:(struct objc_method_description)md instance:(NSNumber *)instance {
NSParameterAssert(md.name != NULL);
self = [super init];
if (self) {
_objc_description = md;
_selector = md.name;
_typeEncoding = @(md.types);
_returnType = (FLEXTypeEncoding)[self.typeEncoding characterAtIndex:0];
_instance = instance;
}
return self;
}
- (NSString *)description {
return NSStringFromSelector(self.selector);
}
- (NSString *)debugDescription {
return [NSString stringWithFormat:@"<%@ name=%@, type=%@>",
NSStringFromClass(self.class), NSStringFromSelector(self.selector), self.typeEncoding];
}
@end

View File

@@ -0,0 +1,41 @@
//
// FLEXProtocolBuilder.h
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 7/4/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import <Foundation/Foundation.h>
@class FLEXProperty, FLEXProtocol, Protocol;
@interface FLEXProtocolBuilder : NSObject
/// Begins to construct a new protocol with the given name.
/// @discussion You must register the protocol with the
/// \c registerProtocol method before you can use it.
+ (instancetype)allocateProtocol:(NSString *)name;
/// Adds a property to a protocol.
/// @param property The property to add.
/// @param isRequired Whether the property is required to implement the protocol.
- (void)addProperty:(FLEXProperty *)property isRequired:(BOOL)isRequired;
/// Adds a property to a protocol.
/// @param selector The selector of the method to add.
/// @param typeEncoding The type encoding of the method to add.
/// @param isRequired Whether the method is required to implement the protocol.
/// @param isInstanceMethod \c YES if the method is an instance method, \c NO if it is a class method.
- (void)addMethod:(SEL)selector
typeEncoding:(NSString *)typeEncoding
isRequired:(BOOL)isRequired
isInstanceMethod:(BOOL)isInstanceMethod;
/// Makes the recieving protocol conform to the given protocol.
- (void)addProtocol:(Protocol *)protocol;
/// Registers and returns the recieving protocol, which was previously under construction.
- (FLEXProtocol *)registerProtocol;
/// Whether the protocol is still under construction or already registered.
@property (nonatomic, readonly) BOOL isRegistered;
@end

View File

@@ -0,0 +1,93 @@
//
// FLEXProtocolBuilder.m
// FLEX
//
// Derived from MirrorKit.
// Created by Tanner on 7/4/15.
// Copyright (c) 2020 FLEX Team. All rights reserved.
//
#import "FLEXProtocolBuilder.h"
#import "FLEXProtocol.h"
#import "FLEXProperty.h"
#import <objc/runtime.h>
#define MutationAssertion(msg) if (self.isRegistered) { \
[NSException \
raise:NSInternalInconsistencyException \
format:msg \
]; \
}
@interface FLEXProtocolBuilder ()
@property (nonatomic) Protocol *workingProtocol;
@property (nonatomic) NSString *name;
@end
@implementation FLEXProtocolBuilder
- (id)init {
[NSException
raise:NSInternalInconsistencyException
format:@"Class instance should not be created with -init"
];
return nil;
}
#pragma mark Initializers
+ (instancetype)allocateProtocol:(NSString *)name {
NSParameterAssert(name);
return [[self alloc] initWithProtocol:objc_allocateProtocol(name.UTF8String)];
}
- (id)initWithProtocol:(Protocol *)protocol {
NSParameterAssert(protocol);
self = [super init];
if (self) {
_workingProtocol = protocol;
_name = NSStringFromProtocol(self.workingProtocol);
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@ name=%@, registered=%d>",
NSStringFromClass(self.class), self.name, self.isRegistered];
}
#pragma mark Building
- (void)addProperty:(FLEXProperty *)property isRequired:(BOOL)isRequired {
MutationAssertion(@"Properties cannot be added once a protocol has been registered");
unsigned int count;
objc_property_attribute_t *attributes = [property copyAttributesList:&count];
protocol_addProperty(self.workingProtocol, property.name.UTF8String, attributes, count, isRequired, YES);
free(attributes);
}
- (void)addMethod:(SEL)selector
typeEncoding:(NSString *)typeEncoding
isRequired:(BOOL)isRequired
isInstanceMethod:(BOOL)isInstanceMethod {
MutationAssertion(@"Methods cannot be added once a protocol has been registered");
protocol_addMethodDescription(self.workingProtocol, selector, typeEncoding.UTF8String, isRequired, isInstanceMethod);
}
- (void)addProtocol:(Protocol *)protocol {
MutationAssertion(@"Protocols cannot be added once a protocol has been registered");
protocol_addProtocol(self.workingProtocol, protocol);
}
- (FLEXProtocol *)registerProtocol {
MutationAssertion(@"Protocol is already registered");
_isRegistered = YES;
objc_registerProtocol(self.workingProtocol);
return [FLEXProtocol protocol:self.workingProtocol];
}
@end