FLEXMethod.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. //
  2. // FLEXMethod.m
  3. // FLEX
  4. //
  5. // Derived from MirrorKit.
  6. // Created by Tanner on 6/30/15.
  7. // Copyright (c) 2015 Tanner Bennett. All rights reserved.
  8. //
  9. #import "FLEXMethod.h"
  10. #import "FLEXMirror.h"
  11. #import "FLEXTypeEncodingParser.h"
  12. #import "FLEXRuntimeUtility.h"
  13. @implementation FLEXMethod
  14. @dynamic implementation;
  15. + (instancetype)buildMethodNamed:(NSString *)name withTypes:(NSString *)typeEncoding implementation:(IMP)implementation {
  16. [NSException raise:NSInternalInconsistencyException format:@"Class instance should not be created with +buildMethodNamed:withTypes:implementation"]; return nil;
  17. }
  18. - (id)init {
  19. [NSException
  20. raise:NSInternalInconsistencyException
  21. format:@"Class instance should not be created with -init"
  22. ];
  23. return nil;
  24. }
  25. #pragma mark Initializers
  26. + (instancetype)method:(Method)method {
  27. return [[self alloc] initWithMethod:method isInstanceMethod:YES];
  28. }
  29. + (instancetype)method:(Method)method isInstanceMethod:(BOOL)isInstanceMethod {
  30. return [[self alloc] initWithMethod:method isInstanceMethod:isInstanceMethod];
  31. }
  32. + (instancetype)selector:(SEL)selector class:(Class)cls {
  33. BOOL instance = !class_isMetaClass(cls);
  34. // class_getInstanceMethod will return an instance method if not given
  35. // not given a metaclass, or a class method if given a metaclass, but
  36. // this isn't documented so we just want to be safe here.
  37. Method m = instance ? class_getInstanceMethod(cls, selector) : class_getClassMethod(cls, selector);
  38. if (m == NULL) return nil;
  39. return [self method:m isInstanceMethod:instance];
  40. }
  41. + (instancetype)selector:(SEL)selector implementedInClass:(Class)cls {
  42. if (![cls superclass]) { return [self selector:selector class:cls]; }
  43. BOOL unique = [cls methodForSelector:selector] != [[cls superclass] methodForSelector:selector];
  44. if (unique) {
  45. return [self selector:selector class:cls];
  46. }
  47. return nil;
  48. }
  49. - (id)initWithMethod:(Method)method isInstanceMethod:(BOOL)isInstanceMethod {
  50. NSParameterAssert(method);
  51. self = [super init];
  52. if (self) {
  53. _objc_method = method;
  54. _isInstanceMethod = isInstanceMethod;
  55. _signatureString = @(method_getTypeEncoding(method) ?: "?@:");
  56. NSString *cleanSig = nil;
  57. if ([FLEXTypeEncodingParser methodTypeEncodingSupported:_signatureString cleaned:&cleanSig]) {
  58. _signature = [NSMethodSignature signatureWithObjCTypes:cleanSig.UTF8String];
  59. }
  60. [self examine];
  61. }
  62. return self;
  63. }
  64. #pragma mark Other
  65. - (NSString *)description {
  66. if (!_flex_description) {
  67. _flex_description = [self prettyName];
  68. }
  69. return _flex_description;
  70. }
  71. - (NSString *)debugNameGivenClassName:(NSString *)name {
  72. NSMutableString *string = [NSMutableString stringWithString:_isInstanceMethod ? @"-[" : @"+["];
  73. [string appendString:name];
  74. [string appendString:@" "];
  75. [string appendString:self.selectorString];
  76. [string appendString:@"]"];
  77. return string;
  78. }
  79. - (NSString *)prettyName {
  80. NSString *methodTypeString = self.isInstanceMethod ? @"-" : @"+";
  81. NSString *readableReturnType = [FLEXRuntimeUtility readableTypeForEncoding:@(self.signature.methodReturnType ?: "")];
  82. NSString *prettyName = [NSString stringWithFormat:@"%@ (%@)", methodTypeString, readableReturnType];
  83. NSArray *components = [self prettyArgumentComponents];
  84. if (components.count) {
  85. return [prettyName stringByAppendingString:[components componentsJoinedByString:@" "]];
  86. } else {
  87. return [prettyName stringByAppendingString:self.selectorString];
  88. }
  89. }
  90. - (NSArray *)prettyArgumentComponents {
  91. // NSMethodSignature can't handle some type encodings
  92. // like ^AI@:ir* which happen to very much exist
  93. if (self.signature.numberOfArguments < self.numberOfArguments) {
  94. return nil;
  95. }
  96. NSMutableArray *components = [NSMutableArray new];
  97. NSArray *selectorComponents = [self.selectorString componentsSeparatedByString:@":"];
  98. NSUInteger numberOfArguments = self.numberOfArguments;
  99. for (NSUInteger argIndex = 2; argIndex < numberOfArguments; argIndex++) {
  100. assert(argIndex < self.signature.numberOfArguments);
  101. const char *argType = [self.signature getArgumentTypeAtIndex:argIndex] ?: "?";
  102. NSString *readableArgType = [FLEXRuntimeUtility readableTypeForEncoding:@(argType)];
  103. NSString *prettyComponent = [NSString
  104. stringWithFormat:@"%@:(%@) ",
  105. selectorComponents[argIndex - 2],
  106. readableArgType
  107. ];
  108. [components addObject:prettyComponent];
  109. }
  110. return components;
  111. }
  112. - (NSString *)debugDescription {
  113. return [NSString stringWithFormat:@"<%@ selector=%@, signature=%@>",
  114. NSStringFromClass(self.class), self.selectorString, self.signatureString];
  115. }
  116. - (void)examine {
  117. _implementation = method_getImplementation(_objc_method);
  118. _selector = method_getName(_objc_method);
  119. _numberOfArguments = method_getNumberOfArguments(_objc_method);
  120. _name = NSStringFromSelector(_selector);
  121. _returnType = (FLEXTypeEncoding *)_signature.methodReturnType;
  122. _returnSize = _signature.methodReturnLength;
  123. }
  124. #pragma mark Public
  125. - (void)setImplementation:(IMP)implementation {
  126. NSParameterAssert(implementation);
  127. method_setImplementation(self.objc_method, implementation);
  128. [self examine];
  129. }
  130. - (NSString *)typeEncoding {
  131. if (!_typeEncoding) {
  132. _typeEncoding = [_signatureString
  133. stringByReplacingOccurrencesOfString:@"[0-9]"
  134. withString:@""
  135. options:NSRegularExpressionSearch
  136. range:NSMakeRange(0, _signatureString.length)
  137. ];
  138. }
  139. return _typeEncoding;
  140. }
  141. #pragma mark Misc
  142. - (void)swapImplementations:(FLEXMethod *)method {
  143. method_exchangeImplementations(self.objc_method, method.objc_method);
  144. [self examine];
  145. [method examine];
  146. }
  147. // Some code borrowed from MAObjcRuntime, by Mike Ash.
  148. - (id)sendMessage:(id)target, ... {
  149. id ret = nil;
  150. va_list args;
  151. va_start(args, target);
  152. switch (self.returnType[0]) {
  153. case FLEXTypeEncodingUnknown: {
  154. [self getReturnValue:NULL forMessageSend:target arguments:args];
  155. break;
  156. }
  157. case FLEXTypeEncodingChar: {
  158. char val = 0;
  159. [self getReturnValue:&val forMessageSend:target arguments:args];
  160. ret = @(val);
  161. break;
  162. }
  163. case FLEXTypeEncodingInt: {
  164. int val = 0;
  165. [self getReturnValue:&val forMessageSend:target arguments:args];
  166. ret = @(val);
  167. break;
  168. }
  169. case FLEXTypeEncodingShort: {
  170. short val = 0;
  171. [self getReturnValue:&val forMessageSend:target arguments:args];
  172. ret = @(val);
  173. break;
  174. }
  175. case FLEXTypeEncodingLong: {
  176. long val = 0;
  177. [self getReturnValue:&val forMessageSend:target arguments:args];
  178. ret = @(val);
  179. break;
  180. }
  181. case FLEXTypeEncodingLongLong: {
  182. long long val = 0;
  183. [self getReturnValue:&val forMessageSend:target arguments:args];
  184. ret = @(val);
  185. break;
  186. }
  187. case FLEXTypeEncodingUnsignedChar: {
  188. unsigned char val = 0;
  189. [self getReturnValue:&val forMessageSend:target arguments:args];
  190. ret = @(val);
  191. break;
  192. }
  193. case FLEXTypeEncodingUnsignedInt: {
  194. unsigned int val = 0;
  195. [self getReturnValue:&val forMessageSend:target arguments:args];
  196. ret = @(val);
  197. break;
  198. }
  199. case FLEXTypeEncodingUnsignedShort: {
  200. unsigned short val = 0;
  201. [self getReturnValue:&val forMessageSend:target arguments:args];
  202. ret = @(val);
  203. break;
  204. }
  205. case FLEXTypeEncodingUnsignedLong: {
  206. unsigned long val = 0;
  207. [self getReturnValue:&val forMessageSend:target arguments:args];
  208. ret = @(val);
  209. break;
  210. }
  211. case FLEXTypeEncodingUnsignedLongLong: {
  212. unsigned long long val = 0;
  213. [self getReturnValue:&val forMessageSend:target arguments:args];
  214. ret = @(val);
  215. break;
  216. }
  217. case FLEXTypeEncodingFloat: {
  218. float val = 0;
  219. [self getReturnValue:&val forMessageSend:target arguments:args];
  220. ret = @(val);
  221. break;
  222. }
  223. case FLEXTypeEncodingDouble: {
  224. double val = 0;
  225. [self getReturnValue:&val forMessageSend:target arguments:args];
  226. ret = @(val);
  227. break;
  228. }
  229. case FLEXTypeEncodingLongDouble: {
  230. long double val = 0;
  231. [self getReturnValue:&val forMessageSend:target arguments:args];
  232. ret = [NSValue value:&val withObjCType:self.returnType];
  233. break;
  234. }
  235. case FLEXTypeEncodingCBool: {
  236. bool val = 0;
  237. [self getReturnValue:&val forMessageSend:target arguments:args];
  238. ret = @(val);
  239. break;
  240. }
  241. case FLEXTypeEncodingVoid: {
  242. [self getReturnValue:NULL forMessageSend:target arguments:args];
  243. return nil;
  244. break;
  245. }
  246. case FLEXTypeEncodingCString: {
  247. char *val = 0;
  248. [self getReturnValue:&val forMessageSend:target arguments:args];
  249. ret = @(val);
  250. break;
  251. }
  252. case FLEXTypeEncodingObjcObject: {
  253. id val = nil;
  254. [self getReturnValue:&val forMessageSend:target arguments:args];
  255. ret = val;
  256. break;
  257. }
  258. case FLEXTypeEncodingObjcClass: {
  259. Class val = Nil;
  260. [self getReturnValue:&val forMessageSend:target arguments:args];
  261. ret = val;
  262. break;
  263. }
  264. case FLEXTypeEncodingSelector: {
  265. SEL val = 0;
  266. [self getReturnValue:&val forMessageSend:target arguments:args];
  267. ret = NSStringFromSelector(val);
  268. break;
  269. }
  270. case FLEXTypeEncodingArrayBegin: {
  271. void *val = 0;
  272. [self getReturnValue:&val forMessageSend:target arguments:args];
  273. ret = [NSValue valueWithBytes:val objCType:self.signature.methodReturnType];
  274. break;
  275. }
  276. case FLEXTypeEncodingUnionBegin:
  277. case FLEXTypeEncodingStructBegin: {
  278. if (self.signature.methodReturnLength) {
  279. void * val = malloc(self.signature.methodReturnLength);
  280. [self getReturnValue:val forMessageSend:target arguments:args];
  281. ret = [NSValue valueWithBytes:val objCType:self.signature.methodReturnType];
  282. } else {
  283. [self getReturnValue:NULL forMessageSend:target arguments:args];
  284. }
  285. break;
  286. }
  287. case FLEXTypeEncodingBitField: {
  288. [self getReturnValue:NULL forMessageSend:target arguments:args];
  289. break;
  290. }
  291. case FLEXTypeEncodingPointer: {
  292. void * val = 0;
  293. [self getReturnValue:&val forMessageSend:target arguments:args];
  294. ret = [NSValue valueWithPointer:val];
  295. break;
  296. }
  297. default: {
  298. [NSException raise:NSInvalidArgumentException
  299. format:@"Unsupported type encoding: %s", (char *)self.returnType];
  300. }
  301. }
  302. va_end(args);
  303. return ret;
  304. }
  305. // Code borrowed from MAObjcRuntime, by Mike Ash.
  306. - (void)getReturnValue:(void *)retPtr forMessageSend:(id)target, ... {
  307. va_list args;
  308. va_start(args, target);
  309. [self getReturnValue:retPtr forMessageSend:target arguments:args];
  310. va_end(args);
  311. }
  312. // Code borrowed from MAObjcRuntime, by Mike Ash.
  313. - (void)getReturnValue:(void *)retPtr forMessageSend:(id)target arguments:(va_list)args {
  314. if (!_signature) {
  315. return;
  316. }
  317. NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:_signature];
  318. NSUInteger argumentCount = _signature.numberOfArguments;
  319. invocation.target = target;
  320. for (NSUInteger i = 2; i < argumentCount; i++) {
  321. int cookie = va_arg(args, int);
  322. if (cookie != FLEXMagicNumber) {
  323. [NSException
  324. raise:NSInternalInconsistencyException
  325. format:@"%s: incorrect magic cookie %08x; make sure you didn't forget "
  326. "any arguments and that all arguments are wrapped in FLEXArg().", __func__, cookie
  327. ];
  328. }
  329. const char *typeString = va_arg(args, char *);
  330. void *argPointer = va_arg(args, void *);
  331. NSUInteger inSize, sigSize;
  332. NSGetSizeAndAlignment(typeString, &inSize, NULL);
  333. NSGetSizeAndAlignment([_signature getArgumentTypeAtIndex:i], &sigSize, NULL);
  334. if (inSize != sigSize) {
  335. [NSException
  336. raise:NSInternalInconsistencyException
  337. format:@"%s:size mismatch between passed-in argument and "
  338. "required argument; in type:%s (%lu) requested:%s (%lu)",
  339. __func__, typeString, (long)inSize, [_signature getArgumentTypeAtIndex:i], (long)sigSize
  340. ];
  341. }
  342. [invocation setArgument:argPointer atIndex:i];
  343. }
  344. // Hack to make NSInvocation invoke the desired implementation
  345. IMP imp = [invocation methodForSelector:NSSelectorFromString(@"invokeUsingIMP:")];
  346. void (*invokeWithIMP)(id, SEL, IMP) = (void *)imp;
  347. invokeWithIMP(invocation, 0, _implementation);
  348. if (_signature.methodReturnLength && retPtr) {
  349. [invocation getReturnValue:retPtr];
  350. }
  351. }
  352. @end
  353. @implementation FLEXMethod (Comparison)
  354. - (NSComparisonResult)compare:(FLEXMethod *)method {
  355. return [self.selectorString compare:method.selectorString];
  356. }
  357. @end