mirror of
				https://github.com/SoPat712/YTLitePlus.git
				synced 2025-10-31 04:44:14 -04:00 
			
		
		
		
	added files via upload
This commit is contained in:
		
							
								
								
									
										378
									
								
								Tweaks/FLEX/ObjectExplorers/FLEXObjectExplorer.m
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										378
									
								
								Tweaks/FLEX/ObjectExplorers/FLEXObjectExplorer.m
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,378 @@ | ||||
| // | ||||
| //  FLEXObjectExplorer.m | ||||
| //  FLEX | ||||
| // | ||||
| //  Created by Tanner Bennett on 8/28/19. | ||||
| //  Copyright © 2020 FLEX Team. All rights reserved. | ||||
| // | ||||
|  | ||||
| #import "FLEXObjectExplorer.h" | ||||
| #import "FLEXUtility.h" | ||||
| #import "FLEXRuntimeUtility.h" | ||||
| #import "NSObject+FLEX_Reflection.h" | ||||
| #import "FLEXRuntime+Compare.h" | ||||
| #import "FLEXRuntime+UIKitHelpers.h" | ||||
| #import "FLEXPropertyAttributes.h" | ||||
| #import "FLEXMetadataSection.h" | ||||
| #import "NSUserDefaults+FLEX.h" | ||||
|  | ||||
| @implementation FLEXObjectExplorerDefaults | ||||
|  | ||||
| + (instancetype)canEdit:(BOOL)editable wantsPreviews:(BOOL)showPreviews { | ||||
|     FLEXObjectExplorerDefaults *defaults = [self new]; | ||||
|     defaults->_isEditable = editable; | ||||
|     defaults->_wantsDynamicPreviews = showPreviews; | ||||
|     return defaults; | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
| @interface FLEXObjectExplorer () { | ||||
|     NSMutableArray<NSArray<FLEXProperty *> *> *_allProperties; | ||||
|     NSMutableArray<NSArray<FLEXProperty *> *> *_allClassProperties; | ||||
|     NSMutableArray<NSArray<FLEXIvar *> *> *_allIvars; | ||||
|     NSMutableArray<NSArray<FLEXMethod *> *> *_allMethods; | ||||
|     NSMutableArray<NSArray<FLEXMethod *> *> *_allClassMethods; | ||||
|     NSMutableArray<NSArray<FLEXProtocol *> *> *_allConformedProtocols; | ||||
|     NSMutableArray<FLEXStaticMetadata *> *_allInstanceSizes; | ||||
|     NSMutableArray<FLEXStaticMetadata *> *_allImageNames; | ||||
|     NSString *_objectDescription; | ||||
| } | ||||
| @end | ||||
|  | ||||
| @implementation FLEXObjectExplorer | ||||
|  | ||||
| #pragma mark - Initialization | ||||
|  | ||||
| + (id)forObject:(id)objectOrClass { | ||||
|     return [[self alloc] initWithObject:objectOrClass]; | ||||
| } | ||||
|  | ||||
| - (id)initWithObject:(id)objectOrClass { | ||||
|     NSParameterAssert(objectOrClass); | ||||
|      | ||||
|     self = [super init]; | ||||
|     if (self) { | ||||
|         _object = objectOrClass; | ||||
|         _objectIsInstance = !object_isClass(objectOrClass); | ||||
|          | ||||
|         [self reloadMetadata]; | ||||
|     } | ||||
|  | ||||
|     return self; | ||||
| } | ||||
|  | ||||
|  | ||||
| #pragma mark - Public | ||||
|  | ||||
| + (void)configureDefaultsForItems:(NSArray<id<FLEXObjectExplorerItem>> *)items { | ||||
|     BOOL hidePreviews = NSUserDefaults.standardUserDefaults.flex_explorerHidesVariablePreviews; | ||||
|     FLEXObjectExplorerDefaults *mutable = [FLEXObjectExplorerDefaults | ||||
|         canEdit:YES wantsPreviews:!hidePreviews | ||||
|     ]; | ||||
|     FLEXObjectExplorerDefaults *immutable = [FLEXObjectExplorerDefaults | ||||
|         canEdit:NO wantsPreviews:!hidePreviews | ||||
|     ]; | ||||
|  | ||||
|     // .tag is used to cache whether the value of .isEditable; | ||||
|     // This could change at runtime so it is important that | ||||
|     // it is cached every time shortcuts are requeted and not | ||||
|     // just once at as shortcuts are initially registered | ||||
|     for (id<FLEXObjectExplorerItem> metadata in items) { | ||||
|         metadata.defaults = metadata.isEditable ? mutable : immutable; | ||||
|     } | ||||
| } | ||||
|  | ||||
| - (NSString *)objectDescription { | ||||
|     if (!_objectDescription) { | ||||
|         // Hard-code UIColor description | ||||
|         if ([FLEXRuntimeUtility safeObject:self.object isKindOfClass:[UIColor class]]) { | ||||
|             CGFloat h, s, l, r, g, b, a; | ||||
|             [self.object getRed:&r green:&g blue:&b alpha:&a]; | ||||
|             [self.object getHue:&h saturation:&s brightness:&l alpha:nil]; | ||||
|  | ||||
|             return [NSString stringWithFormat: | ||||
|                 @"HSL: (%.3f, %.3f, %.3f)\nRGB: (%.3f, %.3f, %.3f)\nAlpha: %.3f", | ||||
|                 h, s, l, r, g, b, a | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         NSString *description = [FLEXRuntimeUtility safeDescriptionForObject:self.object]; | ||||
|  | ||||
|         if (!description.length) { | ||||
|             NSString *address = [FLEXUtility addressOfObject:self.object]; | ||||
|             return [NSString stringWithFormat:@"Object at %@ returned empty description", address]; | ||||
|         } | ||||
|          | ||||
|         if (description.length > 10000) { | ||||
|             description = [description substringToIndex:10000]; | ||||
|         } | ||||
|  | ||||
|         _objectDescription = description; | ||||
|     } | ||||
|  | ||||
|     return _objectDescription; | ||||
| } | ||||
|  | ||||
| - (void)setClassScope:(NSInteger)classScope { | ||||
|     _classScope = classScope; | ||||
|      | ||||
|     [self reloadScopedMetadata]; | ||||
| } | ||||
|  | ||||
| - (void)reloadMetadata { | ||||
|     _allProperties = [NSMutableArray new]; | ||||
|     _allClassProperties = [NSMutableArray new]; | ||||
|     _allIvars = [NSMutableArray new]; | ||||
|     _allMethods = [NSMutableArray new]; | ||||
|     _allClassMethods = [NSMutableArray new]; | ||||
|     _allConformedProtocols = [NSMutableArray new]; | ||||
|     _allInstanceSizes = [NSMutableArray new]; | ||||
|     _allImageNames = [NSMutableArray new]; | ||||
|     _objectDescription = nil; | ||||
|  | ||||
|     [self reloadClassHierarchy]; | ||||
|      | ||||
|     NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; | ||||
|     BOOL hideBackingIvars = defaults.flex_explorerHidesPropertyIvars; | ||||
|     BOOL hidePropertyMethods = defaults.flex_explorerHidesPropertyMethods; | ||||
|     BOOL hidePrivateMethods = defaults.flex_explorerHidesPrivateMethods; | ||||
|     BOOL showMethodOverrides = defaults.flex_explorerShowsMethodOverrides; | ||||
|      | ||||
|     NSMutableArray<NSArray<FLEXProperty *> *> *allProperties = [NSMutableArray new]; | ||||
|     NSMutableArray<NSArray<FLEXProperty *> *> *allClassProps = [NSMutableArray new]; | ||||
|     NSMutableArray<NSArray<FLEXMethod *> *> *allMethods = [NSMutableArray new]; | ||||
|     NSMutableArray<NSArray<FLEXMethod *> *> *allClassMethods = [NSMutableArray new]; | ||||
|  | ||||
|     // Loop over each class and each superclass, collect | ||||
|     // the fresh and unique metadata in each category | ||||
|     Class superclass = nil; | ||||
|     NSInteger count = self.classHierarchyClasses.count; | ||||
|     NSInteger rootIdx = count - 1; | ||||
|     for (NSInteger i = 0; i < count; i++) { | ||||
|         Class cls = self.classHierarchyClasses[i]; | ||||
|         superclass = (i < rootIdx) ? self.classHierarchyClasses[i+1] : nil; | ||||
|  | ||||
|         [allProperties addObject:[self | ||||
|             metadataUniquedByName:[cls flex_allInstanceProperties] | ||||
|             superclass:superclass | ||||
|             kind:FLEXMetadataKindProperties | ||||
|             skip:showMethodOverrides | ||||
|         ]]; | ||||
|         [allClassProps addObject:[self | ||||
|             metadataUniquedByName:[cls flex_allClassProperties] | ||||
|             superclass:superclass | ||||
|             kind:FLEXMetadataKindClassProperties | ||||
|             skip:showMethodOverrides | ||||
|         ]]; | ||||
|         [_allIvars addObject:[self | ||||
|             metadataUniquedByName:[cls flex_allIvars] | ||||
|             superclass:nil | ||||
|             kind:FLEXMetadataKindIvars | ||||
|             skip:NO | ||||
|         ]]; | ||||
|         [allMethods addObject:[self | ||||
|             metadataUniquedByName:[cls flex_allInstanceMethods] | ||||
|             superclass:superclass | ||||
|             kind:FLEXMetadataKindMethods | ||||
|             skip:showMethodOverrides | ||||
|         ]]; | ||||
|         [allClassMethods addObject:[self | ||||
|             metadataUniquedByName:[cls flex_allClassMethods] | ||||
|             superclass:superclass | ||||
|             kind:FLEXMetadataKindClassMethods | ||||
|             skip:showMethodOverrides | ||||
|         ]]; | ||||
|         [_allConformedProtocols addObject:[self | ||||
|             metadataUniquedByName:[cls flex_protocols] | ||||
|             superclass:superclass | ||||
|             kind:FLEXMetadataKindProtocols | ||||
|             skip:NO | ||||
|         ]]; | ||||
|          | ||||
|         // TODO: join instance size, image name, and class hierarchy into a single model object | ||||
|         // This would greatly reduce the laziness that has begun to manifest itself here | ||||
|         [_allInstanceSizes addObject:[FLEXStaticMetadata | ||||
|             style:FLEXStaticMetadataRowStyleKeyValue | ||||
|             title:@"Instance Size" number:@(class_getInstanceSize(cls)) | ||||
|         ]]; | ||||
|         [_allImageNames addObject:[FLEXStaticMetadata | ||||
|             style:FLEXStaticMetadataRowStyleDefault | ||||
|             title:@"Image Name" string:@(class_getImageName(cls) ?: "Created at Runtime") | ||||
|         ]]; | ||||
|     } | ||||
|      | ||||
|     _classHierarchy = [FLEXStaticMetadata classHierarchy:self.classHierarchyClasses]; | ||||
|      | ||||
|     NSArray<NSArray<FLEXProperty *> *> *properties = allProperties; | ||||
|      | ||||
|     // Potentially filter property-backing ivars | ||||
|     if (hideBackingIvars) { | ||||
|         NSArray<NSArray<FLEXIvar *> *> *ivars = _allIvars.copy; | ||||
|         _allIvars = [ivars flex_mapped:^id(NSArray<FLEXIvar *> *list, NSUInteger idx) { | ||||
|             // Get a set of all backing ivar names for the current class in the hierarchy | ||||
|             NSSet *ivarNames = [NSSet setWithArray:({ | ||||
|                 [properties[idx] flex_mapped:^id(FLEXProperty *p, NSUInteger idx) { | ||||
|                     // Nil if no ivar, and array is flatted | ||||
|                     return p.likelyIvarName; | ||||
|                 }]; | ||||
|             })]; | ||||
|              | ||||
|             // Remove ivars whose name is in the ivar names list | ||||
|             return [list flex_filtered:^BOOL(FLEXIvar *ivar, NSUInteger idx) { | ||||
|                 return ![ivarNames containsObject:ivar.name]; | ||||
|             }]; | ||||
|         }]; | ||||
|     } | ||||
|      | ||||
|     // Potentially filter property-backing methods | ||||
|     if (hidePropertyMethods) { | ||||
|         allMethods = [allMethods flex_mapped:^id(NSArray<FLEXMethod *> *list, NSUInteger idx) { | ||||
|             // Get a set of all property method names for the current class in the hierarchy | ||||
|             NSSet *methodNames = [NSSet setWithArray:({ | ||||
|                 [properties[idx] flex_flatmapped:^NSArray *(FLEXProperty *p, NSUInteger idx) { | ||||
|                     if (p.likelyGetterExists) { | ||||
|                         if (p.likelySetterExists) { | ||||
|                             return @[p.likelyGetterString, p.likelySetterString]; | ||||
|                         } | ||||
|                          | ||||
|                         return @[p.likelyGetterString]; | ||||
|                     } else if (p.likelySetterExists) { | ||||
|                         return @[p.likelySetterString]; | ||||
|                     } | ||||
|                      | ||||
|                     return nil; | ||||
|                 }]; | ||||
|             })]; | ||||
|              | ||||
|             // Remove methods whose name is in the property method names list | ||||
|             return [list flex_filtered:^BOOL(FLEXMethod *method, NSUInteger idx) { | ||||
|                 return ![methodNames containsObject:method.selectorString]; | ||||
|             }]; | ||||
|         }]; | ||||
|     } | ||||
|      | ||||
|     if (hidePrivateMethods) { | ||||
|         id methodMapBlock = ^id(NSArray<FLEXMethod *> *list, NSUInteger idx) { | ||||
|             // Remove methods which contain an underscore | ||||
|             return [list flex_filtered:^BOOL(FLEXMethod *method, NSUInteger idx) { | ||||
|                 return ![method.selectorString containsString:@"_"]; | ||||
|             }]; | ||||
|         }; | ||||
|         id propertyMapBlock = ^id(NSArray<FLEXProperty *> *list, NSUInteger idx) { | ||||
|             // Remove methods which contain an underscore | ||||
|             return [list flex_filtered:^BOOL(FLEXProperty *prop, NSUInteger idx) { | ||||
|                 return ![prop.name containsString:@"_"]; | ||||
|             }]; | ||||
|         }; | ||||
|          | ||||
|         allMethods = [allMethods flex_mapped:methodMapBlock]; | ||||
|         allClassMethods = [allClassMethods flex_mapped:methodMapBlock]; | ||||
|         allProperties = [allProperties flex_mapped:propertyMapBlock]; | ||||
|         allClassProps = [allClassProps flex_mapped:propertyMapBlock]; | ||||
|     } | ||||
|      | ||||
|     _allProperties = allProperties; | ||||
|     _allClassProperties = allClassProps; | ||||
|     _allMethods = allMethods; | ||||
|     _allClassMethods = allClassMethods; | ||||
|  | ||||
|     // Set up UIKit helper data | ||||
|     // Really, we only need to call this on properties and ivars | ||||
|     // because no other metadata types support editing. | ||||
|     NSArray<NSArray *>*metadatas = @[ | ||||
|         _allProperties, _allClassProperties, _allIvars, | ||||
|        /* _allMethods, _allClassMethods, _allConformedProtocols */ | ||||
|     ]; | ||||
|     for (NSArray *matrix in metadatas) { | ||||
|         for (NSArray *metadataByClass in matrix) { | ||||
|             [FLEXObjectExplorer configureDefaultsForItems:metadataByClass]; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     [self reloadScopedMetadata]; | ||||
| } | ||||
|  | ||||
|  | ||||
| #pragma mark - Private | ||||
|  | ||||
| - (void)reloadScopedMetadata { | ||||
|     _properties = self.allProperties[self.classScope]; | ||||
|     _classProperties = self.allClassProperties[self.classScope]; | ||||
|     _ivars = self.allIvars[self.classScope]; | ||||
|     _methods = self.allMethods[self.classScope]; | ||||
|     _classMethods = self.allClassMethods[self.classScope]; | ||||
|     _conformedProtocols = self.allConformedProtocols[self.classScope]; | ||||
|     _instanceSize = self.allInstanceSizes[self.classScope]; | ||||
|     _imageName = self.allImageNames[self.classScope]; | ||||
| } | ||||
|  | ||||
| /// Accepts an array of flex metadata objects and discards objects | ||||
| /// with duplicate names, as well as properties and methods which | ||||
| /// aren't "new" (i.e. those which the superclass responds to) | ||||
| - (NSArray *)metadataUniquedByName:(NSArray *)list | ||||
|                         superclass:(Class)superclass | ||||
|                               kind:(FLEXMetadataKind)kind | ||||
|                               skip:(BOOL)skipUniquing { | ||||
|     if (skipUniquing) { | ||||
|         return list; | ||||
|     } | ||||
|      | ||||
|     // Remove items with same name and return filtered list | ||||
|     NSMutableSet *names = [NSMutableSet new]; | ||||
|     return [list flex_filtered:^BOOL(id obj, NSUInteger idx) { | ||||
|         NSString *name = [obj name]; | ||||
|         if ([names containsObject:name]) { | ||||
|             return NO; | ||||
|         } else { | ||||
|             [names addObject:name]; | ||||
|  | ||||
|             // Skip methods and properties which are just overrides, | ||||
|             // potentially skip ivars and methods associated with properties | ||||
|             switch (kind) { | ||||
|                 case FLEXMetadataKindProperties: | ||||
|                     if ([superclass instancesRespondToSelector:[obj likelyGetter]]) { | ||||
|                         return NO; | ||||
|                     } | ||||
|                     break; | ||||
|                 case FLEXMetadataKindClassProperties: | ||||
|                     if ([superclass respondsToSelector:[obj likelyGetter]]) { | ||||
|                         return NO; | ||||
|                     } | ||||
|                     break; | ||||
|                 case FLEXMetadataKindMethods: | ||||
|                     if ([superclass instancesRespondToSelector:NSSelectorFromString(name)]) { | ||||
|                         return NO; | ||||
|                     } | ||||
|                     break; | ||||
|                 case FLEXMetadataKindClassMethods: | ||||
|                     if ([superclass respondsToSelector:NSSelectorFromString(name)]) { | ||||
|                         return NO; | ||||
|                     } | ||||
|                     break; | ||||
|  | ||||
|                 case FLEXMetadataKindProtocols: | ||||
|                 case FLEXMetadataKindClassHierarchy: | ||||
|                 case FLEXMetadataKindOther: | ||||
|                     return YES; // These types are already uniqued | ||||
|                     break; | ||||
|                      | ||||
|                 // Ivars cannot be overidden | ||||
|                 case FLEXMetadataKindIvars: break; | ||||
|             } | ||||
|  | ||||
|             return YES; | ||||
|         } | ||||
|     }]; | ||||
| } | ||||
|  | ||||
|  | ||||
| #pragma mark - Superclasses | ||||
|  | ||||
| - (void)reloadClassHierarchy { | ||||
|     // The class hierarchy will never contain metaclass objects by this logic; | ||||
|     // it is always the same for a given class and instances of it | ||||
|     _classHierarchyClasses = [[self.object class] flex_classHierarchy]; | ||||
| } | ||||
|  | ||||
| @end | ||||
		Reference in New Issue
	
	Block a user
	 Balackburn
					Balackburn