mirror of
https://github.com/darlinghq/darling-corefoundation.git
synced 2024-10-07 09:13:26 +00:00
775d287f3c
For some reason, LLDB is really unhappy about NSInvocation's debug info, and refuses to display NSInvocation values (or any values when NSInvocation is in scope). Now, we can at least `po` invocations.
415 lines
10 KiB
Objective-C
415 lines
10 KiB
Objective-C
//
|
|
// NSInvocation.m
|
|
// CoreFoundation
|
|
//
|
|
// Copyright (c) 2014 Apportable. All rights reserved.
|
|
//
|
|
|
|
#import <Foundation/NSArray.h>
|
|
#import <Foundation/NSData.h>
|
|
#import <Foundation/NSException.h>
|
|
#import <Foundation/NSInvocation.h>
|
|
#import <Foundation/NSMethodSignature.h>
|
|
|
|
#import "Block_private.h"
|
|
#import "NSInvocationInternal.h"
|
|
#import "NSObjCRuntimeInternal.h"
|
|
|
|
#import <objc/message.h>
|
|
#import <objc/runtime.h>
|
|
#import <dispatch/dispatch.h>
|
|
|
|
|
|
@implementation NSInvocation
|
|
|
|
+ (void)load
|
|
{
|
|
#if LEGACY_METHOD
|
|
// signature should be @encode(long long) @encode(id) @encode(SEL) @encode(SEL) @encode(marg_list)
|
|
class_addMethod([NSObject class], @selector(forward::), (IMP)&_CF_forwarding_prep_0, "q@::^v");
|
|
#else
|
|
objc_setForwardHandler(&_CF_forwarding_prep_0, &_CF_forwarding_prep_1);
|
|
#endif
|
|
}
|
|
|
|
- (void **)_idxToArg:(NSUInteger)idx
|
|
{
|
|
if (idx == 0)
|
|
{
|
|
return _retdata;
|
|
}
|
|
|
|
NSMethodType *argType = [_signature _argInfo:idx];
|
|
return _frame + argType->offset;
|
|
}
|
|
|
|
// NSInvocation can retain its arguments, including copies of C
|
|
// strings which it places in an NSData. For such strings, this method
|
|
// therefore modifies the frame to use an internal pointer to the
|
|
// NSData rather than to the original string.
|
|
- (void)_retainArgument:(NSUInteger)idx
|
|
{
|
|
NSMethodType *argInfo = [_signature _argInfo: idx];
|
|
const char *type = stripQualifiersAndComments(argInfo->type);
|
|
void **arg = [self _idxToArg: idx];
|
|
|
|
if (type[0] == _C_ID)
|
|
{
|
|
id object = (id) *arg;
|
|
if (object != nil)
|
|
{
|
|
[_container addObject: (id) *arg];
|
|
}
|
|
}
|
|
else if (type[0] == _C_CHARPTR)
|
|
{
|
|
char *str = (char *) *arg;
|
|
if (str != NULL)
|
|
{
|
|
NSUInteger length = strlen(str) + 1;
|
|
NSData *data = [NSData dataWithBytes: str length: length];
|
|
*arg = [data bytes];
|
|
[_container addObject: data];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void) _addAttachedObject: (id) object
|
|
{
|
|
if (_container == nil)
|
|
{
|
|
_container = [[NSMutableArray alloc] init];
|
|
}
|
|
[_container addObject: object];
|
|
}
|
|
|
|
- (instancetype)initWithMethodSignature:(NSMethodSignature *)sig
|
|
{
|
|
if (sig == nil)
|
|
{
|
|
[self release];
|
|
[NSException raise:NSInvalidArgumentException format:@"signature cannot be nil"];
|
|
return nil;
|
|
}
|
|
|
|
self = [super init];
|
|
|
|
if (self)
|
|
{
|
|
_signature = [sig retain];
|
|
|
|
NSUInteger retSize = 0;
|
|
NSGetSizeAndAlignment([_signature methodReturnType], &retSize, NULL);
|
|
retSize = MAX(retSize, RET_SIZE_ARGS);
|
|
_retdata = calloc(retSize + [_signature frameLength], 1);
|
|
_frame = _retdata + retSize;
|
|
|
|
if ([sig _stret])
|
|
{
|
|
// Set up the return value pointer for the objc_msgSend_stret call.
|
|
void **ret = _frame;
|
|
*ret = _retdata;
|
|
}
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)init
|
|
{
|
|
[self release]; // init should not work on NSInvocation
|
|
return nil;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_container release];
|
|
[_signature release];
|
|
free(_retdata);
|
|
[super dealloc];
|
|
}
|
|
|
|
+ (instancetype)invocationWithMethodSignature:(NSMethodSignature *)sig
|
|
{
|
|
return [[[self alloc] initWithMethodSignature:sig] autorelease];
|
|
}
|
|
|
|
- (NSMethodSignature *)methodSignature
|
|
{
|
|
return _signature;
|
|
}
|
|
|
|
- (void)retainArguments
|
|
{
|
|
if (_retainedArgs)
|
|
{
|
|
return;
|
|
}
|
|
_retainedArgs = YES;
|
|
|
|
NSUInteger capacity = [_signature numberOfArguments] + 1; // Add one for return value.
|
|
if (_container == nil)
|
|
{
|
|
_container = [[NSMutableArray alloc] initWithCapacity: capacity];
|
|
}
|
|
|
|
for (NSUInteger idx = 0; idx < capacity; idx++)
|
|
{
|
|
[self _retainArgument:idx];
|
|
}
|
|
}
|
|
|
|
- (BOOL)argumentsRetained
|
|
{
|
|
return _retainedArgs;
|
|
}
|
|
|
|
- (id)target
|
|
{
|
|
id t = nil;
|
|
[self getArgument:&t atIndex:0];
|
|
return t;
|
|
}
|
|
|
|
- (void)setTarget:(id)target
|
|
{
|
|
[self setArgument:&target atIndex:0];
|
|
}
|
|
|
|
- (SEL)selector
|
|
{
|
|
SEL sel;
|
|
[self getArgument:&sel atIndex:1];
|
|
return sel;
|
|
}
|
|
|
|
- (void)setSelector:(SEL)selector
|
|
{
|
|
[self setArgument:&selector atIndex:1];
|
|
}
|
|
|
|
- (void)getReturnValue:(void *)retLoc
|
|
{
|
|
[self getArgument:retLoc atIndex:-1];
|
|
}
|
|
|
|
- (void)setReturnValue:(void *)retLoc
|
|
{
|
|
[self setArgument:retLoc atIndex:-1];
|
|
}
|
|
|
|
- (void)getArgument:(void *)argumentLocation atIndex:(NSInteger)idx
|
|
{
|
|
// idx initially goes like this:
|
|
// -1: return value
|
|
// 0: self
|
|
// 1: _cmd
|
|
// 2+: arguments
|
|
// Thus we add 1 to get an index into _frame.
|
|
idx++;
|
|
|
|
if (idx > [_signature numberOfArguments] || idx < 0)
|
|
{
|
|
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:nil userInfo:nil];
|
|
}
|
|
|
|
NSMethodType *argInfo = [_signature _argInfo:idx];
|
|
void **arg = [self _idxToArg:idx];
|
|
|
|
memcpy(argumentLocation, arg, argInfo->size);
|
|
}
|
|
|
|
- (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx
|
|
{
|
|
// idx initially goes like this:
|
|
// -1: return value
|
|
// 0: self
|
|
// 1: _cmd
|
|
// 2+: arguments
|
|
// Thus we add 1 to get an index into _frame.
|
|
idx++;
|
|
|
|
if (idx > [_signature numberOfArguments] || idx < 0)
|
|
{
|
|
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:nil userInfo:nil];
|
|
}
|
|
|
|
NSMethodType *argInfo = [_signature _argInfo:idx];
|
|
void **arg = [self _idxToArg:idx];
|
|
|
|
memcpy(arg, argumentLocation, argInfo->size);
|
|
|
|
if (_retainedArgs)
|
|
{
|
|
[self _retainArgument:idx];
|
|
}
|
|
}
|
|
|
|
- (void)invoke
|
|
{
|
|
id target = nil;
|
|
[self getArgument:&target atIndex:0];
|
|
|
|
if (target == nil)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static Class NSBlockClass = Nil;
|
|
static dispatch_once_t once = 0L;
|
|
dispatch_once(&once, ^{
|
|
// __NSGlobalBlock -> NSBlock
|
|
NSBlockClass = class_getSuperclass(class_getSuperclass(object_getClass(^{})));
|
|
});
|
|
|
|
BOOL blockClass = NO;
|
|
Class cls = object_getClass(target);
|
|
|
|
while (cls != Nil)
|
|
{
|
|
if (cls == NSBlockClass)
|
|
{
|
|
blockClass = YES;
|
|
}
|
|
cls = class_getSuperclass(cls);
|
|
}
|
|
|
|
char rettype = [_signature methodReturnType][0];
|
|
|
|
if (blockClass)
|
|
{
|
|
struct Block_layout *block_layout = (struct Block_layout *)target;
|
|
__invoke__(block_layout->invoke, _retdata, _frame, [_signature frameLength], rettype);
|
|
}
|
|
else if ([_signature _stret])
|
|
{
|
|
char dummy[RET_SIZE_ARGS];
|
|
__invoke__(&objc_msgSend_stret, &dummy, _frame, [_signature frameLength], rettype);
|
|
}
|
|
else
|
|
{
|
|
__invoke__(&objc_msgSend, _retdata, _frame, [_signature frameLength], rettype);
|
|
}
|
|
|
|
if (_retainedArgs)
|
|
{
|
|
// Retain the return value.
|
|
[self _retainArgument:0];
|
|
}
|
|
}
|
|
|
|
- (void)invokeWithTarget:(id)target
|
|
{
|
|
[self setTarget:target];
|
|
[self invoke];
|
|
}
|
|
|
|
- (NSString *) debugDescription
|
|
{
|
|
CFMutableStringRef description = CFStringCreateMutable(NULL, 0);
|
|
CFStringAppend(description, (CFStringRef) [self description]);
|
|
|
|
for (NSInteger index = -1; index < (NSInteger) [_signature numberOfArguments]; index++)
|
|
{
|
|
CFStringAppend(description, @"\n");
|
|
|
|
switch (index)
|
|
{
|
|
case -1:
|
|
CFStringAppend(description, @"return value");
|
|
break;
|
|
case 0:
|
|
CFStringAppend(description, @"target");
|
|
break;
|
|
case 1:
|
|
CFStringAppend(description, @"selector");
|
|
break;
|
|
default:
|
|
CFStringAppendFormat(description, NULL, @"argument %ld", (long) index);
|
|
break;
|
|
}
|
|
CFStringAppend(description, @": ");
|
|
|
|
NSMethodType *argInfo = [_signature _argInfo: index + 1];
|
|
CFStringAppendFormat(description, NULL, @"{%s} ", argInfo->type);
|
|
|
|
char type = stripQualifiersAndComments(argInfo->type)[0];
|
|
switch (type)
|
|
{
|
|
case _C_VOID:
|
|
CFStringAppend(description, @"void");
|
|
break;
|
|
case _C_CLASS:
|
|
{
|
|
Class class;
|
|
[self getArgument: &class atIndex: index];
|
|
if (class == Nil)
|
|
{
|
|
CFStringAppend(description, @"Nil");
|
|
}
|
|
else
|
|
{
|
|
CFStringAppendFormat(description, NULL, @"%s", class_getName(class));
|
|
}
|
|
break;
|
|
}
|
|
case _C_SEL:
|
|
{
|
|
SEL selector;
|
|
[self getArgument: &selector atIndex: index];
|
|
if (!selector)
|
|
{
|
|
CFStringAppend(description, @"null");
|
|
}
|
|
else
|
|
{
|
|
CFStringAppendFormat(description, NULL, @"%s", sel_getName(selector));
|
|
}
|
|
break;
|
|
}
|
|
|
|
#define HANDLE(_c_type, type, format) \
|
|
case _c_type: \
|
|
{ \
|
|
type value; \
|
|
[self getArgument: &value atIndex: index]; \
|
|
CFStringAppendFormat(description, NULL, format, value); \
|
|
break; \
|
|
}
|
|
|
|
HANDLE(_C_CHR, char, @"%c");
|
|
HANDLE(_C_UCHR, unsigned char, @"%u");
|
|
HANDLE(_C_BOOL, _Bool, @"%d");
|
|
HANDLE(_C_SHT, short, @"%d");
|
|
HANDLE(_C_USHT, unsigned short, @"%u");
|
|
HANDLE(_C_INT, int, @"%d");
|
|
HANDLE(_C_UINT, unsigned int, @"%u");
|
|
HANDLE(_C_LNG, long, @"%ld");
|
|
HANDLE(_C_ULNG, unsigned long, @"%lu");
|
|
HANDLE(_C_LNG_LNG, long long, @"%lld");
|
|
HANDLE(_C_ULNG_LNG, unsigned long long, @"%llu");
|
|
HANDLE(_C_FLT, float, @"%f");
|
|
HANDLE(_C_DBL, double, @"%f");
|
|
HANDLE(_C_CHARPTR, const char *, @"%s");
|
|
HANDLE(_C_ID, id, @"%p");
|
|
HANDLE(_C_PTR, void *, @"%p");
|
|
|
|
#undef HANDLE
|
|
|
|
default:
|
|
{
|
|
const void *ptr = [self _idxToArg: index + 1];
|
|
CFDataRef data = CFDataCreateWithBytesNoCopy(NULL, ptr, argInfo->size, kCFAllocatorNull);
|
|
CFStringAppend(description, (CFStringRef) [data description]);
|
|
CFRelease(data);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CFAutorelease(description);
|
|
return description;
|
|
}
|
|
|
|
@end
|