/ NSInvocation.m
NSInvocation.m
1 // 2 // NSInvocation.m 3 // CoreFoundation 4 // 5 // Copyright (c) 2014 Apportable. All rights reserved. 6 // 7 8 #import <Foundation/NSArray.h> 9 #import <Foundation/NSData.h> 10 #import <Foundation/NSException.h> 11 #import <Foundation/NSInvocation.h> 12 #import <Foundation/NSMethodSignature.h> 13 14 #import "Block_private.h" 15 #import "NSInvocationInternal.h" 16 #import "NSObjCRuntimeInternal.h" 17 18 #import <objc/message.h> 19 #import <objc/runtime.h> 20 #import <dispatch/dispatch.h> 21 22 23 @implementation NSInvocation 24 25 + (void)load 26 { 27 #if LEGACY_METHOD 28 // signature should be @encode(long long) @encode(id) @encode(SEL) @encode(SEL) @encode(marg_list) 29 class_addMethod([NSObject class], @selector(forward::), (IMP)&_CF_forwarding_prep_0, "q@::^v"); 30 #else 31 objc_setForwardHandler(&_CF_forwarding_prep_0, &_CF_forwarding_prep_1); 32 #endif 33 } 34 35 - (void **)_idxToArg:(NSUInteger)idx 36 { 37 if (idx == 0) 38 { 39 return _retdata; 40 } 41 42 NSMethodType *argType = [_signature _argInfo:idx]; 43 return _frame + argType->offset; 44 } 45 46 // NSInvocation can retain its arguments, including copies of C 47 // strings which it places in an NSData. For such strings, this method 48 // therefore modifies the frame to use an internal pointer to the 49 // NSData rather than to the original string. 50 - (void)_retainArgument:(NSUInteger)idx 51 { 52 NSMethodType *argInfo = [_signature _argInfo: idx]; 53 const char *type = stripQualifiersAndComments(argInfo->type); 54 void **arg = [self _idxToArg: idx]; 55 56 if (type[0] == _C_ID) 57 { 58 id object = (id) *arg; 59 if (object != nil) 60 { 61 [_container addObject: (id) *arg]; 62 } 63 } 64 else if (type[0] == _C_CHARPTR) 65 { 66 char *str = (char *) *arg; 67 if (str != NULL) 68 { 69 NSUInteger length = strlen(str) + 1; 70 NSData *data = [NSData dataWithBytes: str length: length]; 71 *arg = [data bytes]; 72 [_container addObject: data]; 73 } 74 } 75 } 76 77 - (void) _addAttachedObject: (id) object 78 { 79 if (_container == nil) 80 { 81 _container = [[NSMutableArray alloc] init]; 82 } 83 [_container addObject: object]; 84 } 85 86 - (instancetype)_initWithMethodSignature: (NSMethodSignature*)sig frame: (void*)frame 87 { 88 if (sig == nil) 89 { 90 [self release]; 91 [NSException raise:NSInvalidArgumentException format:@"signature cannot be nil"]; 92 return nil; 93 } 94 95 self = [super init]; 96 97 if (self) 98 { 99 _signature = [sig retain]; 100 101 NSUInteger retSize = 0; 102 NSGetSizeAndAlignment([_signature methodReturnType], &retSize, NULL); 103 retSize = MAX(retSize, RET_SIZE_ARGS); 104 _retdata = calloc(retSize + [_signature frameLength], 1); 105 _frame = _retdata + retSize; 106 107 if (frame) { 108 memcpy(_frame, frame, [_signature frameLength]); 109 } 110 111 if ([sig _stret]) 112 { 113 // Set up the return value pointer for the objc_msgSend_stret call. 114 void **ret = _frame; 115 *ret = _retdata; 116 } 117 } 118 119 return self; 120 } 121 122 - (instancetype)initWithMethodSignature:(NSMethodSignature *)sig 123 { 124 return [self _initWithMethodSignature: sig frame: NULL]; 125 } 126 127 - (instancetype)init 128 { 129 [self release]; // init should not work on NSInvocation 130 return nil; 131 } 132 133 - (void)dealloc 134 { 135 [_container release]; 136 [_signature release]; 137 free(_retdata); 138 [super dealloc]; 139 } 140 141 142 + (instancetype)_invocationWithMethodSignature: (NSMethodSignature*)sig frame: (void*)frame 143 { 144 return [[[self alloc] _initWithMethodSignature: sig frame: frame] autorelease]; 145 } 146 147 + (instancetype)invocationWithMethodSignature:(NSMethodSignature *)sig 148 { 149 return [[[self alloc] initWithMethodSignature:sig] autorelease]; 150 } 151 152 - (NSMethodSignature *)methodSignature 153 { 154 return _signature; 155 } 156 157 - (void)retainArguments 158 { 159 if (_retainedArgs) 160 { 161 return; 162 } 163 _retainedArgs = YES; 164 165 NSUInteger capacity = [_signature numberOfArguments] + 1; // Add one for return value. 166 if (_container == nil) 167 { 168 _container = [[NSMutableArray alloc] initWithCapacity: capacity]; 169 } 170 171 for (NSUInteger idx = 0; idx < capacity; idx++) 172 { 173 [self _retainArgument:idx]; 174 } 175 } 176 177 - (BOOL)argumentsRetained 178 { 179 return _retainedArgs; 180 } 181 182 - (id)target 183 { 184 id t = nil; 185 [self getArgument:&t atIndex:0]; 186 return t; 187 } 188 189 - (void)setTarget:(id)target 190 { 191 [self setArgument:&target atIndex:0]; 192 } 193 194 - (SEL)selector 195 { 196 SEL sel; 197 [self getArgument:&sel atIndex:1]; 198 return sel; 199 } 200 201 - (void)setSelector:(SEL)selector 202 { 203 [self setArgument:&selector atIndex:1]; 204 } 205 206 - (void)getReturnValue:(void *)retLoc 207 { 208 [self getArgument:retLoc atIndex:-1]; 209 } 210 211 - (void)setReturnValue:(void *)retLoc 212 { 213 [self setArgument:retLoc atIndex:-1]; 214 } 215 216 - (void)getArgument:(void *)argumentLocation atIndex:(NSInteger)idx 217 { 218 // idx initially goes like this: 219 // -1: return value 220 // 0: self 221 // 1: _cmd 222 // 2+: arguments 223 // Thus we add 1 to get an index into _frame. 224 idx++; 225 226 if (idx > [_signature numberOfArguments] || idx < 0) 227 { 228 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:nil userInfo:nil]; 229 } 230 231 NSMethodType *argInfo = [_signature _argInfo:idx]; 232 void **arg = [self _idxToArg:idx]; 233 234 memcpy(argumentLocation, arg, argInfo->size); 235 } 236 237 - (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx 238 { 239 // idx initially goes like this: 240 // -1: return value 241 // 0: self 242 // 1: _cmd 243 // 2+: arguments 244 // Thus we add 1 to get an index into _frame. 245 idx++; 246 247 if (idx > [_signature numberOfArguments] || idx < 0) 248 { 249 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:nil userInfo:nil]; 250 } 251 252 NSMethodType *argInfo = [_signature _argInfo:idx]; 253 void **arg = [self _idxToArg:idx]; 254 255 memcpy(arg, argumentLocation, argInfo->size); 256 257 if (_retainedArgs) 258 { 259 [self _retainArgument:idx]; 260 } 261 } 262 263 static BOOL isBlock(id object) 264 { 265 static Class NSBlockClass = Nil; 266 static dispatch_once_t once = 0L; 267 dispatch_once(&once, ^{ 268 // __NSGlobalBlock -> NSBlock 269 NSBlockClass = class_getSuperclass(class_getSuperclass(object_getClass(^{}))); 270 }); 271 272 for ( 273 Class class = object_getClass(object); 274 class != Nil; 275 class = class_getSuperclass(class) 276 ) 277 { 278 if (class == NSBlockClass) 279 { 280 return YES; 281 } 282 } 283 284 return NO; 285 } 286 287 - (void) _invokeUsingIMP: (IMP) imp withFrame: (void *) frame 288 { 289 if ([self target] == nil) 290 { 291 return; 292 } 293 294 char rettype = [_signature methodReturnType][0]; 295 296 if ([_signature _stret]) 297 { 298 char dummy[RET_SIZE_ARGS]; 299 __invoke__(imp, &dummy, frame, [_signature frameLength], rettype); 300 } 301 else 302 { 303 __invoke__(imp, _retdata, frame, [_signature frameLength], rettype); 304 } 305 306 if (_retainedArgs) 307 { 308 // Retain the return value. 309 [self _retainArgument:0]; 310 } 311 } 312 313 - (void) invokeUsingIMP: (IMP) imp 314 { 315 [self _invokeUsingIMP: imp withFrame: _frame]; 316 } 317 318 - (void)invoke 319 { 320 id target = [self target]; 321 if (target == nil) 322 { 323 return; 324 } 325 326 IMP imp; 327 328 if (isBlock([self target])) 329 { 330 struct Block_layout *block_layout = (struct Block_layout *) target; 331 imp = (IMP)block_layout->invoke; 332 } 333 #if !defined(__arm64__) 334 else if ([_signature _stret]) 335 { 336 imp = (IMP)&objc_msgSend_stret; 337 } 338 #endif 339 else 340 { 341 imp = &objc_msgSend; 342 } 343 344 [self _invokeUsingIMP: imp withFrame: _frame]; 345 } 346 347 - (void) invokeWithTarget: (id) target 348 { 349 [self setTarget: target]; 350 [self invoke]; 351 } 352 353 - (void) invokeSuper 354 { 355 id target = [self target]; 356 if (target == nil) 357 { 358 return; 359 } 360 361 unsigned char *frameCopy = malloc([_signature frameLength]); 362 memcpy(frameCopy, _frame, [_signature frameLength]); 363 364 struct objc_super super = { 365 .receiver = target, 366 #ifdef __OBJC2__ 367 .super_class 368 #else 369 .class 370 #endif 371 = class_getSuperclass([target class]) 372 }; 373 NSMethodType *argType = [_signature _argInfo: 1]; 374 *(struct objc_super **) (frameCopy + argType->offset) = &super; 375 376 #if !defined(__arm64__) 377 IMP imp = [_signature _stret] ? &objc_msgSendSuper_stret : &objc_msgSendSuper; 378 #else 379 IMP imp = &objc_msgSendSuper; 380 #endif 381 [self _invokeUsingIMP: imp withFrame: frameCopy]; 382 383 free(frameCopy); 384 } 385 386 - (NSString *) debugDescription 387 { 388 CFMutableStringRef description = CFStringCreateMutable(NULL, 0); 389 CFStringAppend(description, (CFStringRef) [self description]); 390 391 for (NSInteger index = -1; index < (NSInteger) [_signature numberOfArguments]; index++) 392 { 393 CFStringAppend(description, @"\n"); 394 395 switch (index) 396 { 397 case -1: 398 CFStringAppend(description, @"return value"); 399 break; 400 case 0: 401 CFStringAppend(description, @"target"); 402 break; 403 case 1: 404 CFStringAppend(description, @"selector"); 405 break; 406 default: 407 CFStringAppendFormat(description, NULL, @"argument %ld", (long) index); 408 break; 409 } 410 CFStringAppend(description, @": "); 411 412 NSMethodType *argInfo = [_signature _argInfo: index + 1]; 413 CFStringAppendFormat(description, NULL, @"{%s} ", argInfo->type); 414 415 char type = stripQualifiersAndComments(argInfo->type)[0]; 416 switch (type) 417 { 418 case _C_VOID: 419 CFStringAppend(description, @"void"); 420 break; 421 case _C_CLASS: 422 { 423 Class class; 424 [self getArgument: &class atIndex: index]; 425 if (class == Nil) 426 { 427 CFStringAppend(description, @"Nil"); 428 } 429 else 430 { 431 CFStringAppendFormat(description, NULL, @"%s", class_getName(class)); 432 } 433 break; 434 } 435 case _C_SEL: 436 { 437 SEL selector; 438 [self getArgument: &selector atIndex: index]; 439 if (!selector) 440 { 441 CFStringAppend(description, @"null"); 442 } 443 else 444 { 445 CFStringAppendFormat(description, NULL, @"%s", sel_getName(selector)); 446 } 447 break; 448 } 449 450 #define HANDLE(_c_type, type, format) \ 451 case _c_type: \ 452 { \ 453 type value; \ 454 [self getArgument: &value atIndex: index]; \ 455 CFStringAppendFormat(description, NULL, format, value); \ 456 break; \ 457 } 458 459 HANDLE(_C_CHR, char, @"%c"); 460 HANDLE(_C_UCHR, unsigned char, @"%u"); 461 HANDLE(_C_BOOL, _Bool, @"%d"); 462 HANDLE(_C_SHT, short, @"%d"); 463 HANDLE(_C_USHT, unsigned short, @"%u"); 464 HANDLE(_C_INT, int, @"%d"); 465 HANDLE(_C_UINT, unsigned int, @"%u"); 466 HANDLE(_C_LNG, long, @"%ld"); 467 HANDLE(_C_ULNG, unsigned long, @"%lu"); 468 HANDLE(_C_LNG_LNG, long long, @"%lld"); 469 HANDLE(_C_ULNG_LNG, unsigned long long, @"%llu"); 470 HANDLE(_C_FLT, float, @"%f"); 471 HANDLE(_C_DBL, double, @"%f"); 472 HANDLE(_C_CHARPTR, const char *, @"%s"); 473 HANDLE(_C_ID, id, @"%p"); 474 HANDLE(_C_PTR, void *, @"%p"); 475 476 #undef HANDLE 477 478 default: 479 { 480 const void *ptr = [self _idxToArg: index + 1]; 481 CFDataRef data = CFDataCreateWithBytesNoCopy(NULL, ptr, argInfo->size, kCFAllocatorNull); 482 CFStringAppend(description, (CFStringRef) [data description]); 483 CFRelease(data); 484 break; 485 } 486 } 487 } 488 489 CFAutorelease(description); 490 return description; 491 } 492 493 @end