On Being a Monster
Don't do this. Seriously.
Edit: There are some important developments, please make sure you check them out.. You shouldn't have tried to use this code before but now it's just completely unnecessary.
Original:
This is horrible and you should never never do this under any circumstances. There are plenty of bear traps in software engineering. You needn't put your foot into this particular trap. In the name of keeping a promise, here we go.
The Setup
We need some C code that is going to receive a function pointer from our Swift code:
typedef void(*PointerToFunction)();
static inline void some_c_function(PointerToFunction f) {
f();
}
Then we'll need some magic struct declarations to represent the swift trampolines and function object:
struct swift_func_wrapper {
var trampolinePtr: UnsafeMutablePointer<uintptr_t>
var functionObject: UnsafeMutablePointer<swift_func_object>
}
struct swift_func_object {
var original_type_ptr: UnsafeMutablePointer<uintptr_t>
var unknown: UnsafeMutablePointer<UInt64> //always 8 bytes even on 32-bit
var function_address: uintptr_t
var selfPtr: UnsafeMutablePointer<uintptr_t>
}
I think original_type_ptr
is a pointer to the type metadata for the function but I'm not sure. I have no idea what unknown
is. My assumption on selfPtr
is it comes into play if the function is an instance member and would hold the address of the instance.
Last but not least we need a function that we'd like to invoke from the C world:
func doWhatever() {
println("called")
}
The Crime Scene
var p = UnsafeMutablePointer<()->Void>.alloc(1)
p.initialize(doWhatever)
var wrapperPtr = UnsafeMutablePointer<swift_func_wrapper>(p)
let opaque = COpaquePointer(bitPattern: wrapperPtr.memory.functionObject.memory.function_address)
let c_function_pointer = CFunctionPointer<()->Void>(opaque)
some_c_function(c_function_pointer)
Let's unpack this. First we get a pointer to a void-returning function that takes no parameters and initialize it to point at our victim function. Then we blindly cast it to be a pointer to swift_func_wrapper
. The layout of that structure is where the magic happens and you had better hope it's correct!
Next we do some more awful bit manipulation to convert the function pointer to a COpaquePointer
which we can then coerce to a CFunctionPointer
of the appropriate type.
Last but not least we hand that off to our unsuspecting C function and watch as "called" is printed to the console. Mission Accomplished.
Massive caveats abound here:
- Are these structures safe to use? Not in the slightest.
- What about the memory management rules? Object lifetimes? Good luck.
- Does it work with arbitrary closures? Doubtful.
Conclusion
God help us all.
This blog represents my own personal opinion and is not endorsed by my employer.