Commit graph

25 commits

Author SHA1 Message Date
Andreas Kling
cd38cdf6cf LibJS: Pass var environment binding counts through FFI
Thread the var environment binding count through the Rust and C++ SFD
metadata helpers wherever the function environment binding count already
travels. This lets CreateVariableEnvironment use the cached var
environment shape for functions with parameter expressions.
2026-05-18 20:35:14 +02:00
Andreas Kling
ef74c1ca55 LibJS: Keep cached bytecode file-backed
Teach Bytecode::Executable to store its instruction stream as either
an owned Vector or a retained Core::ImmutableBytes range. Cached
bytecode materialization now clones the immutable blob owner and lets
the executable point directly into the file-backed cache blob instead
of copying instruction bytes back onto the heap.

Keep a cached instruction data pointer inside the stream wrapper so the
asm interpreter still has a direct hot-path load. Align executable
bytecode payloads in the cache format so mmap-backed instruction
streams satisfy validator and interpreter alignment requirements.
2026-05-18 20:35:14 +02:00
Andreas Kling
a5ba300186 LibJS: Split dynamic environment lookups from coordinates
Add separate bytecode instructions for environment lookups that must
stay dynamic, such as eval- and with-sensitive scopes. Keep the
coordinate variants for eagerly resolved declarative environments so
their operands can be treated as immutable at runtime.

This removes cached-coordinate mutation from the interpreter paths and
updates the bytecode expectations for the new dynamic lookup opcodes.
2026-05-18 20:35:14 +02:00
Andreas Kling
1ce4242b4b LibJS: Store bytecode cache indexes instead of pointers
Store compact cache indexes in bytecode instructions instead of raw
pointers to the executable cache vectors. This keeps the instruction
stream independent from heap addresses and removes pointer fixups when
materializing cached bytecode.

Resolve the mutable cache pointers at execution time from the current
Executable. Bytecode test expectations are updated for the smaller cache
operands and resulting instruction offsets.
2026-05-18 20:35:14 +02:00
Andreas Kling
30314e8ad0 LibJS: Lazily decode cached function bytecode
Store nested function executables in the bytecode cache as
length-prefixed payloads, and keep those payloads mapped instead of
decoding every function when materializing the outer script or module.
Decode and validate a cached function executable only when that
function is installed for lazy materialization.

Validate cached executable bytecode before storing it on shared data,
and recurse source range validation through the lazy executable
payload. This keeps corrupt cache entries on the recoverable
materialization path instead of crashing when a function is first used.

This avoids turning the warm disk cache into retained dirty heap for
all uncalled nested functions on large sites.
2026-05-17 08:58:45 +02:00
Andreas Kling
7e7d57fb1a LibJS: Reject impossible bytecode cache table counts
Reject counted bytecode cache table payloads whose count is larger than
the payload byte length. All currently encoded records and constants use
at least one payload byte, so these counts cannot be valid. Accepting
them would only risk oversized allocations during materialization.

Add Rust coverage for record sequence and constant table headers with
impossible counts.
2026-05-16 08:13:35 +02:00
Andreas Kling
6c615c40f3 LibJS: Borrow bytecode cache executable tables
Store executable-side tables as counted encoded payloads instead of
allocating decoded Rust vectors while reading the bytecode cache. This
keeps identifier, property key, string, exception handler, source map,
local variable, shared function, and class blueprint tables borrowed
from mapped cache blobs until validation or materialization walks them.

Bump the bytecode cache format because these table encodings now carry
raw payload lengths and preserve their own nested alignment.
2026-05-16 08:13:35 +02:00
Andreas Kling
ac02d2e956 LibJS: Borrow bytecode cache constant tables
Store executable constants as a counted encoded payload instead of a
sequence of decoded records. Decoding a mapped cache blob can now retain
that payload as a borrowed range and postpone inflating ConstantValue
objects until executable materialization actually needs them.
2026-05-16 08:13:35 +02:00
Andreas Kling
1ad3039001 LibJS: Align bytecode cache UTF-16 payloads
Bump the bytecode cache format and pad serialized UTF-16 strings to u16
alignment. This lets mapped cache blobs expose decoded UTF-16 payloads
as FFI slices directly instead of re-decoding them into temporary
aligned buffers during materialization.

Update the bytecode cache corruption helpers to skip the new string
padding when walking serialized blobs.
2026-05-16 08:13:35 +02:00
Andreas Kling
6f4bdb3cb6 LibJS: Borrow bytecode cache UTF-16 strings
Keep decoded executable strings as ranges into the mapped bytecode cache
blob when the decoder has a foreign owner. Materialize aligned UTF-16
buffers only when passing the data to C++ or rebuilding generator-owned
structures for executable creation.

This covers executable string tables, string constants, local variable
names, nested function metadata, and cached class blueprint strings. Add
Rust coverage for borrowing decoded UTF-16 payloads from a foreign blob.
2026-05-16 08:13:35 +02:00
Andreas Kling
a646f9d0bf LibJS: Borrow mapped bytecode cache executable bytes
Keep executable bytecode payloads decoded from owner-backed bytecode
cache blobs as ranges into the original blob instead of copying them
into Rust Vec allocations. The mapped blob owner is held by decoded
executable records, including lazy nested function executables, so the
borrowed bytecode remains alive until materialization copies it into the
final C++ Executable.

Use the owner-backed decoder for HTTP bytecode cache hits and keep the
plain byte decoder for tests and in-memory callers. Add coverage for
materializing bytecode cache data from an ImmutableBytes mapped file.
2026-05-16 08:13:35 +02:00
Andreas Kling
a31c2c388b LibJS: Stop persisting basic_block_start_offsets on Executable
Keep basic block offsets as construction-only metadata rather than
storing them on every Executable. The validator now receives the offsets
through a transient Rust FFI span, and the bytecode dump rebuilds block
starts by scanning labels, terminators, and exception handler metadata.

Drop the table from the bytecode cache format and bump the format
version so old caches are rebuilt. This removes a field that was only
used by validation and bytecode dump paths.
2026-05-14 12:08:12 +02:00
Andreas Kling
21cbfb3cb1 LibJS: Drop source ranges from bytecode source maps
Store source map locations as bytecode offset, line, and column.
Runtime consumers only emit the start line and column, so source end
positions and source text offsets do not need to be carried through
Executable source maps, bytecode cache serialization, or the Rust FFI.

Keep SourceCode's internal position cache able to track source text
offsets so callers can still translate source offsets to line and
column pairs when needed. Hash dump-bytecode IDs from the name, first
source position, and bytecode size instead of source slices that need
end offsets.

Bump the bytecode cache format version for the slimmer serialized
source map entry shape.
2026-05-14 09:41:03 +02:00
Andreas Kling
e926e86f8d LibJS: Materialize compiled function bytecode lazily
Keep fully compiled function bytecode in its Rust-side form until the
function is called for the first time. This covers decoded disk cache
records and freshly precompiled bytecode, so startup avoids eagerly
allocating every nested function executable.

Validate cached function bytecode before accepting a cache entry. This
keeps the existing failure behavior for corrupt on-disk cache data. Add
coverage for bytecode-cache and freshly precompiled functions to assert
that nested executables stay absent after script materialization, then
appear after the function is called.
2026-05-14 08:15:01 +02:00
Andreas Kling
4ef3c076f9 LibJS: Preserve imported names in module bytecode cache
Store the original imported binding name when serializing a re-export of
an imported binding as an indirect export. The cache previously kept the
local alias, so materialized modules could fail to resolve valid exports
such as `export { renamed as default }`.

Bump the bytecode cache format version so existing blobs with the stale
metadata are ignored. Add coverage for both normal module loading and
materializing this pattern from bytecode cache.
2026-05-13 20:54:10 +02:00
Andreas Kling
afa1f77252 LibJS: Materialize decoded bytecode cache blobs
Create parser-free script and module materializers for decoded cache
blobs. Cached functions create SFDs without Rust compile inputs and
attach their precompiled executable immediately, while declaration
metadata is populated from decoded records.

Treat cache blobs as external input from the HTTP disk cache. Run
bytecode validation unconditionally before fixing up cache pointers, and
reject decoded source ranges or metadata indices that would be
out-of-bounds during C++ materialization.

Report executable validation failures as parser errors so callers can
reject corrupt sidecars and fall back to source compilation. LibJS tests
cover corrupt top-level bytecode, declaration bytecode, and declaration
source spans.
2026-05-06 08:20:06 +02:00
Andreas Kling
b265694f0d LibJS: Match bytecode cache blobs to their source
Store a SHA-256 fingerprint of the decoded source text in each bytecode
cache blob, and require callers to provide the expected fingerprint when
validating or decoding a blob.

This rejects sidecars for stale HTTP cache entries whose URL and request
headers still match but whose source body has been replaced. Bytecode
cache tests cover the mismatched-source rejection path.
2026-05-06 08:20:06 +02:00
Andreas Kling
de9f2b8343 LibJS: Decode bytecode cache blobs over FFI
Expose an owned decoded bytecode cache handle through RustIntegration.
This lets C++ callers keep validated metadata and executable records in
Rust-owned cache structures without invoking the parser.

Extend the js bytecode cache validation mode to create and free the FFI
handle so blob generation exercises the ownership path.
2026-05-06 08:20:06 +02:00
Andreas Kling
fb11f81305 LibJS: Cache declaration function bytecode
Precompile top-level function declarations used during script and
module instantiation while producing a full bytecode cache entry. Keep
normal off-thread execution artifacts on the existing eager path, and
store declaration records separately from executable nested functions.

Place those records after declaration metadata in the cache blob, and
bump the blob version so older sidecars are rejected.
2026-05-06 08:20:06 +02:00
Andreas Kling
9b33cd1c21 LibJS: Return decoded bytecode cache blobs
Make bytecode cache validation return a decoded blob containing the
validated program record, declaration metadata, and executable records.

This keeps a single Rust-owned object alive for consumers that need to
materialize cached bytecode after validation succeeds.
2026-05-06 08:20:06 +02:00
Andreas Kling
16f4775a99 LibJS: Decode bytecode cache executable records
Decode bytecode cache executable records into owned Rust data instead
of only skipping over their serialized fields during validation.

Keep cached bytecode, constants, nested functions, and class blueprints
available for later materialization without rebuilding ASTs.
2026-05-06 08:20:06 +02:00
Andreas Kling
4f1bf52eb3 LibJS: Persist bytecode cache declaration metadata
Store and decode script declaration-instantiation metadata and module
import, export, request, and declaration metadata in bytecode cache
blobs.

Return decoded metadata as owned Rust records so warm-cache script and
module construction can recover parser-derived facts from the sidecar.
2026-05-06 08:20:06 +02:00
Andreas Kling
c5b6739c47 LibJS: Tag bytecode cache blobs with program type
Record whether each bytecode cache blob contains a classic script or a
module, and pass that type through the serializer call sites.

Require validation callers to provide the expected program type so
script and module sidecars cannot be reused for the wrong loader.
2026-05-06 08:20:06 +02:00
Andreas Kling
b327f61ab3 LibJS: Validate bytecode cache blob records
Add structural validation for bytecode cache blobs using the serialized
record layout. The decoder shares primitive helpers across cache
sections instead of duplicating flat parsing logic.

Reject bad magic values, unsupported versions, invalid enum tags, and
truncated sections before materialization. Bound sequence reads against
the remaining blob so malformed sidecars cannot force large allocations.
2026-05-06 08:20:06 +02:00
Andreas Kling
96a6782800 LibJS: Serialize compiled bytecode cache blobs
Add a versioned Rust bytecode cache writer for fully compiled programs.
The blob records executable bytecode, metadata tables, source maps,
exception handlers, nested function bytecode, and class blueprints
without materializing GC objects.

Expose the serialized blob through RustIntegration as an owned
ByteBuffer so Web-facing callers can store it as HTTP cache data.
2026-05-06 08:20:06 +02:00