TBRuntimeController.m 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. //
  2. // TBRuntimeController.m
  3. // TBTweakViewController
  4. //
  5. // Created by Tanner on 3/23/17.
  6. // Copyright © 2017 Tanner Bennett. All rights reserved.
  7. //
  8. #import "TBRuntimeController.h"
  9. #import "TBRuntime.h"
  10. #import "FLEXMethod.h"
  11. @interface TBRuntimeController ()
  12. @property (nonatomic, readonly) NSCache *bundlePathsCache;
  13. @property (nonatomic, readonly) NSCache *bundleNamesCache;
  14. @property (nonatomic, readonly) NSCache *classNamesCache;
  15. @property (nonatomic, readonly) NSCache *methodsCache;
  16. @end
  17. @implementation TBRuntimeController
  18. #pragma mark Initialization
  19. static TBRuntimeController *controller = nil;
  20. + (instancetype)shared {
  21. static dispatch_once_t onceToken;
  22. dispatch_once(&onceToken, ^{
  23. controller = [self new];
  24. });
  25. return controller;
  26. }
  27. - (id)init {
  28. self = [super init];
  29. if (self) {
  30. _bundlePathsCache = [NSCache new];
  31. _bundleNamesCache = [NSCache new];
  32. _classNamesCache = [NSCache new];
  33. _methodsCache = [NSCache new];
  34. }
  35. return self;
  36. }
  37. #pragma mark Public
  38. + (NSArray *)dataForKeyPath:(TBKeyPath *)keyPath {
  39. if (keyPath.bundleKey) {
  40. if (keyPath.classKey) {
  41. if (keyPath.methodKey) {
  42. return [[self shared] methodsForKeyPath:keyPath];
  43. } else {
  44. return [[self shared] classesForClassToken:keyPath.classKey andBundleToken:keyPath.bundleKey];
  45. }
  46. } else {
  47. return [[self shared] bundleNamesForToken:keyPath.bundleKey];
  48. }
  49. } else {
  50. return @[];
  51. }
  52. }
  53. + (NSDictionary *)methodsForToken:(TBToken *)token
  54. instance:(NSNumber *)inst
  55. inClasses:(NSArray<NSString*> *)classes {
  56. NSMutableDictionary *methods = [NSMutableDictionary dictionary];
  57. for (NSString *className in classes) {
  58. NSMutableArray *target = [NSMutableArray arrayWithObject:className];
  59. methods[className] = [[TBRuntime runtime] methodsForToken:token
  60. instance:inst
  61. inClasses:target];
  62. }
  63. return methods;
  64. }
  65. + (NSString *)shortBundleNameForClass:(NSString *)name {
  66. NSString *imagePath = @(class_getImageName(NSClassFromString(name)));
  67. return [[TBRuntime runtime] shortNameForImageName:imagePath];
  68. }
  69. + (NSArray *)allBundleNames {
  70. return [TBRuntime runtime].imageDisplayNames;
  71. }
  72. #pragma mark Private
  73. - (NSMutableArray *)bundlePathsForToken:(TBToken *)token {
  74. // Only cache if no wildcard
  75. BOOL shouldCache = token == TBWildcardOptionsNone;
  76. if (shouldCache) {
  77. NSMutableArray<NSString*> *cached = [self.bundlePathsCache objectForKey:token];
  78. if (cached) {
  79. return cached;
  80. }
  81. NSMutableArray<NSString*> *bundles = [[TBRuntime runtime] bundlePathsForToken:token];
  82. [self.bundlePathsCache setObject:bundles forKey:token];
  83. return bundles;
  84. }
  85. else {
  86. return [[TBRuntime runtime] bundlePathsForToken:token];
  87. }
  88. }
  89. - (NSMutableArray<NSString*> *)bundleNamesForToken:(TBToken *)token {
  90. // Only cache if no wildcard
  91. BOOL shouldCache = token == TBWildcardOptionsNone;
  92. if (shouldCache) {
  93. NSMutableArray<NSString*> *cached = [self.bundleNamesCache objectForKey:token];
  94. if (cached) {
  95. return cached;
  96. }
  97. NSMutableArray<NSString*> *bundles = [[TBRuntime runtime] bundleNamesForToken:token];
  98. [self.bundleNamesCache setObject:bundles forKey:token];
  99. return bundles;
  100. }
  101. else {
  102. return [[TBRuntime runtime] bundleNamesForToken:token];
  103. }
  104. }
  105. - (NSMutableArray<NSString*> *)classesForClassToken:(TBToken *)clsToken andBundleToken:(TBToken *)bundleToken {
  106. // Only cache if no wildcard
  107. BOOL shouldCache = bundleToken.options == 0 && clsToken.options == 0;
  108. NSString *key = nil;
  109. if (shouldCache) {
  110. key = [@[bundleToken.description, clsToken.description] componentsJoinedByString:@"+"];
  111. NSMutableArray<NSString*> *cached = [self.classNamesCache objectForKey:key];
  112. if (cached) {
  113. return cached;
  114. }
  115. }
  116. NSMutableArray<NSString*> *bundles = [self bundlePathsForToken:bundleToken];
  117. NSMutableArray<NSString*> *classes = [[TBRuntime runtime] classesForToken:clsToken inBundles:bundles];
  118. if (shouldCache) {
  119. [self.classNamesCache setObject:classes forKey:key];
  120. }
  121. return classes;
  122. }
  123. - (NSMutableArray<FLEXMethod*> *)methodsForKeyPath:(TBKeyPath *)keyPath {
  124. // Only cache if no wildcard, but check cache anyway bc I'm lazy
  125. NSMutableArray<FLEXMethod*> *cached = [self.methodsCache objectForKey:keyPath];
  126. if (cached) {
  127. return cached;
  128. }
  129. NSMutableArray<NSString*> *classes = [self classesForClassToken:keyPath.classKey andBundleToken:keyPath.bundleKey];
  130. NSMutableArray<FLEXMethod*> *methods = [[TBRuntime runtime] methodsForToken:keyPath.methodKey
  131. instance:keyPath.instanceMethods
  132. inClasses:classes];
  133. [methods sortUsingComparator:^NSComparisonResult(FLEXMethod *m1, FLEXMethod *m2) {
  134. return [m1.description caseInsensitiveCompare:m2.description];
  135. }];
  136. // Only cache if no wildcard
  137. if (keyPath.bundleKey.isAbsolute &&
  138. keyPath.classKey.isAbsolute) {
  139. [self.methodsCache setObject:methods forKey:keyPath];
  140. }
  141. return methods;
  142. }
  143. @end