prototype for calling msgSend directly with CGo
This is a quick sketch of a possible alternative approach for sending messages.
This is based on Apple's example for creating type-safe pointers to objc_msgSend:
https://developer.apple.com/documentation/apple-silicon/addressing-architectural-differences-in-your-macos-code#Enable-Strict-Type-Enforcement-for-Dynamic-Method-Dispatching
If we're planning on generating the Go wrappers based on the API, we could generate the CGo code too to more closely follow what the Obj-C pre-processor is doing for tranlating method calls to C code.
This should work for compiling on ARM for #5 though I haven't tested that out yet since it just depends on the C compiler, and not custom assembly to invoke the function for each platform.
Though that would mean that users wanting to send message by name would also
need to provide CGo code (or use a code generator we provide) instead of
calling Send(msg, args...).
But I wanted to get something up to discuss, since if we go this route it would
affect other things like #49 where we'd move more logic into the code generator
instead of the runtime reflection-style code in Send.
very cool find, i was wondering what benefits we'd get from putting more wrapper code in embedded obj-c and I guess this is one of them. sort of eliminates the need for variadic. BUT i think the dynamic API is great to have and these should be able to co-exist... just means it doesn't eliminate variadic.
Yeah, I think a dynamic API would be nice, but if we follow this pattern the dynamic portion could be an optional extra package instead of a core functionality.
Though doing a little more digging, I wonder if these give the necessary functionality directly in the Obj-C library: performSelector:withObject: - this is simpler and people could use it directly for dynamic invocations NSInvocation is more full-featured for calling all kinds of methods. It's a little more complex, but we could include a reflection-based wrapper for constructing this type of dynamic call more easily.
So if that works, it could eliminate the need for the variadic implementation which would be much simpler to support across architectures.
Oh wow. Yeah, maybe this calls for a NSInvocation wrapper implemented in the style we'd use for code generation (to try it out on something real) and see if we can replace our msgSend implementation with it.
I've added a lower-level send implementation based on NSInvocation with a couple test cases.
The return destination and argument locations are currently passed as unsafe.Pointer for a more direct mapping, but we can wrap that to provide some easier to use layers.
That would also give us a place to add some sanity checks like ensuring that the data we're passing is at least the right size for the Obj-C argument to avoid memory-safety issues.
Based on the cgo docs, I think we also need to make some changes to prevent passing Go pointers as argument values.
Passing pointers to the arguments, like this is ok because the Obj-C code just uses the pointer to get the value, but then makes a copy to store in the invocation's arg data:
var value int = 100
setArgumentAtIndex(inv, unsafe.Pointer(&value), 2)
But trying to pass data containing Go pointers would be unsafe since the garbage collector can't track where the C code is holding those references.
But yeah this approach seems suitable for replacing the Send implementation, eliminating the need for variadic.
(Though for ARM support, the other arch-dependent feature is AddMethod which sets up the function calls to allow Obj-C to call back into the Go method implementations.)
I've updated this with a potential Send-style implementation based on NSInvocation. It's pretty close, but something I ran into is that there doesn't appear to be a "supported" way of using that with super. There is a private method NSInvocation.invokeUsingIMP:, which would allow you to look up the super-class implementation and then call into it:
https://stackoverflow.com/questions/9831914/nsinvocation-and-super
There are also some interesting, but hacky proposals for adding methods to the class under a different selector name which points to the super-implementation.
Though with the code generation, we could also do something like the earlier code, but including "cast" wrappers around objc_msgSendSuper with the C signatures to let CGo handle it at compile time.
Thanks for exploring this. You probably know by now, but for the archive I think the new branch's FFI approach is going to be the way forward!