Skip to content
/ wasm_ffi Public
forked from EP-u-NW/web_ffi

Translates dart:ffi calls on the web to WebAssembly using dart:js

License

Notifications You must be signed in to change notification settings

vm75/wasm_ffi

 
 

Repository files navigation

wasm_ffi

wasm_ffi intends to be a drop-in replacement for dart:ffi on the web platform using wasm. wasm_ffi is built on top of web_ffi. For ease of use cross-platform, the following are provided:

  • ffi_bridge: selects wasm_ffi/ffi.dart for web and dart:ffi for other platforms
  • ffi_utils_bridge: selects wasm_ffi/ffi_utils.dart for web and package:ffi for other platforms
  • FfiWrapper: a simple wrapper utility which loads DynamicLibrary asynchronously. Also provides a safeUsing method which uses the current library's memory.

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.

Webassembly (wasm) compiled with emscripten as well as standalone wasm is supported.

The provided example shows how to use wasm_ffi both in web and in dart.

Installation

  • Dart
dart pub add wasm_ffi
  • Flutter
flutter pub add wasm_ffi

Usage examples

FfiWrapper and ffigen (all platforms)

  • Generate bindings using ffigen
  • Replace import 'dart:ffi' as ffi; with import 'package:wasm_ffi/ffi_bridge.dart' as ffi; in the generated binding files
  • Instantiate FfiWrapper: ffiWrapper = await FfiWrapper.load('path to wasm or js');
  • Create binding instance: BindingClass bindings = BindingClass(ffiWrapper.library);
  • Call method: ffiWrapper.safeUsing((Arena arena) { ... });

Direct load example (only for web)

import 'package:wasm_ffi/ffi.dart' as ffi;

Future<void> main() async {
    final library = await DynamicLibrary.open('path to wasm or js'); // NOTE: It is async
    final func = library.lookupFunction<int Function(), int Function()>('functionName');
    print(func());
}

Differences to dart:ffi

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.

  • The DynamicLibrary class constructor is different. One key difference is that the 'load' method is asynchronous.
  • If more than one library is loaded, it is recommended to use FfiWrapper:safeUsing instead of using, as it ensure that the correct memory is used.
  • Each library has its own memory, so objects cannot be shared between libraries.
  • Some advanced types are still unsupported.
  • There are some classes and functions that are present in wasm_ffi but not in dart:ffi; such things are annotated with @extra.
  • There is a new class Memory which is IMPORTANT and explained in deepth below.
  • 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.

Rules for functions (TODO: needs update)

There are some rules and things to notice when working with functions:

  • When looking up a function using DynamicLibrary.lookup<NativeFunction<NF>>() (or DynamicLibraryExtension.lookupFunction<T extends Function, F extends Function>()) the actuall type argument NF (or T respectively) of is not used: There is no type checking, if the function exported from WebAssembly 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 (or F ) if you call NativeFunctionPointer.asFunction<DF>() (or DynamicLibraryExtension.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> and Pointer<Pointer<Int32>> are allowed but Pointer<Pointer<Pointer<Int32>>> is not.
    • If the return type is Pointer<NativeFunction> you MUST use Pointer<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 for NativeFunctions 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>>>), use Pointer<IntPtr> and cast it yourselfe afterwards using Pointer.cast().

Memory (TODO: needs update)

NOTE: While most of this section is still correct, some of it is now automated. 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.

About

Translates dart:ffi calls on the web to WebAssembly using dart:js

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 96.5%
  • Dart 3.5%