mirror of
				https://github.com/SoPat712/YTLitePlus.git
				synced 2025-10-31 12:54:14 -04: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
	 Balackburn
					Balackburn