DNA

Dart Native Access lets you deal with native libraries from Dart with zero lines of C/C++ code. Just pure Dart.

How to ...

For instance, you want to call getppid function from libc library on Linux.

  • Find method signature in documentation:
pid_t getpid(void);
  • Map parameters types and return type to C data types:
typedef int pid_t;
  • Map C data types to Dart types and DNA type constants. See Type mapping.
  • Define class Libc with annotation @Library:
@Library('libc.so.6')
class Libc {
  
}

libc.so.6 is library name

  • Define method getppid. Annotate parameters with @Param and method with @Method with corresponding type constants:
class Libc {
  int getpid();
}

In this example method has no parameters.

  • Finish with boilerplate code:
class Libc extends DynamicLibrary<Libc> {
  int getpid();
}

  • Use it
Libc libc = new Libc();
var processId = libc.getpid();

For full code see Examples.

How to define library

@Library('name')
class Library DynamicLibrary<Library> {
    
}

name is the OS specific library name. For instance: libname.so or name.dll or libname.dylib.

Please see documentation for the target OS to know where dynamic linker searches for the library.

FIXME: Symlinks aren't supported

How to define method

@Method(C_XYZ) T method(
  @Param(C_XYZ) T1 inParam, 
  @Param(C_XYZ, out: true) Ref<T2> outParam);

C_XYZ is C type constant. See Type mapping.

How to define structure

@Struct()
class Struct {
    @Field(C_XYZ) T field;
}

Type mapping

CDartConstantIn/OutComments
voidvoidVOIDIn
charintC_CHARIn
shortintC_SHORTIn
intintC_INTIn
longintC_LONGIn
longlongintC_LONGLONGIn
boolboolC_BOOLIn
floatdoubleC_FLOATIn
doubledoubleC_DOUBLEIn
---------------
{type} *intC_POINTERInRaw pointer value.
{type} *TypedDataTYPEDDATAIn\OutTypeData must be initialized and have expected by callee size.
---------------
struct *TC_STRUCTInT is struct class. See Define structure.
struct *Ref<T>C_STRUCTOutT is struct class. See Define structure.
------------
char *StringC_STRINGIn
char *Ref<String>C_STRINGOut
char **List<String>LISTSTRINGIn
{integer type} *List<int>C_{type}In
{integer type} *Ref<List<int>>C_{type}Out
{decimal type} *List<double>C_{type}In
{decimal type} *Ref<List<double>>C_{type}Out

Out parameters

Out parameters must be marked as @Param(..., out: true)

Dart doesn't support parameters by reference. It's good but it's the cause of one challenge: callee cannot override parameter object. For instance, String parameter cannot be modified inside called method.

Therefore, it's needed to use wrapper object to emulate out parameters: Ref<T>.

FIXME: Describe pointers, in/out parameters, String parameters and TypedData parameters

FAQ

  • I see Failed assertion: 'libraryPointer != 0

Dynamic linker cannot find the library. Check the library name.

  • I see Failed assertion: 'methodPointer != 0

Dynamic linker cannot find method in the library. Check the method name. Check that library exports the method.

  • My library is cross platform and has different name on each platform.

See example.

@Library('libname.so', 'name.dll')
class Library DynamicLibrary<Library> {

}
  • My library doesn't export functions or/and uses custom logic to get function pointer

See example.

@Library('name.dll')
class Library DynamicLibrary<Library> {
  Library() : super(getMethodPointer);

  static int getMethodPointer(DynamicLibrary that, Invocation invocation) {
    return ... //raw function pointer here
  }
}

Examples

Linux

libc library

@Library('libc.so.6')
class Libc extends DynamicLibrary<Libc>  {
  int getpid();

  int getuid();
  
  int getgid();

  int getgroups(int count, @Param(C_INT, out: true, list: true) Ref<List<int>> groups);
  
  int rand();
  
  int rand_r(@Param(TYPEDDATA) TypedData seedp);
  
  void srand(@Param(C_UINT) int seed);
}

void main() {
  var libc = new Libc();

  var processId = libc.getpid();
  print('pid $processId');

  var userId = libc.getuid();
  print('user id ${userId}');

  var groupId = libc.getgid();
  print('group id ${groupId}');

  var n = libc.getgroups(0, new Ref(new List()));
  var groups = new Ref<List<int>>(new List<int>.filled(n, 0));
  libc.getgroups(n, groups);
  print('groups ${groups.value}');

  libc.srand(0xDEADBEEF);
  var rand = libc.rand();
  print('random $rand');

  var seed = new Uint32List.fromList([0xDEADBEEF]);
  var srand = libc.rand_r(seed);
  print('random with seed $srand');
}

Windows

kenel32 library

@Library('Kernel32')
class Kernel32 extends DynamicLibrary<Kernel32> {
  
  int GetCurrentProcess();

  int GetProcessId(@Param(C_INT) int process);

  int GetModuleFileNameA(
      @Param(C_INT) int process,
      @Param(C_STRING, out: true) Ref<String> imageFileName,
      @Param(C_INT) int size);

  bool GetProcessWorkingSetSize(
      @Param(C_INT) int process,
      @Param(TYPEDDATA) TypedData minimumWorkingSetSize,
      @Param(TYPEDDATA) TypedData maximumWorkingSetSize);

  int GetLastError();
}

void main() {
  Kernel32 kernel32 = new Kernel32();
  var process = kernel32.GetCurrentProcess();
  var processId = kernel32.GetProcessId(process);
  print('pid $processId = ${io.pid}');

  var min = new Uint32List(1);
  var max = new Uint32List(1);
  kernel32.GetProcessWorkingSetSize(process, min, max);
  print('working set min ${min} max ${max} error ${kernel32.GetLastError()}');

  var name = new Ref(new String.fromCharCodes(new List.filled(64, 0)));
  kernel32.GetModuleFileNameA(0, name, name.value.length);
  print('module name  ${name.value} error ${kernel32.GetLastError()}');
}

Please find more examples in examples/.

Requirements

  • Linux 64-bit
  • Windows 32-bit and 64-bit

To Do

  • Improve performance
  • A lot of things aren't supported yet
    • Platform independent types: int8, int16, int32, int64 etc.
    • Function pointers to Dart methods
    • Custom structure align
    • Unions
    • ...
  • Mac OS support
  • Refactor and clean up code

Libraries

dna
Dart Native Access