Add initial support for imp_implementationWithBlock() and friends. Currently

only works on x86 and x86-64 and doesn't work with sret functions.
main
theraven 14 years ago
parent db1c9052de
commit 43ee8b9819

@ -1,6 +1,6 @@
.POSIX: .POSIX:
.SUFFIXES: .cc .c .m .o .SUFFIXES: .cc .c .m .o .S
MAJOR_VERSION = 4 MAJOR_VERSION = 4
MINOR_VERSION = 6 MINOR_VERSION = 6
@ -10,12 +10,12 @@ VERSION = $(MAJOR_VERSION).$(MINOR_VERSION).$(SUBMINOR_VERSION)
CFLAGS += -std=gnu99 -fPIC -fexceptions CFLAGS += -std=gnu99 -fPIC -fexceptions
CXXFLAGS += -fPIC -fexceptions CXXFLAGS += -fPIC -fexceptions
CPPFLAGS += -DTYPE_DEPENDENT_DISPATCH -DGNUSTEP CPPFLAGS += -DTYPE_DEPENDENT_DISPATCH -DGNUSTEP
CPPFLAGS += -D__OBJC_RUNTIME_INTERNAL__=1 -D_XOPEN_SOURCE=500 CPPFLAGS += -D__OBJC_RUNTIME_INTERNAL__=1 -D_XOPEN_SOURCE=500 -D__BSD_VISIBLE=1
# Suppress warnings about incorrect selectors # Suppress warnings about incorrect selectors
CPPFLAGS += -DNO_SELECTOR_MISMATCH_WARNINGS CPPFLAGS += -DNO_SELECTOR_MISMATCH_WARNINGS
# Some helpful flags for debugging. # Some helpful flags for debugging.
#CPPFLAGS += -g -O0 -fno-inline CPPFLAGS += -g -O0 -fno-inline
PREFIX?= /usr/local PREFIX?= /usr/local
LIB_DIR= ${PREFIX}/lib LIB_DIR= ${PREFIX}/lib
@ -32,6 +32,8 @@ OBJECTS = \
arc.o\ arc.o\
associate.o\ associate.o\
blocks_runtime.o\ blocks_runtime.o\
block_to_imp.o\
block_trampolines.o\
caps.o\ caps.o\
category_loader.o\ category_loader.o\
class_table.o\ class_table.o\
@ -68,18 +70,22 @@ libobjc.a: $(OBJECTS)
@echo Linking static Objective-C runtime library... @echo Linking static Objective-C runtime library...
@ld -r -s -o $@ $(OBJECTS) @ld -r -s -o $@ $(OBJECTS)
.cc.o: .cc.o: Makefile
@echo Compiling `basename $<`... @echo Compiling `basename $<`...
@$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ @$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
.c.o: .c.o: Makefile
@echo Compiling `basename $<`... @echo Compiling `basename $<`...
@$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ @$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
.m.o: .m.o: Makefile
@echo Compiling `basename $<`... @echo Compiling `basename $<`...
@$(CC) $(CPPFLAGS) $(CFLAGS) -fobjc-exceptions -c $< -o $@ @$(CC) $(CPPFLAGS) $(CFLAGS) -fobjc-exceptions -c $< -o $@
.S.o: Makefile
@echo Assembling `basename $<`...
@$(CC) $(CPPFLAGS) -c $< -o $@
install: all install: all
@echo Installing libraries... @echo Installing libraries...
@install -d $(LIB_DIR) @install -d $(LIB_DIR)

@ -0,0 +1,26 @@
#include <objc/runtime.h>
#include <objc/blocks_runtime.h>
#include <assert.h>
@interface Foo @end
@implementation Foo @end
@interface Foo (Dynamic)
+(int)count: (int)i;
@end
int main(void)
{
__block int b = 0;
void* blk = ^(id self, int a) {
b += a;
return b; };
blk = Block_copy(blk);
IMP imp = imp_implementationWithBlock(blk);
class_addMethod((objc_getMetaClass("Foo")), @selector(count:), imp, "i@:i");
assert(2 == [Foo count: 2]);
assert(4 == [Foo count: 2]);
assert(6 == [Foo count: 2]);
assert(imp_getBlock(imp) == (blk));
imp_removeBlock(blk);
return 0;
}

@ -0,0 +1,125 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include "objc/runtime.h"
#include "objc/blocks_runtime.h"
#include "blocks_runtime.h"
#include "lock.h"
#include "visibility.h"
#define PAGE_SIZE 4096
static void *executeBuffer;
static void *writeBuffer;
static ptrdiff_t offset;
static mutex_t trampoline_lock;
static char *tmpPattern;
struct wx_buffer
{
void *w;
void *x;
};
PRIVATE void init_trampolines(void)
{
INIT_LOCK(trampoline_lock);
char *tmp = getenv("TMPDIR");
if (NULL == tmp)
{
tmp = "/tmp/";
}
if (0 > asprintf(&tmpPattern, "%s/objc_trampolinesXXXXXXXXXXX", tmp))
{
abort();
}
}
static struct wx_buffer alloc_buffer(size_t size)
{
LOCK_FOR_SCOPE(&trampoline_lock);
if ((0 == offset) || (offset + size >= PAGE_SIZE))
{
int fd = mkstemp(tmpPattern);
unlink(tmpPattern);
ftruncate(fd, PAGE_SIZE);
void *w = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
executeBuffer = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
*((void**)w) = writeBuffer;
writeBuffer = w;
offset = sizeof(void*);
}
struct wx_buffer b = { writeBuffer + offset, executeBuffer + offset };
offset += size;
return b;
}
extern void __objc_block_trampoline;
extern void __objc_block_trampoline_end;
extern void __objc_block_trampoline_sret;
extern void __objc_block_trampoline_end_sret;
IMP imp_implementationWithBlock(void *block)
{
struct block_literal *b = block;
void *start;
void *end;
if ((b->flags & BLOCK_USE_SRET) == BLOCK_USE_SRET)
{
start = &__objc_block_trampoline_sret;
end = &__objc_block_trampoline_end_sret;
}
else
{
start = &__objc_block_trampoline;
end = &__objc_block_trampoline_end;
}
size_t trampolineSize = end - start;
// If we don't have a trampoline intrinsic for this architecture, return a
// null IMP.
if (0 >= trampolineSize) { return 0; }
struct wx_buffer buf = alloc_buffer(trampolineSize + 2*sizeof(void*));
void **out = buf.w;
out[0] = (void*)b->invoke;
out[1] = Block_copy(b);
memcpy(&out[2], start, trampolineSize);
out = buf.x;
return (IMP)&out[2];
}
static void* isBlockIMP(void *anIMP)
{
LOCK(&trampoline_lock);
void *e = executeBuffer;
void *w = writeBuffer;
UNLOCK(&trampoline_lock);
while (e)
{
if ((anIMP > e) && (anIMP < e + PAGE_SIZE))
{
return ((char*)w) + ((char*)anIMP - (char*)e);
}
e = *(void**)e;
w = *(void**)w;
}
return 0;
}
void *imp_getBlock(IMP anImp)
{
if (0 == isBlockIMP((void*)anImp)) { return 0; }
return *(((void**)anImp) - 1);
}
BOOL imp_removeBlock(IMP anImp)
{
void *w = isBlockIMP((void*)anImp);
if (0 == w) { return NO; }
Block_release(((void**)anImp) - 1);
return YES;
}

@ -0,0 +1,49 @@
#
# This file defines some trampolines for calling blocks. A block function
# looks like this:
#
# retType blockFn(block*, ...)
#
# An IMP looks like this:
#
# retType imp(id, SEL,...)
#
# The trampoline must find the block pointer and then call the block function
# with the correct first argument, the self pointer moved to the second real
# argument (the first block argument) and the _cmd parameter excised
.file "block_trampolines.S"
.globl __objc_block_trampoline_sret
.type __objc_block_trampoline_sret, @function
.globl __objc_block_trampoline_end_sret
.globl __objc_block_trampoline
.type __objc_block_trampoline, @function
.globl __objc_block_trampoline_end
#if __x86_64
__objc_block_trampoline:
next:
mov -14(%rip), %esi # Load the block pointer into the second argument
xchg %edi, %esi # Swap the first and second arguments
jmp *-30(%rip) # Call the block function
__objc_block_trampoline_end:
__objc_block_trampoline_sret:
__objc_block_trampoline_end_sret:
#elif __i386
__objc_block_trampoline:
call next_line # Store the instruction pointer on the stack
next_line:
pop %eax # Load the old instruction pointer
mov 4(%esp), %ebx # Load the self parameter
mov %ebx, 8(%esp) # Store self as the second argument
mov -9(%eax), %ebx # Load the block pointer to %ebx
mov %ebx, 4(%esp) # Store the block pointer in the first argument
jmp *-13(%eax) # Call the block function
__objc_block_trampoline_end:
__objc_block_trampoline_sret:
__objc_block_trampoline_end_sret:
#else
__objc_block_trampoline:
__objc_block_trampoline_end:
__objc_block_trampoline_sret:
__objc_block_trampoline_end_sret:
#endif

@ -0,0 +1,181 @@
/**
* Block descriptor flags.
*/
enum block_flags
{
/**
* The block descriptor contains copy and dispose helpers.
*/
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
/**
* The helpers have C++ code.
*/
BLOCK_HAS_CTOR = (1 << 26),
/**
* Block is stored in global memory and does not need to be copied.
*/
BLOCK_IS_GLOBAL = (1 << 28),
/**
* Block function uses a calling convention that returns a structure via a
* pointer passed in by the caller.
*/
BLOCK_USE_SRET = (1 << 29),
/**
* Block has an Objective-C type encoding.
*/
BLOCK_HAS_SIGNATURE = (1 << 30),
/**
* Mask for the reference count in byref structure's flags field. The low
* 3 bytes are reserved for the reference count, the top byte for the
* flags.
*/
BLOCK_REFCOUNT_MASK = 0x00ffffff
};
/**
* Flags used in the final argument to _Block_object_assign() and
* _Block_object_dispose(). These indicate the type of copy or dispose to
* perform.
*/
enum
{
/**
* The value is of some id-like type, and should be copied as an
* Objective-C object: i.e. by sending -retain or via the GC assign
* functions in GC mode (not yet supported).
*/
BLOCK_FIELD_IS_OBJECT = 3,
/**
* The field is a block. This must be copied by the block copy functions.
*/
BLOCK_FIELD_IS_BLOCK = 7,
/**
* The field is an indirect reference to a variable declared with the
* __block storage qualifier.
*/
BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable
BLOCK_FIELD_IS_WEAK = 16, // declared __weak
BLOCK_BYREF_CALLER = 128, // called from byref copy/dispose helpers
};
#define IS_SET(x, y) ((x & y) == y)
/**
* Block descriptor that contains copy and dispose operations.
*/
struct block_descriptor_copydispose
{
/**
* Reserved for future use. Currently always 0.
*/
unsigned long int reserved;
/** Size of the block. */
unsigned long int size;
/**
* Copy function, generated by the compiler to help copy the block if it
* contains nontrivial copy operations.
*/
void (*copy_helper)(void *dst, void *src);
/**
* Dispose function, generated by the compiler to help copy the block if it
* contains nontrivial destructors.
*/
void (*dispose_helper)(void *src);
/**
* Objective-C type encoding of the block.
*/
const char *encoding;
};
/**
* Block descriptor that does not contain copy and dispose helper functions.
*/
struct block_descriptor
{
/**
* Reserved for future use, currently always 0.
*/
unsigned long int reserved;
/** Size of the block. */
unsigned long int size;
/**
* Objective-C type encoding of the block.
*/
const char *encoding;
};
// Helper structure
struct block_literal
{
/**
* Class pointer. Always initialised to &_NSConcreteStackBlock for blocks
* that are created on the stack or &_NSConcreteGlobalBlock for blocks that
* are created in global storage.
*/
void *isa;
/**
* Flags. See the block_flags enumerated type for possible values.
*/
int flags;
/**
* Reserved - always initialised to 0 by the compiler. Used for the
* reference count in this implementation.
*/
int reserved;
/**
* The function that implements the block. The first argument is this
* structure, the subsequent arguments are the block's explicit parameters.
* If the BLOCK_USE_SRET flag is set, there is an additional hidden
* argument, which is a pointer to the space on the stack allocated to hold
* the return value.
*/
void (*invoke)(void *, ...);
/**
* The block's descriptor. This is either block_descriptor or
* block_descriptor_copydispose, depending on whether the
* BLOCK_HAS_COPY_DISPOSE flag is set.
*/
struct block_descriptor_copydispose *descriptor;
/**
* Block variables are appended to this structure.
*/
};
/**
* Structure used for on-stack variables that are referenced by blocks.
*/
struct block_byref_obj
{
/**
* Class pointer. Currently unused and always NULL. Could be used in the
* future to support introspection.
*/
void *isa;
/**
* The pointer to the structure that contains the real version of the data.
* All accesses go via this pointer. If an on-stack byref structure is
* copied to the heap, then its forwarding pointer should point to the heap
* version. Otherwise it should point to itself.
*/
struct block_byref_obj *forwarding;
/**
* Flags and reference count.
*/
int flags; //refcount;
/**
* Size of this structure.
*/
int size;
/**
* Copy function.
*/
void (*byref_keep)(struct block_byref_obj *dst, const struct block_byref_obj *src);
/**
* Dispose function.
*/
void (*byref_dispose)(struct block_byref_obj *);
/**
* __block-qualified variables are copied here.
*/
};

@ -26,6 +26,7 @@
#import "objc/blocks_runtime.h" #import "objc/blocks_runtime.h"
#import "objc/runtime.h" #import "objc/runtime.h"
#import "objc/objc-arc.h" #import "objc/objc-arc.h"
#include "blocks_runtime.h"
#include "gc_ops.h" #include "gc_ops.h"
#include "visibility.h" #include "visibility.h"
#include <stdio.h> #include <stdio.h>
@ -34,188 +35,8 @@
#include <limits.h> #include <limits.h>
#include <assert.h> #include <assert.h>
static void *_HeapBlockByRef = (void*)1;
/**
* Block descriptor flags.
*/
enum block_flags
{
/**
* The block descriptor contains copy and dispose helpers.
*/
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
/**
* The helpers have C++ code.
*/
BLOCK_HAS_CTOR = (1 << 26),
/**
* Block is stored in global memory and does not need to be copied.
*/
BLOCK_IS_GLOBAL = (1 << 28),
/**
* Block function uses a calling convention that returns a structure via a
* pointer passed in by the caller.
*/
BLOCK_USE_SRET = (1 << 29),
/**
* Block has an Objective-C type encoding.
*/
BLOCK_HAS_SIGNATURE = (1 << 30),
/**
* Mask for the reference count in byref structure's flags field. The low
* 3 bytes are reserved for the reference count, the top byte for the
* flags.
*/
BLOCK_REFCOUNT_MASK = 0x00ffffff
};
/**
* Flags used in the final argument to _Block_object_assign() and
* _Block_object_dispose(). These indicate the type of copy or dispose to
* perform.
*/
enum
{
/**
* The value is of some id-like type, and should be copied as an
* Objective-C object: i.e. by sending -retain or via the GC assign
* functions in GC mode (not yet supported).
*/
BLOCK_FIELD_IS_OBJECT = 3,
/**
* The field is a block. This must be copied by the block copy functions.
*/
BLOCK_FIELD_IS_BLOCK = 7,
/**
* The field is an indirect reference to a variable declared with the
* __block storage qualifier.
*/
BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable
BLOCK_FIELD_IS_WEAK = 16, // declared __weak
BLOCK_BYREF_CALLER = 128, // called from byref copy/dispose helpers
};
#define IS_SET(x, y) ((x & y) == y)
/**
* Block descriptor that contains copy and dispose operations.
*/
struct block_descriptor_copydispose
{
/**
* Reserved for future use. Currently always 0.
*/
unsigned long int reserved;
/** Size of the block. */
unsigned long int size;
/**
* Copy function, generated by the compiler to help copy the block if it
* contains nontrivial copy operations.
*/
void (*copy_helper)(void *dst, void *src);
/**
* Dispose function, generated by the compiler to help copy the block if it
* contains nontrivial destructors.
*/
void (*dispose_helper)(void *src);
/**
* Objective-C type encoding of the block.
*/
const char *encoding;
};
/**
* Block descriptor that does not contain copy and dispose helper functions.
*/
struct block_descriptor
{
/**
* Reserved for future use, currently always 0.
*/
unsigned long int reserved;
/** Size of the block. */
unsigned long int size;
/**
* Objective-C type encoding of the block.
*/
const char *encoding;
};
// Helper structure
struct block_literal
{
/**
* Class pointer. Always initialised to &_NSConcreteStackBlock for blocks
* that are created on the stack or &_NSConcreteGlobalBlock for blocks that
* are created in global storage.
*/
void *isa;
/**
* Flags. See the block_flags enumerated type for possible values.
*/
int flags;
/**
* Reserved - always initialised to 0 by the compiler. Used for the
* reference count in this implementation.
*/
int reserved;
/**
* The function that implements the block. The first argument is this
* structure, the subsequent arguments are the block's explicit parameters.
* If the BLOCK_USE_SRET flag is set, there is an additional hidden
* argument, which is a pointer to the space on the stack allocated to hold
* the return value.
*/
void (*invoke)(void *, ...);
/**
* The block's descriptor. This is either block_descriptor or
* block_descriptor_copydispose, depending on whether the
* BLOCK_HAS_COPY_DISPOSE flag is set.
*/
struct block_descriptor_copydispose *descriptor;
/**
* Block variables are appended to this structure.
*/
};
/** static void *_HeapBlockByRef = (void*)1;
* Structure used for on-stack variables that are referenced by blocks.
*/
struct block_byref_obj
{
/**
* Class pointer. Currently unused and always NULL. Could be used in the
* future to support introspection.
*/
void *isa;
/**
* The pointer to the structure that contains the real version of the data.
* All accesses go via this pointer. If an on-stack byref structure is
* copied to the heap, then its forwarding pointer should point to the heap
* version. Otherwise it should point to itself.
*/
struct block_byref_obj *forwarding;
/**
* Flags and reference count.
*/
int flags; //refcount;
/**
* Size of this structure.
*/
int size;
/**
* Copy function.
*/
void (*byref_keep)(struct block_byref_obj *dst, const struct block_byref_obj *src);
/**
* Dispose function.
*/
void (*byref_dispose)(struct block_byref_obj *);
/**
* __block-qualified variables are copied here.
*/
};
/** /**

@ -22,6 +22,7 @@ void init_dispatch_tables(void);
void init_gc(void); void init_gc(void);
void init_protocol_table(void); void init_protocol_table(void);
void init_selector_tables(void); void init_selector_tables(void);
void init_trampolines(void);
void objc_send_load_message(Class class); void objc_send_load_message(Class class);
/* Number of threads that are alive. */ /* Number of threads that are alive. */
@ -59,6 +60,7 @@ void __objc_exec_class(struct objc_module_abi_8 *module)
init_dispatch_tables(); init_dispatch_tables();
init_alias_table(); init_alias_table();
init_arc(); init_arc();
init_trampolines();
first_run = NO; first_run = NO;
} }

@ -826,6 +826,25 @@ void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPo
*/ */
void objc_removeAssociatedObjects(id object); void objc_removeAssociatedObjects(id object);
/**
* Converts a block into an IMP that can be used as a method. The block should
* take an object pointer (self) as its first argument, and then the same
* arguments as the method.
*/
IMP imp_implementationWithBlock(void *block);
/**
* Returns the block that was used in an IMP created by
* imp_implementationWithBlock(). The result of calling this function with any
* other IMP is undefined.
*/
void *imp_getBlock(IMP anImp);
/**
* Removes a block that was converted to an IMP with
* imp_implementationWithBlock(). The result of calling this function with any
* other IMP is undefined. Returns YES on success, NO on failure.
*/
BOOL imp_removeBlock(IMP anImp);
/** /**
* Adds a method to a specific object, This method will not be added to any * Adds a method to a specific object, This method will not be added to any
* other instances of the same class. * other instances of the same class.

Loading…
Cancel
Save