Add a helper for finding the first position in a sorted container where
needle can be inserted while preserving sort order. This gives callers a
lower-bound insertion point.
Cover empty inputs, duplicate values, custom comparators, and constexpr
use in TestBinarySearch.
macOS POSIX shared memory objects returned by shm_open() only support
mmap() and ftruncate(). Calls to read(), write(), pread() or pwrite()
all fail with ESPIPE. The cranelift-compiler child was using pread/
pwrite via File::read_exact_at/write_all_at, which works fine on
Linux where the parent uses memfd_create(), but fails on macOS where
the parent uses shm_open().
Add a macOS-specific path that mmaps the inherited shm fd with
MAP_SHARED, mirroring the Windows MapViewOfFile path. Writes through
the mapping are automatically visible to the parent, so the explicit
write-back step at the end is also skipped on macOS.
On macOS, the cranelift JIT compilation step works by creating an
anonymous shared memory segment via shm_open() in the parent process
and passing the fd as an argument to the spawned cranelift-compiler
child. POSIX requires shm_open() to set FD_CLOEXEC on the returned
fd, which caused the child to immediately fail with EBADF when it
tried to read from the inherited fd number.
Clear FD_CLOEXEC after shm_open() so the fd actually survives the
spawn. The Linux memfd_create() path is unaffected since we call it
with flags=0 (no MFD_CLOEXEC), and Windows passes an inheritable
HANDLE instead of an fd.
paint_descendants already skipped positioned descendants at stack level
zero so they're only painted by paint_internal's pass, but the SVG-root
and grid-item branches returned before reaching the skip, making them
be painted twice.
Move the skip above them to make it apply to all deferred positioned
descendants uniformly.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/9277
Previously, adding or removing a constructed `CSSStyleSheet` via
`document.adoptedStyleSheets` invalidated style for the entire document
or shadow root. We now route this through the same style invalidation
machinery that's used when we add or remove `<style>` elements,
resulting in fewer full invalidations.
Allow the WebIDL parser to accept files without a top-level interface,
and have the exposed-interface generator skip those modules.
With that in place, register support IDLs through
libweb_js_bindings() as well, including generated CSS IDLs from the
build directory, and remove the separate support-idl plumbing.
This is enabled now that the IDL generator rules have been simplified
so that every IDL file produces a corresponding header and cpp file.
Move the cache's Skia-dependent members (sk_sp<SkImage>, the
DecodedImageFrame-keyed HashMap, and the SkColorSpace-based key traits)
into a private Impl struct defined in the .cpp file. The header now only
needs forward declarations for SkImage and sk_sp, so including the cache
no longer drags in Skia headers into translation units that just want to
hold a pointer to the cache.
DisplayListDataSpan offsets used to point into the owning DisplayList's
command byte buffer. That made copied command sequences
position-dependent: whenever commands were captured, replayed, or
appended elsewhere, every inline data span inside command payloads had
to be decoded, adjusted, and written back.
Store those spans relative to the containing command payload instead.
CommandPayloadBuilder still computes padding from the final byte-stream
layout so inline data keeps the alignment expected by typed readers, but
the recorded offset is now local to the payload. This makes command
sequences self-contained byte ranges that can be copied without
rewriting their embedded spans.
Move inline data lookup to DisplayListPlayer by tracking the current
command payload while dispatching a command. The Skia player now
resolves glyph runs, gradient stops, path data, dash arrays, and nested
display list command bytes from that current payload instead of from the
active display list's global byte buffer.
With spans no longer absolute, remove the offset-adjustment helpers from
DisplayList.cpp and simplify append_command_sequence() and
copy_command_sequence_from() to copy command bytes directly. Add small
display-list object byte helpers and tighten the command/header
byte-stream contract to require trivially copyable payload structs.
Paint styles were stored as display list resources, which kept path
painting tied to in-process C++ objects. Move the gradient and pattern
payload into FillPath and StrokePath instead, with gradient stops
serialized as parallel color and position spans.
This is prep work for making display lists serializable across the IPC
boundary without needing to marshal SVG paint style objects separately.
Sync our local copy of `idlharness.js` with upstream WPT changes.
This also fixes worker IDL harness tests by replacing the local
`window.location` based URL handling in `fetch_spec()`. Worker globals
do not expose `window`, so worker IDL tests could fail during setup
before any interface checks ran.
The local `window.location` logic was originally needed for `file://`
execution, but this is no longer needed as our IDL tests are now
served over HTTP.
Introduce MemoryBuffer, a memory backing store that uses mmap to reserve
the full wasm32 address space (4 GiB + guard pages) upfront, growing
without a copy and falling back to a ByteBuffer when mmap fails.
Also let Frame know how to handle non-owned locals (to e.g. allow
allocating them on the native stack.)
A finalized GC::Function should no longer be callable. Clear the
underlying AK::Function during finalization so stale references fail as
empty callbacks instead of calling through finalized capture storage.
Delayed preload and image animation callbacks can outlive the objects
they notify. Incremental sweeping makes this easier to hit because stale
callback state may be reclaimed before the delayed work runs.
Use a weak link element when firing preload load and error events, and
use a weak image style value from animated image timers instead of a raw
pointer.
Executable caches can retain weak pointers to shapes and prototypes
across collections. With incremental sweeping, a previous sweep may have
already freed the block behind one of those pointers by the time pruning
runs for weak containers.
Use the live HeapBlock registry before reading cached cells. This
matches the other weak containers updated for incremental sweeping.
SharedResourceRequest can be finalized while fetch work is pending.
Use weak fetch callbacks so late work no-ops once the request is gone.
Finalization now only drops local references and callbacks. It does not
stop fetch, since that allocates new algorithms during collection.
Tear down the MediaControls (and its Core::Timer-driven hover handler)
during HTMLMediaElement finalization. Otherwise, between weak-clearing
and incremental sweep destroying the element, a queued hover timer
event can still fire and trip a VERIFY against an already-cleared
GC::Weak reference to a shadow tree node.
The phase breakdown was authored when sweep_dead_cells was the
single STW sweep phase, with sweep_callbacks and weak-container
work nested under it. After incremental sweep landed, that nesting
no longer matches reality: sweep_callbacks runs at STW every
collection, the weak-container prune is its own STW step, and
sweep_dead_cells only runs for CollectEverything.
Promote prune_weak_containers and sweep_callbacks to top-level
phases so they show up correctly in the report, and gate the
sweep_dead_cells subsection on a non-zero time so normal
collections no longer print a wall of zero rows.
The prune-weak-containers loop was previously untimed, leaving an
unaccounted gap in the per-GC totals. Wire it through the existing
ScopedPhaseTimer mechanism. The PhaseTimings field for it is
renamed from sweep_weak_containers_us to prune_weak_containers_us
to disambiguate from sweep_weak_blocks_us, which times a different
piece of work.
Clear NodeWithStyle image observers during finalization so pending image
loads cannot call back into observers owned by unreachable layout nodes.
Incremental sweep leaves finalized cells allocated until their block is
swept, so waiting for the C++ destructor is too late.
Treat active Platform::Timer objects as event-loop roots so their GC
callbacks stay marked while the underlying Core::Timer can still fire.
Finalize unreachable timers by stopping the Core timer and dropping the
callback, preventing incremental sweep from leaving a timer with a raw
pointer to a swept GC::Function.
Record incremental sweep batch timing while LIBGC_LOG_LEVEL enables GC
reporting. Print the batch summary once the incremental sweep fully
finishes, so normal collection reports include the delayed sweep work
instead of leaving the sweep section empty for incremental collections.
Now that Heap maintains a persistent set of live heap blocks, use it
in the marking and conservative root scanning phases instead of
rebuilding a local copy on every collection cycle.
Maintain a HashTable<HeapBlock*> of live heap blocks in the Heap,
updated on block creation and destruction.
Weak containers (WeakMap, WeakSet, WeakRef, FinalizationRegistry)
now check block liveness before accessing cell memory in their
remove_dead_cells() methods. This prevents use-after-free when
blocks have been freed during incremental sweeping.
Move weak container cleanup (remove_dead_cells) out of both
sweep_dead_cells() and start_incremental_sweep() to the place
where it is actually safe to inspect cell state: collect_garbage().
Previously, remove_dead_cells could access cells that had already
been swept and poisoned by ASAN, causing use-after-poison crashes
when a new GC triggered while an incremental sweep was in progress.
Instead of sweeping all heap blocks in one go after marking, sweep
incrementally, one block at a time, interleaved with program execution.
This significantly reduces worst-case GC pause times by spreading
sweep work across multiple smaller time slices.
Sweep is driven by two complementary mechanisms:
1. Timer-based sweeping: A 16ms repeating timer drives background
sweep work, processing blocks for up to 5ms per timer fire.
2. Allocation-directed sweeping: Each allocator sweeps its own
pending blocks before creating new ones, ensuring forward
progress even without timer events.
Each allocator maintains its own list of blocks pending sweep,
and allocators with pending work are tracked in a separate list
for efficient timer-driven sweeping.
Key implementation details:
- Newly allocated cells during sweep are marked immediately to
prevent premature collection.
- Mark bits are cleared incrementally as each block is swept,
rather than in a separate pass over the entire heap.
- Finalization and weak reference processing remain stop-the-world
since they must complete atomically before any sweeping occurs.
Use GC::Timer instead of Core::Timer for the cursor blink timer so
that capturing [this] in the callback properly protects the Document
from garbage collection.
Move HeapTimer out of LibWeb and into LibGC as GC::Timer, inheriting
from GC::Cell instead of JS::Cell. Add a finalize() override that
stops the timer, ensuring it is cleaned up during GC finalization.
Reading LIBGC_LOG_LEVEL once at startup picks the verbosity for the
collection reports:
0/default: silent.
1: per-GC report with totals and detailed per-phase timings.
2+: everything in level 1, plus the full block allocator dump.
The existing collect_garbage(..., print_report=true) entry point still
works and behaves like a per-call floor of level 1, so the DevTools
"collect-garbage" inspector request keeps emitting a report regardless
of the env var. The allocator dump is now off by default at level 1
since most reports do not need it.
Right-aligned the percentage column in the breakdown so the numbers
line up cleanly.
The per-collection report now includes microsecond timings (with
percentage of total) for each phase and major subphase:
gather_roots
must-survive scan / embedder roots / explicit roots
conservative roots
register scan / stack scan / conservative-vector / cell lookup
mark_live_cells
initial visit / BFS marking / clear uprooted
finalize_unmarked_cells
sweep_weak_blocks
sweep_dead_cells
block iteration / weak containers / sweep callbacks
block reclassify / update threshold
Timings are recorded via a small RAII helper into a file-scope struct,
keeping all the plumbing inside Heap.cpp so the public Heap.h surface
stays untouched. Sweep stats now travel back to collect_garbage() the
same way, which lets the report move out of sweep_dead_cells() into a
single print_gc_report() helper run after every phase has completed.
Switch the per-GC report to a precise timer and microsecond output, and
format all byte counts via human_readable_size so they read naturally
(e.g. "12.3 MiB" instead of "12923847 bytes").
Use a direct anonymous mapping for POSIX BlockAllocator chunks and trim
any temporary padding needed to make the live 2 MiB chunk HeapBlock-
aligned.
The GC only needs each 16 KiB HeapBlock slot aligned so from_cell() can
recover the block base by masking low bits. Request that alignment from
mach_vm_map() as well, rather than aligning whole chunks to 2 MiB.
WPT reftests capture screenshots through
WebDriver::draw_bounding_box_from_the_framebuffer(), which creates a
scratch 2D canvas outside normal script execution.
This regressed when CanvasRenderingContext2DSettings started using the
generated bindings conversion path. That path now looks at the current
realm even for this codepath, whereas the previous handwritten
conversion did not, so WebDriver screenshot capture could hit a missing
running execution context and crash.
Push a TemporaryExecutionContext while creating the scratch canvas so
the generated conversion has the realm it expects and WPT reftests no
longer crash.
StringBuilder::clear() did not reset m_utf16_builder_is_ascii. When a
UTF-16 mode builder processed a non-ASCII character then was cleared
and reused, subsequent ASCII content was stored as char16_t. The
to_utf16_string() path then corrupted the first code unit to null via
placement-new overlap in Utf16StringData::from_string_builder().
This caused the HTML parser's shared m_character_insertion_builder to
produce corrupted script text nodes when a non-ASCII character (e.g.
×) appeared in an earlier element, breaking inline script
execution with "Unexpected token Invalid" at line 1 column 1.
Split the generator decision for shared prototype and constructor
objects. Interfaces with custom prototype behavior can still use the
shared InterfaceConstructor when the constructor object itself has no
custom GC cell state.
Teach Intrinsics how to register a shared constructor for an existing
custom prototype object, and keep LegacyFactoryFunction aliases wired up
when the primary interface objects are shared.