FLEXMethod.m 13 KB

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