dna 0.6.1 copy "dna: ^0.6.1" to clipboard
dna: ^0.6.1 copied to clipboard

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

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);

Annotation @Method is used to define C type of return value. Annotation @Param is used to define C type of paramters. C_XYZ is C type constant. See Type mapping. @Method and @Param are optional. See Default mapping.

How to define structure #

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

Annotation @Struct is used to define C struct. Annotation @Field is used to define field of C struct. @Field is required. All fields without @Field annotations are ignored.

Type mapping #

Basic types #

C Dart Constant
void void VOID
char int C_CHAR
short int C_SHORT
uchar int C_UCHAR
ushort int C_USHORT
int int C_INT
uint int C_UINT
long int C_LONG
ulong int C_ULONG
longlong int C_LONGLONG
ulonglong int C_ULONGLONG
bool bool C_BOOL
float double C_FLOAT
double double C_DOUBLE

Fixed-width types #

C Dart Constant
int8_t int C_INT8
uint8_t int C_UINT8
int16_t int C_INT16
uint16_t int C_UINT16
int32_t int C_INT32
uint32_t int C_UINT32
int64_t int C_INT64
uint64_t int C_UINT64

Pointers #

C Dart Constant In/Out Comments
char * String C_STRING In
char * Ref<String> C_STRING Out String must be initialized and have expected by callee size.
char ** List<String> LISTSTRING In
{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
{type} * int C_POINTER In Raw pointer value.
{type} * TypedData TYPEDDATA In/Out TypeData must be initialized and have expected by callee size.

Structs #

C Dart Constant In/Out Comments
struct * T C_STRUCT In/Out T is struct class. See Define structure.

Default mapping #

Dart C Constant
void void VOID
int int C_INT
bool bool C_BOOL
double float C_FLOAT

Out parameters #

If parameter modified by callee it 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) Ref<List<int>> groups);
  
  int rand();
  
  int rand_r(@Param(C_INT) Ref<List<int>> 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(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 Ref([0xDEADBEE]);
  var srand = libc.rand_r(seed);
  print('random with seed $srand');
}

Windows #

kenel32 library

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

  static const int DWORD = C_ULONG;
  static const int HANDLE = C_POINTER;
  static const int HMODULE = HANDLE;  
  static const int PSIZE_T = C_POINTER;
  
  @Method(HANDLE)
  int GetCurrentProcess();

  @Method(DWORD)
  int GetProcessId(
    @Param(HANDLE) int process);

  @Method(DWORD)
  int GetModuleFileNameA(
      @Param(HMODULE) int process,
      @Param(C_STRING) Ref<String> imageFileName,
      @Param(DWORD) int size);

  bool GetProcessWorkingSetSize(
      @Param(HANDLE) int process,
      @Param(PSIZE_T) Ref<List<int>> minimumWorkingSetSize,
      @Param(PSIZE_T) Ref<List<int>> maximumWorkingSetSize);

  @Method(DWORD)
  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 Ref([0]);
  var max = new Ref([0]);
  kernel32.GetProcessWorkingSetSize(process, min, max);
  print('working set min ${min.value} max ${max.value} error ${kernel32.GetLastError()}');

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

Please find more examples in examples/.

Requirements #

  • Linux 64-bit
  • Windows 64-bit

To Do #

  • Improve performance
  • A lot of things aren't supported yet
    • Function pointers to Dart methods
    • Custom structure align
    • Unions
    • ...
  • Mac OS support
  • Refactor and clean up code
2
likes
25
pub points
0%
popularity

Publisher

unverified uploader

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

Homepage

License

Apache-2.0 (license)

More

Packages that depend on dna