FLEXMethod.m 14 KB

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