FLEXMethod.m 14 KB

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