dna 0.6.1 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.
- Linux - dlopen
- Mac OS - dlopen
- Windows - LoadLibrary
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