FLEXMethod.m 14 KB

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