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

Design and implement Dart VM FFI #34452

Closed
mraleph opened this issue Sep 12, 2018 · 129 comments
Closed

Design and implement Dart VM FFI #34452

mraleph opened this issue Sep 12, 2018 · 129 comments
Assignees
Labels
area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. library-ffi P1 A high priority bug; for example, a single project is unusable or has many test failures type-enhancement A request for a change that isn't a bug

Comments

@mraleph
Copy link
Member

mraleph commented Sep 12, 2018

The present feature tracks the implementation of Dart FFI support enabling interop with C & C++ code from Dart.

Status

The feature will be in stable as of Dart 2.12. More details here:
https://dart.dev/guides/libraries/c-interop

For any discussion of this feature, or feedback or questions regarding the feature, kindly join and post the dart-ffi group: https://groups.google.com/forum/#!forum/dart-ffi

We will still actively adding new features to dart:ffi. Please refer to library-ffi for open issues and feature requests.

Background

Some inspirational references:

@mraleph mraleph added area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. type-enhancement A request for a change that isn't a bug labels Sep 12, 2018
@lrhn
Copy link
Member

lrhn commented Sep 14, 2018

A general FFI could also be useful for interaction with JavaScript, so we should consider whether it's possible to make something general wihout compromising on the usability on each platform.

The current JS-interop functionality is not a clean design, it should be possible to improve it.

@matanlurey
Copy link
Contributor

@lrhn:

The current JS-interop functionality is not a clean design, it should be possible to improve it.

I would not assume that without talking to @vsmenon. The syntax might leave a little to be desired, but I have no desire to make a breaking change to the syntax unless it vastly improves end users, not just the fact it isn't a "clean design".

@vsmenon
Copy link
Member

vsmenon commented Sep 14, 2018

Providing a new syntax / library doesn't necessarily imply removing the existing support. With that in mind, I think @lrhn 's suggestion is good.

@yjbanov
Copy link

yjbanov commented Oct 18, 2018

@lrhn @vsmenon I think C/C++-oriented interop makes sense on the Web in the context of WebAssembly. I wouldn't sacrifice native FFI capabilities due to JS. It's fine to have two systems that work best for their respective constraints.

@krisgiesing
Copy link

Is this referring to Dart calling C/C++ interfaces or vice versa?

@mraleph
Copy link
Member Author

mraleph commented Nov 27, 2018

@krisgiesing Initially it will be Dart calling C, later we would like to extend this to C calling Dart.

Though it is very hard to draw a line because we plan to support callbacks as part of the initial offering - so C would be able to call back into Dart.

Here is the markdown version of the vision doc (implementation is currently being prototyped).

Let me know if you have any comments on that one.

@matklad
Copy link

matklad commented Nov 28, 2018

This potentially requires using conditional imports.

Conditional compilation is a pain for IDEs. It's worth looking into how Kotlin handles a similar problem with expect/actual declarations on a language level. In a nutshell, expect/actual means that you write a single header/interface file, and a corresponding implementation for each platform. Because the interface is shared between all platforms, IDE does not need to know the "current" platform to do IDE stuff.

@mraleph
Copy link
Member Author

mraleph commented Nov 28, 2018

@matklad the expect/actual stuff is pretty close to interface libraries that were discussed in context of the conditional imports long time ago - exactly to solve IDE problem. However I don't think this went anywhere - and I think conditional imports are pretty much dead.

I think in the context of FFI we will be implementing what Structure Layouts and Portability describes.

I will amend the section that still talks about conditional imports.

@ghost
Copy link

ghost commented Dec 24, 2018

Any updates for dart lang FFi ? very happy if there have usable FFI .
We can import c/cpp language lib with it.

@dcharkes
Copy link
Contributor

@netroby I'm working on it. We plan to add an initial version during Q1. So far the prototype closely follows the vision doc. Let me know whether that covers your use case.

@fimbault
Copy link

fimbault commented Jan 8, 2019

Seems great, calling C is a must have. Is there a way to get access to the proto? Thanks.

@mraleph
Copy link
Member Author

mraleph commented Jan 11, 2019

@fimbault we plan to release prototype for public consumption later this quarter in few stages: first we will land support for FFI in JIT mode on X64 only, then it will be expanded to cover X64, ARM, ARM64 in JIT and AOT.

@jodinathan
Copy link

@matklad the expect/actual stuff is pretty close to interface libraries that were discussed in context of the conditional imports long time ago - exactly to solve IDE problem. However I don't think this went anywhere - and I think conditional imports are pretty much dead.

I think in the context of FFI we will be implementing what Structure Layouts and Portability describes.

I will amend the section that still talks about conditional imports.

are you talking about this: #24581 ?

I use it and it is great.

@robertmuth
Copy link

Are there plans to support calling into shared libraries similar to python's ctypes?

@mraleph
Copy link
Member Author

mraleph commented Jan 18, 2019

@robertmuth yes, please see the doc referenced from #34452 (comment) for more details.

@rootext
Copy link

rootext commented Jan 24, 2019

It will be great to have FFI in Dart.
From my perspective, FFI includes the following major features:

  1. Load dynamic library by name
  2. Lookup function in library by name
  3. Define native function signature
  4. Map dart types to native types and vice versa (including structs and unions)

There is proposal for all of them here.
Some thoughts:

For 1.
It's proposed to load library by name manually:

final lib = DynamicLibrary.open('libfoo.so');

It is good approach. However there are some alternative ways:

a. Load library manually but return class were all native (or abstract) functions resolved to dynamic library functions:

class Foo extends DynamicLibrary {
  int add(int a, int b) native;
}
final Foo lib = DynamicLibrary.open<Foo>(Platform.isLinux ? 'libfoo.so' : 'foo.dll');
lib.add(1,2); //3

Dart VM lookup all native function in class automatically.
JNA

b. The same as a. but use class name as dynamic library name:

//mapped to libfoo.so on Linux, to libfoo.dylib on macOS and foo.dll on Windows
class Foo extends DynamicLibrary { 
  int add(int a, int b) native;
}
final Foo lib = new Foo();

This automatic mapping is implemented in native extensions: Dart's library name mapped to dynamic library name. Nothing new.

c. The same as a. but use annotation on class:

@ffi.Library('foo')
class MyFoo { 
  int add(int a, int b) native;
}
final Foo lib = new Foo();

If library has different names (for instance OpenGL) than annotation can accept list of names.

@ffi.Library('Opengl32.dll', 'libGL.so.1')
class OpenGL {
}

d. Don't use class and define library name for each function:

@ffi.Library('foo')
int add(int a, int b) native;

C# DllImport.
Dart automatically load dynamic library by name and lookup function (symbol).
Disadvantages: developer doesn't control when dynamic library is loaded/unloaded. At least special API is needed.

e. Use Dart library name as in native extensions
Disadvantages: developer doesn't control when dynamic library is loaded/unloaded, there is no way to define different names.

For 2.
It is OK to be able to lookup function manually.
However, imagine a library with hundreds functions.
Have hundreds lines of code like:

final add = lib.lookupFunction<ffi.Int32 Function(ffi.Int32, ffi.Int32), int Function(int, int)>('add');

isn't great.

There are some other options to lookup function automatically:

a. Lookup by function name:

int add(int a, int b) native; //lookup 'add' function in dynamic library

b. Define name after native keyword as in native extensions:

int myadd(int a, int b) native 'add'; //lookup 'add' function in dynamic library

c. Lookup by name in annotation:

@ffi.Function('add')
int myadd(int a, int b) native; // lookup 'add' function in dynamic library

It works in for cases 1.a-1.c and for 1.d.
Anyway, I believe, it should be a way to load functions automatically based on function signature, which is already defined in my Dart code.

For 3. and 4.
It would be great to have something like this:

int add(@ffi.Type('Int') int a, @ffi.Type('Int') int b)

where annotation @ffi.Type define C type of parameter.

Top C data types should be supported: integer types (char, short, int, long, long long including unsigned), float types, pointers etc.
Dart VM should resolve their sizes in bits automatically based on run-time platform.
It isn't enough to support platform-independent types like int32_t or unit64_t.
It would be inconvenient to define sizes for each platform manually.

Some real world examples:

void srand(@ffi.Type('Uint') int seed); //libc on Linux
@ffi.Type('Ulong') int GetLastError(); //kernel32 on Windows

int SDL_Init(@ffi.Type('Int32') int flags); //SDL cross-platform
void SDL_ShowWindow(@ffi.Type('IntPtr') int window); //SDL cross-platform

For instance, SDL uses both platform-independent (int32_t) and platform-dependent (int, int *) types in API.

For structs it would be great to have automatic struct packing based on field order, its C type and run-time platform.
Instead of:

@ffi.struct({
  'x64 && linux': { // Layout on 64-bit Linux
    'x': ffi.Field(ffi.Double, 0),
    'y': ffi.Field(ffi.Double, 8),
    'next': ffi.Field(ffi.Double, 16)
  },
  'arm && ios': {  // Layout on 32-bit iOS
    'x': ffi.Field(ffi.Float, 4),
    'y': ffi.Field(ffi.Float, 8),
    'next': ffi.Field(ffi.Pointer, 0)
  },
})
class Point extends ffi.Pointer<Point> {
  double x;
  double y;
  Point next;
}

it would be great to write something like:

class Point  {
  @ffi.Type('Double')
  double x;
  @ffi.Type('Double')
  double y;
  @ffi.Type('Pointer')
  Point next;
}

It is enough information for Dart VM to pack structure on each supported platform.
However, ability to define field offset and struct size manually is welcome:

@ffi.StructSize(24)
class Point  {
  @ffi.Offset(0)
  @ffi.Type('Double')
  double x;
  @ffi.Offset(8)
  @ffi.Type('Double')
  double y;
  @ffi.Offset(16)
  @ffi.Type('Pointer')
  Point next;
}

JNA
C#

@dcharkes
Copy link
Contributor

@WoodyGuo You would have to use JNI for that.

@WoodyGuo
Copy link

WoodyGuo commented Oct 8, 2019

@WoodyGuo You would have to use JNI for that.

Thanks Daco for the prompt reply!

So I assume that I must split my single native library into 2 ones.
One is to be loaded from Java via System.loadLibrary, and the other is to be loaded from Dart via FFI.
So what would you suggest to use to call code in one library from the other one?

dart_ffi

@mikeperri
Copy link

@WoodyGuo It looks like you can make calls to the same instance of the same native library from both Java and Dart. This cpp file worked for me (after calling the first function from Java, calling the second one returns 123 to Dart):

extern "C" {
    int32_t setByJava = 0;

    JNIEXPORT void JNICALL
    Java_com_michaeljperri_flutter_1plugin_FlutterPlugin_native_1setValue(JNIEnv *env, jobject instance) {
        setByJava = 123;
    }

    __attribute__((visibility("default"))) __attribute__((used))
    int32_t get_val_from_java(const char* filename) {
        return setByJava;
    }
}

The cpp file is linked as a shared library. I have a Java class with
System.loadLibrary("my_plugin") and a Dart file with DynamicLibrary.open("libmy_plugin.so").

@WoodyGuo
Copy link

@mikeperri cool, thanks for the sharing.

@scriptsman
Copy link

Is C calling Dart supported now?

@mit-mit
Copy link
Member

mit-mit commented Jun 3, 2020

Yes, it's supported, but still not entirely complete. See https://dart.dev/guides/libraries/c-interop

@scriptsman
Copy link

@mit-mit Thank you.But it seems that the examples only show how to call C functions in dart.And I want to know if invoking a dart method from the C/C++ code is supported now , just like the end of this page?

@dcharkes
Copy link
Contributor

dcharkes commented Jun 4, 2020

@scriptsman callbacks are supported as well:

  • synchronous callbacks: see samples
  • asynchronous callbacks: see samples

And here and here is the native code for both samples.

@xuchaoqian
Copy link

@dcharkes Looking forward to supporting instance(not class) method.

@listepo

This comment has been minimized.

@dcharkes

This comment has been minimized.

@listepo

This comment has been minimized.

@listepo

This comment has been minimized.

@dcharkes

This comment has been minimized.

@mraleph

This comment has been minimized.

@xuchaoqian

This comment has been minimized.

@listepo
Copy link

listepo commented Nov 2, 2020

@dcharkes @mraleph sorry for the spam. I think the new issue is better, the answer can help not only me

tekknolagi pushed a commit to tekknolagi/dart-assembler that referenced this issue Nov 3, 2020
Prototype for `dart:ffi` on Linux/MacOS x64 in JIT mode.
`dart:ffi` is experimental and its API is likely to change in the future.
Progress and design decisions are tracked in https://github.com/dart-lang/sdk/projects/13


issue: dart-lang#34452
Change-Id: Ifa4566388e42c8757f154741d11e303465ef305d
Cq-Include-Trybots: luci.dart.try:vm-kernel-optcounter-threshold-linux-release-x64-try, vm-kernel-precomp-linux-debug-x64-try, vm-kernel-precomp-linux-release-simarm-try, vm-kernel-precomp-linux-release-simarm64-try, vm-kernel-precomp-linux-release-x64-try, vm-kernel-precomp-mac-release-simarm64-try, vm-kernel-precomp-win-release-x64-try, vm-kernel-mac-debug-x64-try, vm-kernel-asan-linux-release-x64
Reviewed-on: https://dart-review.googlesource.com/c/80124
Reviewed-by: Samir Jindel <sjindel@google.com>
Auto-Submit: Daco Harkes <dacoharkes@google.com>
@mit-mit mit-mit added this to the January Beta Release (2.12) milestone Jan 7, 2021
@franklinyow franklinyow added the P1 A high priority bug; for example, a single project is unusable or has many test failures label Jan 12, 2021
@dcharkes
Copy link
Contributor

dcharkes commented Jan 22, 2021

Closing as dart:ffi has now been marked stable.

We will still actively adding new features to dart:ffi. Please refer to library-ffi for open issues and feature requests.

@unicomp21
Copy link

does this work w/ emscripten on the wasm side?

@dcharkes
Copy link
Contributor

does this work w/ emscripten on the wasm side?

No, not yet. This is tracked in:

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 P1 A high priority bug; for example, a single project is unusable or has many test failures type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests