char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
688e96e
main
Snapshots
Runtime::create_snapshot(layouts) captures one context per extension layout in
one V8 artifact. Runtime::from_snapshot(options, &snapshot) restores the matching
layout; Runtime::new(options) bootstraps from scratch.
Static and live state
Extension entries run in dependency order. Capture stops before the one-shot
ext:core/bootstrap onRuntimeStart callbacks; cold and restored runtimes both run
those callbacks with fresh native state before public exports are registered.
Static entries must be deterministic in the target and retain no native resources,
pending work, event handlers, or foreign V8 handles. Process metadata, clocks,
live-only V8 intrinsics (SharedArrayBuffer, WebAssembly), and identity-keyed
state about the global proxy belong in live initializers. Restoration replaces
that proxy, so static WeakMap brands on globalThis do not survive.
V8 omits Error.stackTraceLimit during capture. Core temporarily supplies its
default (10) for error locations, then removes it before serialization. Static
changes to the limit are rejected because they would not survive restoration.
Trust and heap ownership
Snapshot::from_bytesis unsafe: only the avril container is validated. The artifact must come from the same core sources, target, V8 build, and flags, except for the dedicated generator's random seed. The layout fingerprint checks framed core sources/native catalog, entry order, extension sources, and op names; it is mismatch detection, not validation of untrusted V8 bytes.- Overlapping isolates must share one read-only heap image. All layouts are
captured together; a different artifact or cold/restored mixture is rejected
with
ReadOnlyHeapInUsebefore isolate creation. Sequential use is allowed. Core identifies a heap family by the whole V8 blob's SHA-256; directv8isolates bypass this accounting. --no-extensible-ro-snapshotalone does not make independent captures compatible: read-only images can differ in pointer padding and the next SharedFunctionInfo ID. Release V8 silently shares the first image; debug V8'sheap/read-only-spaces.cc::VerifyChecksumrejects mismatches. Sharing the wrong function-ID counter can also break debugger lookups.- Runtimes on one thread must be used and dropped in reverse creation order. Leases stay pinned if disposal panics before deleting the native isolate.
- rusty_v8 cannot dispose unfinished creators. On capture failure, core releases its native state, exits the creator if it is current, and leaks it with a warning. Its built-in heap stays pinned until process exit. Foreign handles or other unserializable resources can make V8 abort, not return an error.
Generation and validation
Use a dedicated, short-lived generator process. Call
initialize_snapshot_generator() before other core use for reproducible artifacts:
core records handles in name order, and V8's fixed generator seed controls identity
hashes and mmap hints. This mode refuses live runtimes; normal runtime processes
are never seeded. Reproducibility assumes the same V8 build/platform and that the
kernel honors those hints. This V8 version does not rehash snapshots by default;
avril snapshots are not rehashable, so do not enable --rehash-snapshot.
The CLI's build script uses canonical runtime assembly, guarding live ops except
platform and encoding-label queries. It embeds one artifact for main and nested
workers, with no runtime cache or cold fallback. Generation requires HOST == TARGET,
including CLI checks. The runtime build dependency means cargo check compiles and
runs the host generator, and release builds compile both host and target copies.
Library-only checks can use -p avril-runtime.
Exercise V8's own heap consistency checks as well as ordinary tests:
CARGO_BUILD_JOBS =2CARGO_TARGET_DIR =/tmp/avril-debug-v8V8_FORCE_DEBUG =true \nix shell nixpkgs#cargo-nextest-c cargo nextest run--locked -p avril-core
1# Snapshots 2 3`Runtime::create_snapshot(layouts)` captures one context per extension layout in 4one V8 artifact. `Runtime::from_snapshot(options, &snapshot)` restores the matching 5layout; `Runtime::new(options)` bootstraps from scratch. 6 7## Static and live state 8 9Extension entries run in dependency order. Capture stops before the one-shot 10`ext:core/bootstrap` `onRuntimeStart` callbacks; cold and restored runtimes both run 11those callbacks with fresh native state before public exports are registered. 12 13Static entries must be deterministic in the target and retain no native resources, 14pending work, event handlers, or foreign V8 handles. Process metadata, clocks, 15live-only V8 intrinsics (`SharedArrayBuffer`, `WebAssembly`), and identity-keyed 16state about the global proxy belong in live initializers. Restoration replaces 17that proxy, so static WeakMap brands on `globalThis` do not survive. 18 19V8 omits `Error.stackTraceLimit` during capture. Core temporarily supplies its 20default (10) for error locations, then removes it before serialization. Static 21changes to the limit are rejected because they would not survive restoration. 22 23## Trust and heap ownership 24 25- `Snapshot::from_bytes` is unsafe: only the avril container is validated. The 26artifact must come from the same core sources, target, V8 build, and flags, 27 except for the dedicated generator's random seed. The layout fingerprint checks 28 framed core sources/native catalog, entry order, extension sources, and op names; 29 it is mismatch detection, not validation of untrusted V8 bytes. 30 - Overlapping isolates must share one read-only heap image. All layouts are 31captured together; a different artifact or cold/restored mixture is rejected 32 with `ReadOnlyHeapInUse` before isolate creation. Sequential use is allowed. 33 Core identifies a heap family by the whole V8 blob's SHA-256; direct `v8` 34 isolates bypass this accounting. 35 - `--no-extensible-ro-snapshot` alone does **not** make independent captures 36compatible: read-only images can differ in pointer padding and the next 37 SharedFunctionInfo ID. Release V8 silently shares the first image; debug V8's 38 `heap/read-only-spaces.cc::VerifyChecksum` rejects mismatches. Sharing the wrong 39 function-ID counter can also break debugger lookups. 40 - Runtimes on one thread must be used and dropped in reverse creation order. 41Leases stay pinned if disposal panics before deleting the native isolate. 42 - rusty_v8 cannot dispose unfinished creators. On capture failure, core releases 43its native state, exits the creator if it is current, and leaks it with a 44 warning. Its built-in heap stays pinned until process exit. Foreign handles or 45 other unserializable resources can make V8 **abort**, not return an error. 46 47 ## Generation and validation 48 49Use a dedicated, short-lived generator process. Call 50`initialize_snapshot_generator()` before other core use for reproducible artifacts: 51core records handles in name order, and V8's fixed generator seed controls identity 52hashes and mmap hints. This mode refuses live runtimes; normal runtime processes 53are never seeded. Reproducibility assumes the same V8 build/platform and that the 54kernel honors those hints. This V8 version does not rehash snapshots by default; 55avril snapshots are not rehashable, so do not enable `--rehash-snapshot`. 56 57The CLI's build script uses canonical runtime assembly, guarding live ops except 58platform and encoding-label queries. It embeds one artifact for main and nested 59workers, with no runtime cache or cold fallback. Generation requires `HOST == TARGET`, 60including CLI checks. The runtime build dependency means `cargo check` compiles and 61runs the host generator, and release builds compile both host and target copies. 62Library-only checks can use `-p avril-runtime`. 63 64Exercise V8's own heap consistency checks as well as ordinary tests: 65 66``` sh 67CARGO_BUILD_JOBS=2 CARGO_TARGET_DIR=/tmp/avril-debug-v8 V8_FORCE_DEBUG=true \ 68nix shell nixpkgs#cargo-nextest -c cargo nextest run --locked -p avril-core 69```