FLEXProtocol.m 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. //
  2. // FLEXProtocol.m
  3. // FLEX
  4. //
  5. // Derived from MirrorKit.
  6. // Created by Tanner on 6/30/15.
  7. // Copyright (c) 2020 FLEX Team. All rights reserved.
  8. //
  9. #import "FLEXProtocol.h"
  10. #import "FLEXProperty.h"
  11. #import "FLEXRuntimeUtility.h"
  12. #import "NSArray+FLEX.h"
  13. #include <dlfcn.h>
  14. @implementation FLEXProtocol
  15. - (id)init {
  16. [NSException
  17. raise:NSInternalInconsistencyException
  18. format:@"Class instance should not be created with -init"
  19. ];
  20. return nil;
  21. }
  22. #pragma mark Initializers
  23. + (NSArray *)allProtocols {
  24. unsigned int prcount;
  25. Protocol *__unsafe_unretained*protocols = objc_copyProtocolList(&prcount);
  26. NSMutableArray *all = [NSMutableArray new];
  27. for(NSUInteger i = 0; i < prcount; i++)
  28. [all addObject:[self protocol:protocols[i]]];
  29. free(protocols);
  30. return all;
  31. }
  32. + (instancetype)protocol:(Protocol *)protocol {
  33. return [[self alloc] initWithProtocol:protocol];
  34. }
  35. - (id)initWithProtocol:(Protocol *)protocol {
  36. NSParameterAssert(protocol);
  37. self = [super init];
  38. if (self) {
  39. _objc_protocol = protocol;
  40. [self examine];
  41. }
  42. return self;
  43. }
  44. #pragma mark Other
  45. - (NSString *)description {
  46. return self.name;
  47. }
  48. - (NSString *)debugDescription {
  49. return [NSString stringWithFormat:@"<%@ name=%@, %lu properties, %lu required methods, %lu optional methods, %lu protocols>",
  50. NSStringFromClass(self.class), self.name, (unsigned long)self.properties.count,
  51. (unsigned long)self.requiredMethods.count, (unsigned long)self.optionalMethods.count, (unsigned long)self.protocols.count];
  52. }
  53. - (void)examine {
  54. _name = @(protocol_getName(self.objc_protocol));
  55. // imagePath
  56. Dl_info exeInfo;
  57. if (dladdr((__bridge const void *)(_objc_protocol), &exeInfo)) {
  58. _imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : nil;
  59. }
  60. // Conformances and methods //
  61. unsigned int pccount, mdrcount, mdocount;
  62. struct objc_method_description *objcrMethods, *objcoMethods;
  63. Protocol *protocol = _objc_protocol;
  64. Protocol * __unsafe_unretained *protocols = protocol_copyProtocolList(protocol, &pccount);
  65. // Protocols
  66. _protocols = [NSArray flex_forEachUpTo:pccount map:^id(NSUInteger i) {
  67. return [FLEXProtocol protocol:protocols[i]];
  68. }];
  69. free(protocols);
  70. // Required instance methods
  71. objcrMethods = protocol_copyMethodDescriptionList(protocol, YES, YES, &mdrcount);
  72. NSArray *rMethods = [NSArray flex_forEachUpTo:mdrcount map:^id(NSUInteger i) {
  73. return [FLEXMethodDescription description:objcrMethods[i] instance:YES];
  74. }];
  75. free(objcrMethods);
  76. // Required class methods
  77. objcrMethods = protocol_copyMethodDescriptionList(protocol, YES, NO, &mdrcount);
  78. _requiredMethods = [[NSArray flex_forEachUpTo:mdrcount map:^id(NSUInteger i) {
  79. return [FLEXMethodDescription description:objcrMethods[i] instance:NO];
  80. }] arrayByAddingObjectsFromArray:rMethods];
  81. free(objcrMethods);
  82. // Optional instance methods
  83. objcoMethods = protocol_copyMethodDescriptionList(protocol, NO, YES, &mdocount);
  84. NSArray *oMethods = [NSArray flex_forEachUpTo:mdocount map:^id(NSUInteger i) {
  85. return [FLEXMethodDescription description:objcoMethods[i] instance:YES];
  86. }];
  87. free(objcoMethods);
  88. // Optional class methods
  89. objcoMethods = protocol_copyMethodDescriptionList(protocol, NO, NO, &mdocount);
  90. _optionalMethods = [[NSArray flex_forEachUpTo:mdocount map:^id(NSUInteger i) {
  91. return [FLEXMethodDescription description:objcoMethods[i] instance:NO];
  92. }] arrayByAddingObjectsFromArray:oMethods];
  93. free(objcoMethods);
  94. // Properties is a hassle because they didn't fix the API until iOS 10 //
  95. if (@available(iOS 10.0, *)) {
  96. unsigned int prrcount, procount;
  97. Class instance = [NSObject class], meta = objc_getMetaClass("NSObject");
  98. // Required class and instance properties //
  99. // Instance first
  100. objc_property_t *rProps = protocol_copyPropertyList2(protocol, &prrcount, YES, YES);
  101. NSArray *rProperties = [NSArray flex_forEachUpTo:prrcount map:^id(NSUInteger i) {
  102. return [FLEXProperty property:rProps[i] onClass:instance];
  103. }];
  104. free(rProps);
  105. // Then class
  106. rProps = protocol_copyPropertyList2(protocol, &prrcount, NO, YES);
  107. _requiredProperties = [[NSArray flex_forEachUpTo:prrcount map:^id(NSUInteger i) {
  108. return [FLEXProperty property:rProps[i] onClass:instance];
  109. }] arrayByAddingObjectsFromArray:rProperties];
  110. free(rProps);
  111. // Optional class and instance properties //
  112. // Instance first
  113. objc_property_t *oProps = protocol_copyPropertyList2(protocol, &procount, YES, YES);
  114. NSArray *oProperties = [NSArray flex_forEachUpTo:prrcount map:^id(NSUInteger i) {
  115. return [FLEXProperty property:oProps[i] onClass:meta];
  116. }];
  117. free(oProps);
  118. // Then class
  119. oProps = protocol_copyPropertyList2(protocol, &procount, NO, YES);
  120. _optionalProperties = [[NSArray flex_forEachUpTo:procount map:^id(NSUInteger i) {
  121. return [FLEXProperty property:oProps[i] onClass:meta];
  122. }] arrayByAddingObjectsFromArray:oProperties];
  123. free(oProps);
  124. } else {
  125. unsigned int prcount;
  126. objc_property_t *objcproperties = protocol_copyPropertyList(protocol, &prcount);
  127. _properties = [NSArray flex_forEachUpTo:prcount map:^id(NSUInteger i) {
  128. return [FLEXProperty property:objcproperties[i]];
  129. }];
  130. free(objcproperties);
  131. }
  132. }
  133. - (BOOL)conformsTo:(Protocol *)protocol {
  134. return protocol_conformsToProtocol(self.objc_protocol, protocol);
  135. }
  136. @end
  137. #pragma mark FLEXMethodDescription
  138. @implementation FLEXMethodDescription
  139. - (id)init {
  140. [NSException
  141. raise:NSInternalInconsistencyException
  142. format:@"Class instance should not be created with -init"
  143. ];
  144. return nil;
  145. }
  146. + (instancetype)description:(struct objc_method_description)description {
  147. return [[self alloc] initWithDescription:description instance:nil];
  148. }
  149. + (instancetype)description:(struct objc_method_description)description instance:(BOOL)isInstance {
  150. return [[self alloc] initWithDescription:description instance:@(isInstance)];
  151. }
  152. - (id)initWithDescription:(struct objc_method_description)md instance:(NSNumber *)instance {
  153. NSParameterAssert(md.name != NULL);
  154. self = [super init];
  155. if (self) {
  156. _objc_description = md;
  157. _selector = md.name;
  158. _typeEncoding = @(md.types);
  159. _returnType = (FLEXTypeEncoding)[self.typeEncoding characterAtIndex:0];
  160. _instance = instance;
  161. }
  162. return self;
  163. }
  164. - (NSString *)description {
  165. return NSStringFromSelector(self.selector);
  166. }
  167. - (NSString *)debugDescription {
  168. return [NSString stringWithFormat:@"<%@ name=%@, type=%@>",
  169. NSStringFromClass(self.class), NSStringFromSelector(self.selector), self.typeEncoding];
  170. }
  171. @end