mirror of
https://github.com/SoPat712/YTLitePlus.git
synced 2025-12-27 08:45:22 -05:00
added files via upload
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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)) \
|
||||
]
|
||||
168
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXClassBuilder.m
Normal file
168
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXClassBuilder.m
Normal 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
|
||||
51
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXIvar.h
Normal file
51
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXIvar.h
Normal 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
|
||||
158
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXIvar.m
Normal file
158
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXIvar.m
Normal 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
|
||||
96
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXMethod.h
Normal file
96
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXMethod.h
Normal 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
|
||||
430
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXMethod.m
Normal file
430
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXMethod.m
Normal 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
|
||||
43
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXMethodBase.h
Normal file
43
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXMethodBase.h
Normal 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
|
||||
49
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXMethodBase.m
Normal file
49
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXMethodBase.m
Normal 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
|
||||
97
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXMirror.h
Normal file
97
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXMirror.h
Normal 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
|
||||
145
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXMirror.m
Normal file
145
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXMirror.m
Normal 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
|
||||
138
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXProperty.h
Normal file
138
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXProperty.h
Normal 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
|
||||
295
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXProperty.m
Normal file
295
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXProperty.m
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
73
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXProtocol.h
Normal file
73
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXProtocol.h
Normal 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
|
||||
212
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXProtocol.m
Normal file
212
Tweaks/FLEX/Utility/Runtime/Objc/Reflection/FLEXProtocol.m
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user