/ 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