wasm_ffi
is a drop-in solution for using dart:ffi
on the web. This enables you to work with WebAssembly
more easily and convenient. It is just a rebrand of web_ffi which is not being maintained.
The general idea is to expose an API that is compatible with dart:ffi
but translates all calls through dart:js
to a browser running WebAssembly
.
Currently, only WebAssembly
compiled with emscripten is usable because emscripten also generates the JavaScript imports WebAssembly
needs.
For a tutorial how to use this package (including the compiler settings for emscripten) see the example/README, but make sure to read this README first!
While wasm_ffi
tries to mimic the dart:ffi
API as close as possible, there are some differences. The list below documents the most importent ones, make sure to read it. For more insight, take a look at the API documentation.
wasm_ffi
was designed with the dart:ffi API 2.12.0 in mind, so there are currently no array extensions (they came with dart 2.13.0)- There is currently no support for structs (but opaque stucts are available).
- There are some classes and functions that are present in
wasm_ffi
but not indart:ffi
; such things are annotated with@extra
. - There is a new class
Memory
which is IMPORTANT and explained in deepth below. - The
DynamicLibrary
class constructor is different and requires an instance of the@extra Module
class . - If you extend the
Opaque
class, you must register the extended class using@extra registerOpaqueType<T>()
before using it! Also, your class MUST NOT have type arguments (what should not be a problem). - There are some rules concerning interacting with native functions, as listed below.
There are some rules and things to notice when working with functions:
- When looking up a function using
DynamicLibrary.lookup<NativeFunction<NF>>()
(orDynamicLibraryExtension.lookupFunction<T extends Function, F extends Function>()
) the actuall type argumentNF
(orT
respectively) of is not used: There is no type checking, if the function exported fromWebAssembly
has the same signature or amount of parameters, only the name is looked up. - There are special constraints on the return type (not on parameter types) of functions
DF
(orF
) if you callNativeFunctionPointer.asFunction<DF>()
(orDynamicLibraryExtension.lookupFunction<T extends Function, F extends Function>()
what uses the former internally):- You may nest the pointer type up to two times but not more:
- e.g.
Pointer<Int32>
andPointer<Pointer<Int32>>
are allowed butPointer<Pointer<Pointer<Int32>>>
is not.
- e.g.
- If the return type is
Pointer<NativeFunction>
you MUST usePointer<NativeFunction<dynamic>>
, everything else will fail. You can restore the type arguments afterwards yourself using casting. On the other hand, as stated above, type arguments forNativeFunction
s are just ignored anyway. - To concretize the things above, return_types.md lists what may be used as return type, everyhing else will cause a runtime error.
- WORKAROUND: If you need something else (e.g.
Pointer<Pointer<Pointer<Double>>>
), usePointer<IntPtr>
and cast it yourselfe afterwards usingPointer.cast()
.
- You may nest the pointer type up to two times but not more:
The first call you sould do when you want to use wasm_ffi
is Memory.init()
. It has an optional parameter where you can adjust your pointer size. The argument defaults to 4 to represent 32bit pointers, if you use wasm64, call Memory.init(8)
.
Contraty to dart:ffi
where the dart process shares all the memory, on WebAssembly
, each instance is bound to a WebAssembly.Memory
object. For now, we assume that every WebAssembly
module you use has it's own memory. If you think we should change that, open a issue on GitHub and report your usecase.
Every pointer you use is bound to a memory object. This memory object is accessible using the @extra Pointer.boundMemory
field. If you want to create a Pointer using the Pointer.fromAddress()
constructor, you may notice the optional bindTo
parameter. Since each pointer must be bound to a memory object, you can explicitly speficy a memory object here. To match the dart:ffi
API, the bindTo
parameter is optional. Because it is optional, there has to be a fallback mechanism if no bindTo
is specified: The static Memory.global
field. If that field is also not set, an exception is thrown when invoking the Pointer.fromAddress()
constructor.
Also, each DynamicLibrary
is bound to a memory object, which is again accessible with @extra DynamicLibrary.boundMemory
. This might come in handy, since Memory
implements the Allocator
class.