Skip to main content

Module bytecode_cache

Module bytecode_cache 

Source
Expand description

Cross-process disk cache for compiled QuickJS bytecode.

Compiling a rolldown bundle to bytecode (bundle_and_compile) or a plugin file (compile_and_extract_plugins) costs ~15 ms cold per process. The in-memory cache only helps within one process; a fresh ferridriver bdd / MCP server start pays it again. This persists the bytecode (plus its source map / manifests) to disk so an unchanged source tree skips BOTH rolldown and the QuickJS compile entirely.

§Soundness

Module::load on bytecode is unsafe: it trusts the input was produced by an identical QuickJS build with native endianness. A disk cache crosses process (and machine) boundaries, so every entry lives under an abi_tag-named directory folding the QuickJS version (which tracks the on-disk BC_VERSION), target arch, endianness, and pointer width. Bytecode is only ever loaded from the directory matching the running toolchain — a mismatched build simply misses and recompiles. Bumping rquickjs changes JS_GetVersion() and thus the directory, so stale bytecode is never loaded.

§Freshness

A bundle inlines its whole import graph, so the entry file’s hash is not enough — an edited (but still-imported) helper must invalidate. Each entry records the content hash of every transitive input (the source map’s sources); a load re-hashes them all and misses on any change, addition, or deletion.

Structs§

CacheEntry
One cached compile: the bytecode plus the auxiliary data each caller needs to reconstruct its result without re-running rolldown.

Functions§

collect_inputs
Collect the transitive input set for a bundle: the entry files plus every sources entry in the source map, canonicalized and deduped.
entry_key
A stable key for a set of entry paths (canonicalized, order-independent). The transitive content check on load is what actually guards freshness; this only needs to be collision-free across distinct bundle requests.
load
Load a cached compile for key, validating that every recorded input still hashes identically. Returns None on any miss, mismatch, or IO error (the caller then compiles and stores).
store
Persist a freshly compiled key -> bytecode entry. Best-effort: any IO failure is swallowed (the cache is an optimization, never a correctness dependency). Writes are atomic via temp-file + rename so a concurrent or crashed writer never exposes a torn manifest.