char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somsnapshot: embed shared main and worker startup in the CLI688e96e

main
3.9 KiB69 linesraw

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_bytes is 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 ReadOnlyHeapInUse before isolate creation. Sequential use is allowed. Core identifies a heap family by the whole V8 blob's SHA-256; direct v8 isolates bypass this accounting.
  • --no-extensible-ro-snapshot alone 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's heap/read-only-spaces.cc::VerifyChecksum rejects 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=2 CARGO_TARGET_DIR=/tmp/avril-debug-v8 V8_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
26  artifact 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
31  captured 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
36  compatible: 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.
41  Leases stay pinned if disposal panics before deleting the native isolate.
42- rusty_v8 cannot dispose unfinished creators. On capture failure, core releases
43  its 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 \
68  nix shell nixpkgs#cargo-nextest -c cargo nextest run --locked -p avril-core
69```