-
Notifications
You must be signed in to change notification settings - Fork 9
Shift to Native Assets + update to uniffi 0.30.0 #99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Nice work! Glad uniffi-dart is working for you. For payjoin-ffi we are stuck on uniffi 0.29 for the time being (see payjoin/rust-payjoin#1167) so it would be nice to separate the code fixes from the uniffi upgrade, such that we can maintain two branches to support 0.30 and keep supporting 0.29 for some time. |
…compatibility
Updates the generated Dart code to use simple library names instead of
absolute paths for Android and Linux platforms. This makes the
generated code compatible with Dart's Native Assets system.
Changes:
- Android: "${Directory.current.path}/lib{libname}.so" → "lib{libname}.so"
- Linux: "${Directory.current.path}/lib{libname}.so" → "lib{libname}.so"
Introduces a `library_loading_strategy` configuration option that allows
users to choose between traditional directory-based library loading and
Native Assets mode.
Configuration options:
- `directory_path` (default): Uses `${Directory.current.path}` for
Android/Linux, maintaining backward compatibility
- `native_assets`: Uses `DynamicLibrary.process()` for all platforms,
enabling Dart Native Assets integration
Usage in uniffi.toml:
```toml
[bindings.dart]
library_loading_strategy = "native_assets"
The generate_dart_bindings function was ignoring the config_file_override parameter when library_mode was true, always using the udl_file path instead. This prevented custom configurations in uniffi.toml from being read when generating bindings in library mode. Fixed by using config_file_override when provided, falling back to udl_file only when no override is specified: let config_path = config_file_override.unwrap_or(udl_file) This ensures the library_loading_strategy and other custom configurations are properly applied in library mode.
This is a major refactor that modernizes the FFI bindings to use Dart 3.10+'s Native Assets system instead of manual DynamicLibrary.open() calls. Key changes: - Replace DynamicLibrary.open() with @Native annotations using asset IDs - Generate Native Assets build hooks (hook/build.dart) using hooks package - Implement asset ID system: package:{dart_package_name}/uniffi:{cdylib_name} - Update test infrastructure to support Native Assets in test environments - Add code_assets and hooks as dependencies in generated pubspec.yaml - Remove runtime library path resolution (handled by Native Assets) Technical details: - Asset IDs use format: "package:{package}/uniffi:{cdylib}" - Build hooks register simple names; Dart auto-prefixes with package path - Generated Dart code constructs full asset ID for @Native annotations - Preserved library_file parameter (needed for proc macro metadata extraction) Benefits: - No manual library loading required - Cross-platform library resolution handled automatically - Better integration with Dart's build system - Simpler generated code with declarative @Native functions All 19 tests passing with new Native Assets implementation.
|
@spacebear21 Since I was a bit stuck with how to bundle/package binaries to publish packages with generated bindings on pub.dev or easily use the packages from other projects, I made quite radical changes, but I think they are worth it: The main advantage it has is that the native lib shouldn't be pre-compiled somewhere or cargo-kit shouldn't be used to hook into the Flutter build process anymore. With Native Assets and native_toolchain_rs a build hook does all of this for the correct target automatically. I think this is the way to go, but of course this needs more eyes and thorough testing. As of now it was tested only very limited with bdk-dart here: bitcoindevkit/bdk-dart#12 Let me know what you think |
- Add free + clone entries to callback vtables (0.30.0 layout) - Implement proper handle-cloning via UniffiHandleMap for callbacks - Wire uniffiFree/uniffiClone into vtable init - Add trait handle LSB check and reject foreign trait implementations
…nals When a primitive type (like u16) was only used within optional fields (u16?) and never as a bare type, uniffi-dart would generate FfiConverterOptionalUInt16 that referenced FfiConverterUInt16, but would never generate the base converter itself, causing compilation errors. The issue was in the compound type (Optional/Sequence) rendering logic, which only rendered inner helpers for Sequence types. Now it also renders helpers for primitive types (Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, Float32, Float64) when they haven't been registered yet. This ensures that when Optional<UInt16> is encountered, both FfiConverterUInt16 and FfiConverterOptionalUInt16 are generated.
The previous implementation of rustCall would call the FFI function and immediately deserialize the result before checking if an error occurred. When a Rust function throws an error, the FFI returns garbage data in the result buffer, causing RangeError or other deserialization failures when Dart tries to lift the invalid data. This fix introduces rustCallWithLifter which: 1. Calls the FFI function to get the raw result 2. Checks the error status 3. Only deserializes (lifts) the result if no error occurred Changes: - Add rustCallWithLifter() function in types.rs that separates FFI call from result lifting - Update function generation in functions.rs to use rustCallWithLifter - Update object method generation in objects.rs to use rustCallWithLifter - Keep original rustCall() for void-returning functions This ensures error handling happens before any deserialization, preventing crashes when functions throw expected errors.
With the changes from Uniffi-Dart/uniffi-dart#99, the native libraries can be compiled automagically in a dart build hook.
With the changes from Uniffi-Dart/uniffi-dart#99, the native libraries can be compiled automagically in a dart build hook.
Per the uniffi changelog: "Foreign handles must always have the lowest bit set".
With the changes from Uniffi-Dart/uniffi-dart#99, the native libraries can be compiled automagically in a dart build hook.
This PR updates uniffi to 0.30.0 and with some other fixes I could make binding generation work with it for bdk-ffi which uses HashMap/Map and pro-macros, so possibly 2 of the 5 critical issues are solved with them as well, but I should probably add specific tests for them still.
A second pretty drastic change done here was moving from path based opening of the native libs to using Native Assets, which recently became a stable feature in both Dart and Flutter and is the recommended way now to use native binaries.