Nu Blocks and Objective-C Blocks, Part 2
OK, at last I’m finally writing part two. Boy it’s surprisingly difficult to update one’s blog regularly…
So in Part 1 I showed how you can write over the function pointer of a block with another function pointer and the block will still work. It’s probably also possible to construct a block from scratch, but I don’t really see any benefit to that.
So, how to make a C block that will call a Nu block? Fortunately most of the work is already done; if you look into the Nu source code in the objc/bridge.m file you can see how Nu is rigged it up so that Objective-C code can call methods implemented in Nu. The magic happens with the help of libffi (Foreign Function Interface) a library that, among other things, allows one to construct C functions at runtime with arbitrary names and argument signatures. When one of these constructed functions gets called, libffi in turn calls a callback that had been specified earlier. This is that callback in Nu; all message sends from Objective-C to Nu pass through this function:
static void objc_calling_nu_method_handler(ffi_cif* cif, void* returnvalue, void** args, void* userdata)
The first argument is a libffi struct previously generated by the user (in Nu this happens in construct_method_handler()) that contains information about the function that libffi was to generate: return type, argument count and types, etc. The second argument is a pointer to where the return value should be written so that libffi can return it to the original caller. The third argument is an array of the argument values and the fourth is user-defined info. In Nu this fourth argument points to an array of pointers where the first element is the Objective-C type encoding of the return value (a c string), the second is the Nu implementation of the method (as a NuBlock) and the remaining elements are the Objective-C type encodings of the arguments (more c strings).
In short, all of the information that the Nu runtime needs to pass control to the Nu method are there.
So what I did was copy all of this code and tweak it a little bit. Instead of having libffi make a function fit to serve as an Objective-C method (i.e. with an argument list like this: “id sender, SEL __cmd, …” (where the first two arguments are the message recipient and the selector of the message being sent, respectively) I had it make one like this: “id block, …” (recall from part 1 of this post that block functions take as a first, hidden argument a pointer to the associated block object). So in my code all calls to Objective-C blocks implemented in Nu pass through this function:
static void objc_calling_nu_block_handler(ffi_cif* cif, void* returnvalue, void** args, void* userdata)
This function in turns just calls the appropriate NuBlock (stored in element two of userdata).
So all that’s left is to make a dummy Objective-C block and write the pointer to the libffi generated function into it. Here is the code that does that:
static id make_cblock (NuBlock *nuBlock, NSString *signature)
{
void *funcptr = construct_block_handler(nuBlock, [signature UTF8String]);
int i = 0xFFFF;
void(^cBlock)(void)=[^(void){printf("%i",i);} copy];
#ifdef __x86_64__
//2*(sizeof(void*)) = 0x10
*((void **)(id)cBlock + 2) = (void *)funcptr;
#else
//3*(sizeof(void*)) = 0xc
*((void **)(id)cBlock + 3) = (void *)funcptr;
#endif
return cBlock;
}
Why that variable i and why the printf()? All that jazz does is make sure the compiler generates a distinct block object every time that code is executed. If it thinks it can get away with it, it will reuse previously created blocks just like it may reuse string literals (try running this code: id a=@”Hi”, b=@”Hi”;printf(“%lx,%lx”,a,b);)
Well the printf doesn’t do anything and will never get called; it’s just an arbitrary expression using the i variable. What’s important is for the block to reference something from the surrounding context, something mutable that will be copied by value. If it does not reference a variable from the surrounding context, or if that variable has the __block qualifier (meaning that it should not be copied), then the compiler will reuse the same block object over and over again.
So that’s how it works. But wait, there is a hitch. The problem is that there appears to be a bug in the Objective-C runtime. Nu uses the runtime functions method_getNumberOfArguments() and method_getArgumentType() in the code that handles message sends from Nu to Objective-C. But these functions (at least as of 10.6.2) return incorrect values for any method expecting a block argument. The encoding type for blocks appears to be “@?” but those runtime functions (which I believe call method_getTypeEncoding and parse the string returned by that) treat that encoding as two separate encodings. Unless this gets fixed in 10.6.3 (my bug report has gone unanswered so far) I’m going to have to write some substitutes for those functions or make a patched version of libobjc.A.dylib. Well, I’m hoping I’ll have time to finish this up soon, and then maybe see if Tim Burks is interested in merging this into the main Nu branch.
For the time being, the version of Nu at my github repository (see sidebar) replaces the above-mentioned runtime functions with calls to similar NSMethodSignature methods. Unfortunately, this seems to raise other more mysterious problems in Nu. But it is useable enough to screw around with.
An Objective-C block can be created by sending the following message to the class NuBridgedBlock:
+(id)cBlockWithNuBlock:(NuBlock*)nb signature:(NSString*)sig
where sig is a string containing the Objective-C type encodings for the return value and arguments. This is a little cumbersome and I plan to implement a convenience macro, maybe something like this:
(cblock int ((id) arg1 (NSRect) arg2) (--nu code here--))
Anyway, I have successfully called various Cocoa methods that expect blocks as well as libdispatch functions from Nu. I just need to work out the kinks and I think it might be pretty useful!
