dart-lang / sdk

The Dart SDK, including the VM, dart2js, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
9.98k stars 1.54k forks source link

Design and implement Dart VM FFI #34452

Closed mraleph closed 3 years ago

mraleph commented 5 years ago

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 https://github.com/dart-lang/sdk/labels/library-ffi for open issues and feature requests.

Background

Some inspirational references:

lrhn commented 5 years ago

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 commented 5 years ago

@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 commented 5 years ago

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 commented 5 years ago

@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 commented 5 years ago

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

mraleph commented 5 years ago

@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 commented 5 years ago

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 commented 5 years ago

@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 commented 5 years ago

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

dcharkes commented 5 years ago

@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 commented 5 years ago

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

mraleph commented 5 years ago

@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 commented 5 years ago

@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: https://github.com/dart-lang/sdk/issues/24581 ?

I use it and it is great.

robertmuth commented 5 years ago

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

mraleph commented 5 years ago

@robertmuth yes, please see the doc referenced from https://github.com/dart-lang/sdk/issues/34452#issuecomment-449941065 for more details.

rootext commented 5 years ago

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 commented 5 years ago

The dart:ffi prototype has progressed to the point where we are deciding on API decisions. (Some design decisions are discussed here.)

To make informed design decisions, we would like more examples on what C APIs you would like to bind. The Flutter C++ interop issue mentions SQLite, Realm, OpenCV, Superpowered, and ffmpeg. Any other APIs we should consider?

kingwill101 commented 5 years ago

@dcharkes two APIS i've been thinking about are libvlc and telegrams tdlib https://github.com/tdlib/td and possibly SDL2

vadimtsushko commented 5 years ago

I believe databases support is often arises as a problem. ODBC, Oracle OCI and so on would be helpful

vadimtsushko commented 5 years ago

I personally made some half baked wrapper for http://www.libxl.com/ . It would be much prettier with the new FFI, I think

JeffersonBledsoe commented 5 years ago

@dcharkes I'm personally looking to use audio libraries such as portAudio and JUCE

bwalter commented 5 years ago

Having a convenient way to interact with Kotlin/Native would be extremly usefull (even if it would of course "somehow" work with a simple C FFI).

ds84182 commented 5 years ago

I'm looking forward to the possibility of accelerated cryptographic ops using libsodium or openssl

rootext commented 5 years ago

It isn't popular case (server-side) but still: SDL2, OpenGL, Vulkan, OpenAL. I wish I can remove all native extensions code and achieve better performance with FFI. Also kernel32.dll and user32.dll on Windows and libc.so on Linux.

bergwerf commented 5 years ago

I am interested in using GTK+ with Dart.

thosakwe commented 5 years ago

Is there a plan for distributing packages that use the new FFI, i.e. on Pub? On Tue, Mar 5, 2019 at 8:20 AM Herman Bergwerf notifications@github.com wrote:

I am interested in using GTK+ with Dart.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/dart-lang/sdk/issues/34452#issuecomment-469676879, or mute the thread https://github.com/notifications/unsubscribe-auth/AJiKPIItmOmpY0HU8T4sdiAYiiWR1MgXks5vTm86gaJpZM4Wl8MH .

dcharkes commented 5 years ago

Is there a plan for distributing packages that use the new FFI, i.e. on Pub?

@thosakwe I've discussed this @jonasfj, but at this point we don't know how yet we want to approach this. Should we distribute source or binaries? (Or provide the option to do either?) It probably depends on the use case. And if the package is used within Flutter, can we reuse something that Flutter does? We're aware of these questions, but do not have an answer yet.

kingwill101 commented 5 years ago

@thosakwe i believe providing both options would be a huge plus.

thosakwe commented 5 years ago

I think that distributing binaries would be easier in that Pub itself wouldn’t really need to change at all.

I personally prefer getting a source package, and then the package manager compiling it, but the infrastructure for that doesn’t exist in Dart, and I guess it’s unrealistic to expect things to change so drastically for just one feature. There’s also no post-install hook functionality in Pub, so ultimately I think distributing binaries would be the best way.

If there’s any disadvantage to that, it’s that you’d have to build your project separately on each different platform. But also, if you are providing support for Linux, etc., you’ve probably already built it on that platform, so it’s not that much of a problem. On Tue, Mar 5, 2019 at 9:04 AM Glenford Williams notifications@github.com wrote:

@thosakwe https://github.com/thosakwe i believe providing both options would be a huge plus.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/dart-lang/sdk/issues/34452#issuecomment-469691161, or mute the thread https://github.com/notifications/unsubscribe-auth/AJiKPPWk_ptAL32032NeYHDzluAQeABFks5vTnlzgaJpZM4Wl8MH .

felipecrv commented 5 years ago

Google LevelDB bindings would be an interesting exercise for the FFI.

On Tue, 5 Mar 2019 at 15:14, Tobe Osakwe notifications@github.com wrote:

I think that distributing binaries would be easier in that Pub itself wouldn’t really need to change at all.

I personally prefer getting a source package, and then the package manager compiling it, but the infrastructure for that doesn’t exist in Dart, and I guess it’s unrealistic to expect things to change so drastically for just one feature. There’s also no post-install hook functionality in Pub, so ultimately I think distributing binaries would be the best way.

If there’s any disadvantage to that, it’s that you’d have to build your project separately on each different platform. But also, if you are providing support for Linux, etc., you’ve probably already built it on that platform, so it’s not that much of a problem. On Tue, Mar 5, 2019 at 9:04 AM Glenford Williams <notifications@github.com

wrote:

@thosakwe https://github.com/thosakwe i believe providing both options would be a huge plus.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/dart-lang/sdk/issues/34452#issuecomment-469691161, or mute the thread < https://github.com/notifications/unsubscribe-auth/AJiKPPWk_ptAL32032NeYHDzluAQeABFks5vTnlzgaJpZM4Wl8MH

.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/dart-lang/sdk/issues/34452#issuecomment-469694585, or mute the thread https://github.com/notifications/unsubscribe-auth/AAMrs3lZBtub-BRHFWYFuAgbu2sOok25ks5vTnu4gaJpZM4Wl8MH .

maks commented 5 years ago

@dcharkes For me this would be to use libgit2 with Flutter.

iffy commented 5 years ago

I'm interested in embedding my own business-logic as a library in Flutter.

lifeng1992 commented 5 years ago

crypto lib write by C or Rust

listepo commented 5 years ago

It would be nice to have a rust-bindgen alternative for the Dart

vejmartin commented 5 years ago

Using Dart / Flutter with Qt might be interesting for desktop apps.

theKAKAN commented 5 years ago

Using Dart / Flutter with Qt might be interesting for desktop apps.

Why?
Skia works in all the major platforms. Sublime Text uses Skia and is pretty fast and lightweight as well :)

vejmartin commented 5 years ago

Using Dart / Flutter with Qt might be interesting for desktop apps.

Why? Skia works in all the major platforms. Sublime Text uses Skia and is pretty fast and lightweight as well :)

Skia is brilliant and it can be used in Qt apps as well (either by painting an SkSurface to a QWidget with QPainter, or by painting directly to a QWindow's context) with Qt providing things like cross-platform windowing and docking, c++ plugin support, installers/bundle builders, networking APIs, parsers and more.

theKAKAN commented 5 years ago

@vejmartin This would increase the size of the executable by a big margin in case you use native features, I think

thosakwe commented 5 years ago

Another great thing would be using the FFI to access serial ports.

robertmuth commented 5 years ago
grant-roy commented 5 years ago

@dcharkes, ti would be great to have support for openBLAS, and graphBLAS. These provide powerful building blocks for working with linear algebra and graphs.

mit-mit commented 5 years ago

We are now at a point where we would like to offer an early preview to get some feedback. We’re looking for feedback on how ergonomic the feature is to use, what the main gaps are, and lastly on general stability and performance.

We are looking for testers who are willing to:

  1. Test the feature by interfacing with a concrete C based library such as SQLite or libphonenumber
  2. Tolerate a feature that is still incomplete and unstable
  3. Offer us feedback on their experience

There is a small SQLite sample demonstrating the FFI feature in the SDK: Readme, FFI wrapper, sample program.

If you are interested, please consider one or more potential libraries you are interested in, and send an email with a description of those to dart-ffi-team@googlegroups.com (we’ll try to make sure we don’t several wrapping the same lib).

Also, you should be aware of the following current main limitations:

On behalf of the Dart FFI team, Michael

mit-mit commented 5 years ago

I'm so sorry, but we had an issue with the google group mentioned above, so if you already sent an email to dart-ffi-team@googlegroups.com, can you please send it again?

Thanks much!

kingwill101 commented 5 years ago

@mit-mit tried loading a basic shared library built from go on Linux but i am unable to get it to work

shared library build command

go build -o dart/bin/test.so -buildmode=c-shared lib.go

and tried opening using

ffi.DynamicLibrary l = dlopenPlatformSpecific("test", path:"/home/kingwill101/go/src/gitlab.com/kingwill101/test/dart/bin/");

dlopenPlatformSpecific comes from the examples provided ArgumentError (Invalid argument(s): Failed to load dynamic library(lib/home/kingwill101/go/src/gitlab.com/kingwill101/test/dart/bin/libtest.so: cannot open shared object file: No such file or directory))

loading a system library works but trying to load using a relative path to the library fails.

dcharkes commented 5 years ago

@glenfordwilliams I suggest extending dlopenPlatformSpecific to create a full path based on where your .dart file is. You can construct the full path in Dart.

Explanation: the paths are directly interpreted by dlopen. This means the relative paths are relative to the dart binary. (This test uses a relative path, and the .so file lives right next to the dart binary.) In general, one would want a path to the .so file relative to the .dart file using that .so file, which would be constructed in Dart.

kingwill101 commented 5 years ago

@dcharkes i tried doing that earlier but for some reason dart adds lib in front of the created path

Exception has occurred.
ArgumentError (Invalid argument(s): Failed to load dynamic library(lib/home/kingwill101/go/src/gitlab.com/kingwill101/test/dart/bin/test.so: cannot open shared object file: No such file or directory))
dcharkes commented 5 years ago

@glenfordwilliams Yes, you're gonna have to modify or make your own version of dlopenPlatformSpecific. The example implementation simply prepends "lib" on Linux on line 10.

sjindel-google commented 5 years ago

It's Linux convention to prepend lib to the names of shared libraries. However, go build won't do that automatically. If you're not planning to run your code on other platforms, you can just use DynamicLibrary.open directly.

kingwill101 commented 5 years ago

sorry about that, vscode seems to be caching old code

kingwill101 commented 5 years ago
import 'dart:ffi' as ffi;
import 'dart:io';
import "package:path/path.dart" show dirname;
import 'dart:io' show Platform;

typedef GoFunction = ffi.Void Function();
typedef GoFunctionOp = void Function();

ffi.DynamicLibrary dlopenPlatformSpecific(String name, {String path}) {
  String fullPath = dirname(Platform.script.path) + "/" + name;
  return ffi.DynamicLibrary.open(fullPath);
}

main(List<String> arguments) {
  Directory current = Directory.current;
  print(current.path);
  ffi.DynamicLibrary l;
  try {
    l = dlopenPlatformSpecific("libtest.so");
  } catch (e) {
    print(e.toString());
  }

  var go = l.lookupFunction<GoFunction, GoFunctionOp>("AGoFunction");
  go();
}

strange thing is i get everything to work when i run from the command line

kingwill101@kingtech:~/go/src/gitlab.com/kingwill101/test/dart$ ~/flutter/bin/cache/dart-sdk/bin/dart bin/main.dart
/home/kingwill101/go/src/gitlab.com/kingwill101/test/dart
AGOFUNCTION()
kingwill101@kingtech:~/go/src/gitlab.com/kingwill101/test/dart$ 

but when i run from vscode i get

bin/main.dart:25:14: Error: Expected type 'NativeFunction<Void Function()>' to be a valid and instantiated subtype of 'NativeType'.
 - 'NativeFunction' is from 'dart:ffi'.
 - 'Void' is from 'dart:ffi'.
  var go = l.lookupFunction<GoFunction, GoFunctionOp>("AGoFunction");
             ^