mirror of
https://github.com/darlinghq/darling-corefoundation.git
synced 2024-10-06 16:53:28 +00:00
494 lines
12 KiB
Objective-C
494 lines
12 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 frame: (void*)frame
|
|
{
|
|
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 (frame) {
|
|
memcpy(_frame, frame, [_signature frameLength]);
|
|
}
|
|
|
|
if ([sig _stret])
|
|
{
|
|
// Set up the return value pointer for the objc_msgSend_stret call.
|
|
void **ret = _frame;
|
|
*ret = _retdata;
|
|
}
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithMethodSignature:(NSMethodSignature *)sig
|
|
{
|
|
return [self _initWithMethodSignature: sig frame: NULL];
|
|
}
|
|
|
|
- (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 frame: (void*)frame
|
|
{
|
|
return [[[self alloc] _initWithMethodSignature: sig frame: frame] autorelease];
|
|
}
|
|
|
|
+ (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];
|
|
}
|
|
}
|
|
|
|
static BOOL isBlock(id object)
|
|
{
|
|
static Class NSBlockClass = Nil;
|
|
static dispatch_once_t once = 0L;
|
|
dispatch_once(&once, ^{
|
|
// __NSGlobalBlock -> NSBlock
|
|
NSBlockClass = class_getSuperclass(class_getSuperclass(object_getClass(^{})));
|
|
});
|
|
|
|
for (
|
|
Class class = object_getClass(object);
|
|
class != Nil;
|
|
class = class_getSuperclass(class)
|
|
)
|
|
{
|
|
if (class == NSBlockClass)
|
|
{
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void) _invokeUsingIMP: (IMP) imp withFrame: (void *) frame
|
|
{
|
|
if ([self target] == nil)
|
|
{
|
|
return;
|
|
}
|
|
|
|
char rettype = [_signature methodReturnType][0];
|
|
|
|
if ([_signature _stret])
|
|
{
|
|
char dummy[RET_SIZE_ARGS];
|
|
__invoke__(imp, &dummy, frame, [_signature frameLength], rettype);
|
|
}
|
|
else
|
|
{
|
|
__invoke__(imp, _retdata, frame, [_signature frameLength], rettype);
|
|
}
|
|
|
|
if (_retainedArgs)
|
|
{
|
|
// Retain the return value.
|
|
[self _retainArgument:0];
|
|
}
|
|
}
|
|
|
|
- (void) invokeUsingIMP: (IMP) imp
|
|
{
|
|
[self _invokeUsingIMP: imp withFrame: _frame];
|
|
}
|
|
|
|
- (void)invoke
|
|
{
|
|
id target = [self target];
|
|
if (target == nil)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IMP imp;
|
|
|
|
if (isBlock([self target]))
|
|
{
|
|
struct Block_layout *block_layout = (struct Block_layout *) target;
|
|
imp = (IMP)block_layout->invoke;
|
|
}
|
|
#if !defined(__arm64__)
|
|
else if ([_signature _stret])
|
|
{
|
|
imp = (IMP)&objc_msgSend_stret;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
imp = &objc_msgSend;
|
|
}
|
|
|
|
[self _invokeUsingIMP: imp withFrame: _frame];
|
|
}
|
|
|
|
- (void) invokeWithTarget: (id) target
|
|
{
|
|
[self setTarget: target];
|
|
[self invoke];
|
|
}
|
|
|
|
- (void) invokeSuper
|
|
{
|
|
id target = [self target];
|
|
if (target == nil)
|
|
{
|
|
return;
|
|
}
|
|
|
|
unsigned char *frameCopy = malloc([_signature frameLength]);
|
|
memcpy(frameCopy, _frame, [_signature frameLength]);
|
|
|
|
struct objc_super super = {
|
|
.receiver = target,
|
|
#ifdef __OBJC2__
|
|
.super_class
|
|
#else
|
|
.class
|
|
#endif
|
|
= class_getSuperclass([target class])
|
|
};
|
|
NSMethodType *argType = [_signature _argInfo: 1];
|
|
*(struct objc_super **) (frameCopy + argType->offset) = &super;
|
|
|
|
#if !defined(__arm64__)
|
|
IMP imp = [_signature _stret] ? &objc_msgSendSuper_stret : &objc_msgSendSuper;
|
|
#else
|
|
IMP imp = &objc_msgSendSuper;
|
|
#endif
|
|
[self _invokeUsingIMP: imp withFrame: frameCopy];
|
|
|
|
free(frameCopy);
|
|
}
|
|
|
|
- (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
|