mirror of
https://github.com/darlinghq/darling-corefoundation.git
synced 2025-02-17 02:17:42 +00:00
Implement block forwarding
This commit is contained in:
parent
8e68fa7bf5
commit
1e120e065d
@ -95,6 +95,8 @@ LStretContinue:
|
||||
|
||||
.fnend
|
||||
|
||||
#error TODO: Implement __CF_forwarding_prep_b for ARM
|
||||
|
||||
#elif __i386__
|
||||
|
||||
.text
|
||||
@ -175,6 +177,28 @@ __CF_forwarding_prep_1:
|
||||
LStretSuccess:
|
||||
ret $4 // return value in stret pointer; return
|
||||
|
||||
.text
|
||||
.global __CF_forwarding_prep_b
|
||||
.align 4, 0x90
|
||||
|
||||
// NOTE: i have not checked this implemention (it's kind of hacked together based on the other two `forwarding_prep`s)
|
||||
// i tried to test it, but blocks in general are broken on i386 at the moment (actually, the root issue is that setjmp is broken on i386)
|
||||
__CF_forwarding_prep_b:
|
||||
push %ebp
|
||||
mov %esp, %ebp // create a stack frame
|
||||
|
||||
sub $0x04, %esp // reserve space for args pointer
|
||||
and $-16, %esp // align stack
|
||||
|
||||
lea 8(%ebp), %eax // load marg_list, skipping frame pointer and return address
|
||||
mov %eax, (%esp) // pass marg_list as first argument
|
||||
|
||||
call ___block_forwarding__ // call through
|
||||
|
||||
mov %ebp, %esp // restore stack
|
||||
pop %ebp // pop stack frame
|
||||
ret // return
|
||||
|
||||
#elif __x86_64__
|
||||
|
||||
.section __TEXT,__text,regular,pure_instructions
|
||||
@ -259,6 +283,46 @@ Lfail:
|
||||
|
||||
.cfi_endproc
|
||||
|
||||
.globl __CF_forwarding_prep_b
|
||||
.align 4, 0x90
|
||||
|
||||
__CF_forwarding_prep_b:
|
||||
.cfi_startproc
|
||||
.cfi_personality 155, ___objc_personality_v0 // not sure if this personality is correct for this function
|
||||
push %rbp
|
||||
.cfi_def_cfa_offset 16
|
||||
.cfi_offset %rbp, -16
|
||||
movq %rsp, %rbp
|
||||
.cfi_def_cfa_register rbp
|
||||
|
||||
// Copy args from regs into a stack var
|
||||
subq $0xd0, %rsp
|
||||
movq %rax, 0xb0(%rsp)
|
||||
movapd %xmm7, 0xa0(%rsp)
|
||||
movapd %xmm6, 0x90(%rsp)
|
||||
movapd %xmm5, 0x80(%rsp)
|
||||
movapd %xmm4, 0x70(%rsp)
|
||||
movapd %xmm3, 0x60(%rsp)
|
||||
movapd %xmm2, 0x50(%rsp)
|
||||
movapd %xmm1, 0x40(%rsp)
|
||||
movapd %xmm0, 0x30(%rsp)
|
||||
movq %r9, 0x28(%rsp)
|
||||
movq %r8, 0x20(%rsp)
|
||||
movq %rcx, 0x18(%rsp)
|
||||
movq %rdx, 0x10(%rsp)
|
||||
movq %rsi, 8(%rsp)
|
||||
movq %rdi, (%rsp)
|
||||
|
||||
// call into the actual forwarder
|
||||
movq %rsp, %rdi
|
||||
call ___block_forwarding__
|
||||
|
||||
movq %rbp, %rsp
|
||||
pop %rbp
|
||||
ret
|
||||
|
||||
.cfi_endproc
|
||||
|
||||
#else
|
||||
#error Missing forwarding prep handlers for this arch https://code.google.com/p/apportable/issues/detail?id=619
|
||||
#endif
|
||||
|
@ -161,6 +161,7 @@ set(cf_sources
|
||||
FoundationExceptions.m
|
||||
NSRunLoopModes.m
|
||||
NSFileSecurity.m
|
||||
NSBlockInvocation.m
|
||||
)
|
||||
|
||||
add_separated_framework(CoreFoundation
|
||||
|
71
NSBlockInvocation.m
Normal file
71
NSBlockInvocation.m
Normal file
@ -0,0 +1,71 @@
|
||||
#import <Foundation/NSInvocation.h>
|
||||
|
||||
#import "NSBlockInvocationInternal.h"
|
||||
|
||||
@implementation NSBlockInvocation
|
||||
|
||||
- (SEL)selector
|
||||
{
|
||||
[self doesNotRecognizeSelector: _cmd];
|
||||
}
|
||||
|
||||
- (void)setSelector: (SEL)selector
|
||||
{
|
||||
[self doesNotRecognizeSelector: _cmd];
|
||||
}
|
||||
|
||||
- (void)invokeSuper
|
||||
{
|
||||
[self doesNotRecognizeSelector: _cmd];
|
||||
}
|
||||
|
||||
- (void)invokeUsingIMP: (IMP)implementation
|
||||
{
|
||||
[self doesNotRecognizeSelector: _cmd];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
struct Block_descriptor_full {
|
||||
struct Block_descriptor_1 desc1;
|
||||
struct Block_descriptor_2 desc2;
|
||||
struct Block_descriptor_3 desc3;
|
||||
};
|
||||
|
||||
struct NSProxyBlockFull {
|
||||
struct NSProxyBlock block;
|
||||
struct Block_descriptor_full descs;
|
||||
char signature[];
|
||||
};
|
||||
|
||||
id __NSMakeSpecialForwardingCaptureBlock(const char* signature, void (^proxyBlock)(NSBlockInvocation*)) {
|
||||
// dummy block we can use to borrow certain details we need for our custom block
|
||||
void (^dummyBlock)(void) = ^{
|
||||
[proxyBlock self];
|
||||
};
|
||||
size_t signatureLength = strlen(signature);
|
||||
struct Block_layout* dummy = (void*)dummyBlock;
|
||||
struct Block_descriptor_full* dummyDescs = dummy->descriptor;
|
||||
struct NSProxyBlockFull* custom = calloc(sizeof(struct NSProxyBlockFull) + signatureLength + 1, 1);
|
||||
|
||||
custom->block.blockInternal.isa = _NSConcreteMallocBlock;
|
||||
custom->block.blockInternal.flags = BLOCK_NEEDS_FREE | BLOCK_HAS_SIGNATURE | BLOCK_HAS_COPY_DISPOSE | (1 << 1);
|
||||
custom->block.blockInternal.invoke = _CF_forwarding_prep_b;
|
||||
custom->block.blockInternal.descriptor = &custom->descs.desc1;
|
||||
|
||||
custom->block.proxy = proxyBlock;
|
||||
|
||||
custom->descs.desc1.size = sizeof(struct NSProxyBlock);
|
||||
|
||||
custom->descs.desc2.copy = dummyDescs->desc2.copy;
|
||||
custom->descs.desc2.dispose = dummyDescs->desc2.dispose;
|
||||
|
||||
custom->descs.desc3.signature = custom->signature;
|
||||
custom->descs.desc3.layout = dummyDescs->desc3.layout;
|
||||
|
||||
strlcpy(custom->signature, signature, signatureLength + 1);
|
||||
|
||||
custom->descs.desc2.copy(custom, dummy);
|
||||
|
||||
return custom;
|
||||
};
|
9
NSBlockInvocationInternal.h
Normal file
9
NSBlockInvocationInternal.h
Normal file
@ -0,0 +1,9 @@
|
||||
#import <Foundation/NSBlockInvocation.h>
|
||||
#import <Block_private.h>
|
||||
|
||||
extern void _CF_forwarding_prep_b();
|
||||
|
||||
struct NSProxyBlock {
|
||||
struct Block_layout blockInternal;
|
||||
void (^proxy)(NSBlockInvocation*);
|
||||
};
|
@ -32,6 +32,8 @@ along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
#import <objc/runtime.h>
|
||||
#import <stdio.h>
|
||||
|
||||
#import "NSBlockInvocationInternal.h"
|
||||
|
||||
#define ALIGN_TO(value, alignment) \
|
||||
(((value) % (alignment)) ? \
|
||||
((value) + (alignment) - ((value) % (alignment))) : \
|
||||
@ -133,3 +135,26 @@ id ___forwarding___(struct objc_sendv_margs *args, void *returnStorage)
|
||||
return nil;
|
||||
}
|
||||
|
||||
void __block_forwarding__(void* frame) {
|
||||
id block = *(id**)frame;
|
||||
Class class = object_getClass(block);
|
||||
const char *className = class_getName(class);
|
||||
|
||||
if (strncmp(className, ZOMBIE_PREFIX, strlen(ZOMBIE_PREFIX)) == 0) {
|
||||
CFLog(3, CFSTR("*** NSBlockInvocation: invocation of deallocated Block instance %p"), block);
|
||||
__builtin_trap();
|
||||
} else {
|
||||
const char* rawSig = _Block_signature(block);
|
||||
|
||||
if (rawSig) {
|
||||
NSMethodSignature* sig = [NSMethodSignature signatureWithObjCTypes: rawSig];
|
||||
NSBlockInvocation* invocation = [NSBlockInvocation _invocationWithMethodSignature: sig frame: frame];
|
||||
invocation.target = nil; // prevent the proxy from accidentally invoking us again
|
||||
(((struct NSProxyBlock*)block)->proxy)(invocation);
|
||||
} else {
|
||||
CFLog(4, CFSTR("*** NSBlockInvocation: Block %p does not have a type signature -- abort"), block);
|
||||
__builtin_trap();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -83,7 +83,7 @@
|
||||
[_container addObject: object];
|
||||
}
|
||||
|
||||
- (instancetype)initWithMethodSignature:(NSMethodSignature *)sig
|
||||
- (instancetype)_initWithMethodSignature: (NSMethodSignature*)sig frame: (void*)frame
|
||||
{
|
||||
if (sig == nil)
|
||||
{
|
||||
@ -104,6 +104,10 @@
|
||||
_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.
|
||||
@ -115,6 +119,11 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithMethodSignature:(NSMethodSignature *)sig
|
||||
{
|
||||
return [self _initWithMethodSignature: sig frame: NULL];
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
[self release]; // init should not work on NSInvocation
|
||||
@ -129,6 +138,12 @@
|
||||
[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];
|
||||
@ -326,7 +341,7 @@ static BOOL isBlock(id object)
|
||||
imp = &objc_msgSend;
|
||||
}
|
||||
|
||||
[self invokeUsingIMP: imp];
|
||||
[self _invokeUsingIMP: imp withFrame: _frame];
|
||||
}
|
||||
|
||||
- (void) invokeWithTarget: (id) target
|
||||
|
@ -56,8 +56,46 @@
|
||||
{
|
||||
NSMethodType *ms = &_types[_count];
|
||||
|
||||
if (nextType[0] == '>' && nextType[1] == '\0') {
|
||||
// handle the case where we initialize the signature using the extended type description of a block parameter
|
||||
// (this is so that we don't have to copy the string and null terminate it in `_signatureForBlockAtArgumentIndex:`)
|
||||
break;
|
||||
}
|
||||
|
||||
currentType = nextType;
|
||||
nextType = NSGetSizeAndAlignment(currentType, &ms->size, &ms->alignment);
|
||||
|
||||
// we need to be able to handle extended type encodings.
|
||||
// these don't affect the size or alignment of the type, but they are considered part of the type and we need to store them
|
||||
if (nextType[0] == '"') {
|
||||
// this describes what kind of object the argument expects.
|
||||
// this case is simple; no nesting possible
|
||||
nextType = strchr(nextType + 1, '"');
|
||||
if (!nextType) {
|
||||
// no closing quotation mark? invalid type encoding.
|
||||
[NSException raise: NSInvalidArgumentException format: @"Invalid type encoding: expected closing quotation mark"];
|
||||
}
|
||||
++nextType; // skip the closing quotation mark
|
||||
} else if (nextType[0] == '<') {
|
||||
// this describes the block signature.
|
||||
// this case is little more complicated; nesting is possible
|
||||
size_t nestLevel = 1;
|
||||
++nextType;
|
||||
for (; nestLevel > 0 && nextType[0] != '\0'; ++nextType) {
|
||||
if (nextType[0] == '<') {
|
||||
++nestLevel;
|
||||
} else if (nextType[0] == '>') {
|
||||
--nestLevel;
|
||||
}
|
||||
}
|
||||
if (nestLevel > 0) {
|
||||
// still missing a closing angle bracket? invalid type encoding.
|
||||
[NSException raise: NSInvalidArgumentException format: @"Invalid type encoding: expected closing angle bracket"];
|
||||
}
|
||||
}
|
||||
|
||||
// there might other extended type encodings i haven't encountered, but those two should be the two most important ones
|
||||
|
||||
ms->type = calloc(nextType - currentType + 1, 1);
|
||||
if (UNLIKELY(ms->type == NULL))
|
||||
{
|
||||
@ -216,4 +254,14 @@
|
||||
return _typeString;
|
||||
}
|
||||
|
||||
- (NSMethodSignature*)_signatureForBlockAtArgumentIndex: (NSUInteger)index
|
||||
{
|
||||
const char* argType = [self getArgumentTypeAtIndex: index];
|
||||
argType = strchr(argType, '<');
|
||||
if (!argType) {
|
||||
return nil;
|
||||
}
|
||||
return [NSMethodSignature signatureWithObjCTypes: argType + 1];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -1,5 +1,9 @@
|
||||
#import <objc/message.h>
|
||||
|
||||
#import <Block_private.h>
|
||||
|
||||
@class NSMethodSignature;
|
||||
|
||||
// Needs to be at least as large as:
|
||||
// - 4 ints (for r0-r3)
|
||||
// - 2 longs (for rdx/rax) and two doubles (for xmm0/xmm1)
|
||||
@ -12,8 +16,9 @@ void __invoke__(void *send, void *retdata, marg_list args, size_t len, char rett
|
||||
extern void _CF_forwarding_prep_0();
|
||||
extern void _CF_forwarding_prep_1();
|
||||
|
||||
|
||||
@interface NSInvocation (Internal)
|
||||
+ (instancetype)_invocationWithMethodSignature: (NSMethodSignature*)signature frame: (void*)frame;
|
||||
- (instancetype)_initWithMethodSignature: (NSMethodSignature*)signature frame: (void*)frame;
|
||||
- (void) invokeSuper;
|
||||
- (void) invokeUsingIMP: (IMP) imp;
|
||||
- (void **) _idxToArg: (NSUInteger) idx;
|
||||
|
Loading…
x
Reference in New Issue
Block a user