nandi/vflutter_ffipublic Fork 0
728acaab2594667f4998fc18812e7c1e2e4cc549
Commits
Clone
git clone https://git.rickub.com/nandi/vflutter_ffi.git
git clone ssh://git@rickub.com/nandi/vflutter_ffi.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

README.md · 210 lines · 8.7 KBmarkdown Blame HistoryRaw
Initial commit: V -> Flutter FFI plugin ad0ccda nandi yesterday1# vflutter_ffi
2
3Write application logic in **V**, call it from **Flutter** over `dart:ffi`.
4
5Built on Flutter's `plugin_ffi` template. No platform channels, no method-channel
6serialisation — Dart calls V through the C ABI directly.
7
8## How it works
9
10V compiles to C. That is the whole trick:
11
12```
13src/vflutter.v --[ v -shared -gc none ]--> C --[ NDK / clang / MSVC ]--> libvflutter.so
14```
15
16V never cross-compiles for the target. It only emits C, and the platform's own
17toolchain owns the ABI, sysroot and flags. That is why Android arm64, iOS
18arm64, Linux, macOS and Windows all work from one source file.
19
20| Platform | Built by | Artifact |
21|---|---|---|
22| Android | NDK via `android/build.gradle``src/CMakeLists.txt` | `libvflutter.so` per ABI |
23| Linux / Windows | Flutter's CMake → `src/CMakeLists.txt` | shared library, auto-bundled |
24| iOS / macOS | CocoaPods compiles pre-generated C | static archive in the app binary |
25
26iOS is the odd one out: Xcode's build sandbox can't run `v`, and App Store
27builds want a static archive. So `tool/gen_ios_sources.sh` generates the C ahead
28of time into `ios/Classes/`, and that is checked in. **Re-run it whenever
29`src/vflutter.v` changes.**
30
31## The two rules
32
33Everything that goes wrong with this bridge goes wrong in one of two ways.
34
35**1. Only C types cross the boundary.**
36`int`, `f64`, `&char`, `voidptr`. Never a V string, array, map, option or
37sumtype — those have V-specific layouts Dart cannot read. Convert at the edge.
38
39**2. The library is built `-gc none`, so V code must free its own temporaries.**
40
41This is the one that bites. Boehm GC can't be cross-compiled per-ABI without
42pain, and a C-ABI library with explicit ownership doesn't need it — but it means
43*every* intermediate allocation inside an exported function leaks unless freed:
44
45```v
46@[export: 'vf_greet']
47fn vf_greet(name &char) &char {
48 n := unsafe { cstring_to_vstring(name) } // allocates
49 res := 'Hello, ${n}, from V!' // allocates
50 out := unsafe { res.str } // handed to the caller
51 unsafe { n.free() } // <-- without this, ~40 bytes/call
52 return out
53}
54```
55
56Measured on this repo: omitting `n.free()` leaks ~12 MB per 300k calls. With it,
57RSS is flat at 300k and 900k calls.
58
59Ownership across the boundary: **V allocates, Dart copies, V frees.** The Dart
60wrapper in `lib/vflutter_ffi.dart` does the `vf_free` in a `finally`, so callers
61never hold a pointer and never leak.
62
Add a justfile f7c3825 nandithebull 20h ago63## Quick start
64
65With [`just`](https://github.com/casey/just):
66
67```
68just mandelbrot # build the V library and run the fractal explorer
Add a WebAssembly backend for the example 728acaa nandithebull 18h ago69just web # serve the same app with V as WebAssembly (no browser opened)
Add a justfile f7c3825 nandithebull 20h ago70just test # analyze + the example's test suite
71just bench # V vs Dart timings for the same frame
72just --list # everything else
73```
74
75Flutter is expected at `~/flutter/bin`; set `FLUTTER_BIN` if yours lives
76elsewhere.
77
Initial commit: V -> Flutter FFI plugin ad0ccda nandi yesterday78## Usage
79
80```dart
81import 'package:vflutter_ffi/vflutter_ffi.dart' as v;
82
Add a WebAssembly backend for the example 728acaa nandithebull 18h ago83await v.initialize(); // required on web, a formality on native
Initial commit: V -> Flutter FFI plugin ad0ccda nandi yesterday84v.add(20, 22); // 42
85v.greet('Flutter'); // "Hello, Flutter, from V!"
86await v.greetAsync('isolate'); // same, off the UI isolate
87```
88
89`-gc none` means no stop-the-world phase and no thread-local runtime state, so
90calls are safe from any isolate. Use `Isolate.run` for anything long enough to
91jank a frame.
92
Add a Mandelbrot example app, and a V kernel worth demonstrating 81540bb nandithebull 20h ago93For bulk data, let Dart own the buffer and have V fill it in place — then
94nothing crosses the boundary that needs freeing, and independent slices can be
95computed concurrently:
96
97```dart
98final view = v.FractalView(width: 800, height: 600, maxIter: 500);
99final pixels = await v.renderParallel(view, tiles: 4); // RGBA8888
100```
101
Add a WebAssembly backend for the example 728acaa nandithebull 18h ago102## Web
103
104The same V source also runs in the browser. `just web` compiles it to
105WebAssembly with emscripten and serves the example app at
106`http://localhost:8080` — it prints the URL and waits rather than launching a
107browser, so open it yourself (`WEB_PORT=9000 just web` to move it). Hot
108restart still works. `dart:ffi` does not exist on web, so a second backend
109drives the wasm module over `dart:js_interop` instead:
110
111```
112lib/src/backend_native.dart dart:ffi
113lib/src/backend_web.dart dart:js_interop + emscripten
114lib/vflutter_ffi.dart picks one with a conditional import
115```
116
117Nothing has to be installed for this. `tool/bin/emcc` is a
118[DotSlash](https://dotslash-cli.com) file that pins emscripten by content
119hash; the toolchain is fetched on first use and cached in `~/.cache/dotslash`.
120Only `dotslash` itself needs to be on `PATH`.
121
122The two backends are held to being the same, not merely similar: `just parity`
123renders a frame with each kernel and compares them byte for byte, and they
124agree exactly — across different compilers, different libm implementations
125(glibc vs musl) and different word sizes (64-bit vs wasm32). Speed is close
126too, ~2-5% off native for the same frame:
127
128```
129800x600 maxIter 100: 68.1 ms native / 69.4 ms wasm
130800x600 maxIter 500: 149.3 ms native / 156.3 ms wasm
131```
132
133Two differences are real and surfaced in the API:
134
135* `await v.initialize()` before anything else. A wasm module cannot be
136 instantiated synchronously. On native it resolves immediately.
137* `v.supportsIsolateParallelism` is false on web. There are no isolates, so
138 `renderParallel` still splits the frame and still produces identical pixels,
139 but the bands run in sequence.
140
141### One trap worth knowing about
142
143V's `vmemcpy` silently skips any copy whose source or destination is
144`<= 0xFFFF` — a null-pointer heuristic that is sound on 64-bit desktop, where
145the low 64 KB is never mapped. On wasm32 emscripten packs static data down at
146address ~1300, so every copy out of a string literal does nothing and V strings
147come back as runs of zero bytes, with no crash and no diagnostic. It only
148appears at `-O1` and above, which makes it look like an optimiser bug.
149`tool/build_wasm.sh` passes `-sGLOBAL_BASE=1048576` to move static data, the
150stack and the heap clear of that check.
151
Add a Mandelbrot example app, and a V kernel worth demonstrating 81540bb nandithebull 20h ago152## Is V faster than Dart?
153
154For the numeric kernel in the example: **no, not meaningfully.** Measured at
155800x600 / 500 iterations on an 8-core Linux box, V through C takes ~152 ms
156against Dart AOT's ~165 ms — inside the noise of a ~10% margin. Dart's AOT
157compiler is good at scalar floating-point loops.
158
159What the bridge does buy you is the ability to *write the logic in V* — or
160reuse V you already have — with a C ABI that is cheap to call and safe to call
161concurrently. Pick it for the language, not for an expected speedup. And build
162with optimisation on: at `-O0` the same kernel is roughly 2x slower than Dart,
163which is what `tool/build.sh` and `src/CMakeLists.txt` now guard against.
164
Initial commit: V -> Flutter FFI plugin ad0ccda nandi yesterday165## Adding a function
166
1671. Export it in `src/vflutter.v` with `@[export: 'vf_yourthing']`, freeing temporaries.
1682. Declare it in `src/vflutter.h`.
1693. Wrap it in `lib/vflutter_ffi.dart`.
1704. `./tool/gen_ios_sources.sh` to refresh the iOS C.
1715. `./tool/build.sh` to smoke-test the host build.
172
173Step 2 also feeds `dart run ffigen --config ffigen.yaml` if you'd rather
174generate the raw bindings than hand-write them.
175
176## Status
177
Add a Mandelbrot example app, and a V kernel worth demonstrating 81540bb nandithebull 20h ago178Verified on Linux with V 0.5.2, Dart 3 and Flutter 3.47: host build, string
179round-trip, isolate dispatch, 900k-call memory stability, and the example app
180built and run as a release Linux binary (CMake -> `v` -> NDK/clang path
Add a WebAssembly backend for the example 728acaa nandithebull 18h ago181included).
182
183The web path is verified end to end as well — wasm built with emscripten 6.0.9,
184the module exercised headlessly under node, the kernel compared byte for byte
185against the native build, and the release web app loaded in headless Chrome
186with its rendered canvas checked for an actual fractal. `just ci` runs all of
187it.
188
189The Android/iOS/Windows glue is written to the standard `plugin_ffi` contract
190but is not exercised here — it needs the respective toolchains.
Initial commit: V -> Flutter FFI plugin ad0ccda nandi yesterday191
192## Layout
193
194```
195src/vflutter.v the V source — the only file you normally edit
196src/vflutter.h C declarations (ffigen input, Xcode input)
197src/CMakeLists.txt V -> C -> shared lib; shared by Android/Linux/Windows
198lib/vflutter_ffi.dart the Dart API callers use
Add a WebAssembly backend for the example 728acaa nandithebull 18h ago199lib/src/backend_*.dart the two backends: dart:ffi and wasm/js_interop
Initial commit: V -> Flutter FFI plugin ad0ccda nandi yesterday200ios/, macos/ pre-generated C + podspec (static archive)
201android/build.gradle NDK build via externalNativeBuild
202tool/build.sh host build + export smoke test
203tool/gen_ios_sources.sh regenerate ios/ and macos/ C after editing the V source
Add a WebAssembly backend for the example 728acaa nandithebull 18h ago204tool/bin/emcc DotSlash file pinning emscripten — nothing to install
205tool/build_wasm.sh V -> C -> wasm for the web build
206tool/test_wasm.mjs headless check of the wasm module (no browser needed)
207tool/check_parity.sh native vs wasm kernel, byte for byte
208tool/check_web.sh loads the built web app in headless Chrome
Add a Mandelbrot example app, and a V kernel worth demonstrating 81540bb nandithebull 20h ago209example/ Flutter app exercising the bridge (see example/README.md)
Initial commit: V -> Flutter FFI plugin ad0ccda nandi yesterday210```