Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'Pointer<NativeFunction<T Function()>> ptr; T Function() func = ptr.asFunction(); ' When T must be 'Pointer' to pass compilation checking. #54467

Open
SilverFruity opened this issue Dec 28, 2023 · 6 comments
Labels
area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. library-ffi P3 A lower priority bug or feature request triaged Issue has been triaged by sub team

Comments

@SilverFruity
Copy link

SilverFruity commented Dec 28, 2023

  • Dart analyzer and linter

Test on dartpad

image

Test on The latest main branch

Executing this code with dart compiled from the latest commit on the main branch of the github repository has the same problem, as follows:

➜  sdk git:(main) ✗ PAGER=cat git log -1
commit f860d84ad77fa0910ae5be18e2211ac06119cf8a (HEAD -> main, origin/master, origin/main, origin/lkgr, origin/base, origin/HEAD)
Author: Nicholas Shahan <[email protected]>
Date:   Thu Dec 28 03:19:30 2023 +0000

    [test] Use Uris in place of Strings in test
    
    Provides consistent behavior for identical and equals across all
    backends. The test was failing on JavaScript backends because
    Strings that are equal are also identical.
    
    Change-Id: I85929f37746d8f7e192410e3d2f6530ce05176c5
    Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/343860
    Reviewed-by: Nate Bosch <[email protected]>
    Auto-Submit: Nicholas Shahan <[email protected]>
    Commit-Queue: Nate Bosch <[email protected]>
➜  sdk git:(main) ✗ ./tools/build.py --no-goma --mode release --arch x64 create_sdk
Generating Xcode projects took 47ms
Done. Made 428 targets from 121 files in 1574ms
buildtools/ninja/ninja -C xcodebuild/ReleaseX64 create_sdk
ninja: Entering directory `xcodebuild/ReleaseX64'
ninja: no work to do.
The build took 2.026 seconds
➜  sdk git:(main) ✗ cat test.dart
import "dart:ffi";

void main() {
   Pointer<NativeFunction<Pointer<Void> Function()>> isAlive = Pointer<NativeFunction<Pointer<Void> Function()>>.fromAddress(0x100000);
   Pointer<Void> Function() isAliveFunc = isAlive.asFunction();

   Pointer<NativeFunction<Void Function()>> isAlive1 = Pointer<NativeFunction<Void Function()>>.fromAddress(0x100000);
   Void Function() isAliveFunc1 = isAlive1.asFunction();
}
➜  sdk git:(main) ✗ ./xcodebuild/ReleaseX64/dart-sdk/bin/dart test.dart            
test.dart:8:44: Error: Expected type 'Void Function()' to be 'void Function()', which is the Dart type corresponding to 'NativeFunction<Void Function()>'.
 - 'Void' is from 'dart:ffi'.
 - 'NativeFunction' is from 'dart:ffi'.
   Void Function() isAliveFunc1 = isAlive1.asFunction();
                                           ^
➜  sdk git:(main) ✗ 
@lrhn lrhn added area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. library-ffi labels Dec 29, 2023
@halildurmus
Copy link
Contributor

halildurmus commented Dec 29, 2023

Using Void in Dart type signatures is not allowed, you should use void instead. You also need to specify the Dart type signatures for functions as type arguments in the asFunction method.

Try this:

void main() {
  final isAlive =
      Pointer<NativeFunction<Pointer<Void> Function()>>.fromAddress(0x100000);
  final isAliveFunc = isAlive.asFunction<Pointer Function()>();

  final isAlive1 =
      Pointer<NativeFunction<Void Function()>>.fromAddress(0x100000);
  final isAliveFunc1 = isAlive1.asFunction<void Function()>();

  final testObject = Pointer<TestResult>.fromAddress(0x100000);
  final func = testObject.ref.dispose
      .asFunction<Pointer Function(Pointer<TestResult>)>();
  final func1 =
      testObject.ref.dispose1.asFunction<void Function(Pointer<TestResult>)>();
}

I skipped specifying types in variable declarations to reduce verbosity. Simply use final or var, whichever you prefer.

@mraleph
Copy link
Member

mraleph commented Jan 3, 2024

cc @dcharkes We should consider upgrading error messages to make them more novice friendly.

I really wish we have a simpler way to managing Dart and C signatures, maybe making primitive types to be extension types could work:

extension type const Int32._(int _) {
}

extension type Pointer<T>._(Address _) {
}

Then if you have a function type Int32 Function(Int32, Pointer<Int32>) which represents both native and Dart signature of the function.

Maybe we should file a bug to explore FFI 2.0 APIs which don't require writing two signatures.

@dcharkes
Copy link
Contributor

dcharkes commented Jan 3, 2024

Maybe we should file a bug to explore FFI 2.0 APIs which don't require writing two signatures.

I've filed an issue with some links. Also, there was some old (internal) design doc for dart:ffi2 which I linked there.

@SilverFruity were you able to get it working?

My suggestion would be to use package:ffigen, it can generate everything correctly for you.

@SilverFruity
Copy link
Author

My current ffi code generated by dart:ffigen works fine, but is avoiding asFunction secondary signatures by Pointer<Void> as follows:

struct TestList {
  void *(*add)(struct TestList *, void *element);
  void *(*dispose)(struct TestList *);
};
Pointer<Void> Function(Pointer<TestList>) add = list.ref.add.asFunction();
add(list, null);
Pointer<Void> Function(Pointer<TestList>) dispose = list.ref.dispose.asFunction();
dispose(list);

@dcharkes
Copy link
Contributor

dcharkes commented Jan 3, 2024

I'm not entirely sure what your code is trying to do.

Are you using dynamic dispatch on purpose? Storing the function pointer in the TestList struct?
If you don't want to use dynamic dispatch, your code should probably be:

struct TestList {
};

void add(struct TestList *, void *element);
void dispose(struct TestList *);

In which case you wouldn't have to use asFunction.

I assume you want to return void.

If you want dynamic dispatch and return void, your C and Dart code should probably look like this:

struct TestList {
  void (*add)(struct TestList *, void *element);
  void (*dispose)(struct TestList *);
};
void Function(Pointer<TestList>) add = list.ref.add.asFunction();
add(list, null);
void Function(Pointer<TestList>) dispose = list.ref.dispose.asFunction();
dispose(list);

@SilverFruity
Copy link
Author

SilverFruity commented Jan 3, 2024

Yes, I'm storing the function pointer in a TestList struct for dynamic dispatch, and then want to be able to call it directly using asFunction.

@a-siva a-siva added P3 A lower priority bug or feature request triaged Issue has been triaged by sub team labels Jan 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. library-ffi P3 A lower priority bug or feature request triaged Issue has been triaged by sub team
Projects
None yet
Development

No branches or pull requests

6 participants