dart-lang / native

Dart packages related to FFI and native assets bundling.
BSD 3-Clause "New" or "Revised" License
157 stars 44 forks source link

[ffigen] Objective-C interface static methods and `ffiNative:` #1229

Open dcharkes opened 4 months ago

dcharkes commented 4 months ago

When using

ffiNative:

With toplevel methods in Objective-C we directly invoke via an @Native

@Native<Void Function()>()
external void play();

But if I change it to a static method on an interface we get a reflective implementation, that does dynamic symbol lookup.

class MyMediaPlayer extends objc.NSObject {
  static void play() {
    _objc_msgSend_2(_class_MyMediaPlayer, _sel_play);
  }

@liamappelbe Is it needed that we do something reflective here? Or can we use a @Native here as well? If we'd want to do that, we'd have to deal with the name mangling?

$ nm test/native_objc_test/static_func_test.dylib
0000000000003d08 t +[StaticFuncTestObj newWithCounter:]  // <-- name mangled?
0000000000003de4 t -[StaticFuncTestObj dealloc]
0000000000003d44 t -[StaticFuncTestObj initWithCounter:]
0000000000003dac t -[StaticFuncTestObj setCounter:]
                 U _OBJC_CLASS_$_NSObject
00000000000080f8 S _OBJC_CLASS_$_StaticFuncTestObj
00000000000080f0 S _OBJC_IVAR_$_StaticFuncTestObj.counter
                 U _OBJC_METACLASS_$_NSObject
0000000000008120 S _OBJC_METACLASS_$_StaticFuncTestObj
0000000000003ec8 s __OBJC_$_CLASS_METHODS_StaticFuncTestObj
0000000000003ee0 s __OBJC_$_INSTANCE_METHODS_StaticFuncTestObj
0000000000008048 s __OBJC_$_INSTANCE_VARIABLES_StaticFuncTestObj
0000000000008070 s __OBJC_CLASS_RO_$_StaticFuncTestObj
0000000000008000 s __OBJC_METACLASS_RO_$_StaticFuncTestObj
                 U __objc_empty_cache
0000000000003b30 T _getBlockRetainCount
0000000000003b54 T _getObjectRetainCount
0000000000003ba4 T _isReadableMemory
                 U _mach_task_self_
                 U _mach_vm_region
                 U _objc_alloc
                 U _objc_msgSend
0000000000003e88 s _objc_msgSend$initWithCounter:
0000000000003ea8 s _objc_msgSend$newWithCounter:
                 U _objc_msgSendSuper2
                 U _objc_retain
0000000000003ca4 T _staticFuncOfBlock
0000000000003c90 T _staticFuncOfNullableObject
0000000000003c7c T _staticFuncOfObject
0000000000003cb8 T _staticFuncReturnsRetained
0000000000003ce4 T _staticFuncReturnsRetainedArg

Or are there other reasons why we can't do this?

This would be mostly for performance reasons. For tree-shaking, we would generate different annotations anyway (https://github.com/dart-lang/native/issues/1099).

liamappelbe commented 4 months ago

That might be possible, but it's not just a matter of name mangling. In C++, a static method in a class is really just a top-level function under the hood (with exactly the same signature). But in ObjC, a static method in a class is really an instance method in the class object.

So whether it's possible depends on details of the ObjC ABI that I haven't investigated yet. The question is, what's the signature of the C function backing + (instancetype)newWithCounter:(int32_t*) _counter;? I could plausibly see it being any of:

StaticFuncTestObj *newWithCounter(int32_t* _counter);
StaticFuncTestObj *newWithCounter(Class* receiver, int32_t* _counter);
StaticFuncTestObj *newWithCounter(Class* receiver, SEL selector, int32_t* _counter);