Commit graph

514 commits

Author SHA1 Message Date
Andreas Kling
be5320b67c LibJS: Evaluate ToNumber for out-of-bounds typed array writes
The interpreter's fast path for PutByValue on a typed array treated an
out-of-bounds index as a silent no-op and returned without touching the
value. That is observably wrong: TypedArraySetElement evaluates
ToNumber(value) before checking the index, so a value with a valueOf
side effect must still have that side effect run even when the store is
ultimately discarded.

Fall back to the slow path on an out-of-bounds or otherwise invalid
index instead of reporting success. The slow path runs the full
TypedArraySetElement algorithm, which performs the coercion and then
discards the write. Direct assignment now matches Reflect.set, which
already went through the slow path.

Fixes the staging/sm typed array out-of-bounds ToNumber test262 case
and adds a test-js regression covering direct assignment, Reflect.set,
and Reflect.defineProperty.
2026-06-17 21:24:33 +02:00
Andreas Kling
1f36340419 LibJS: Remove unused C++ local variable wrapper
The Rust bytecode generator only passes local variable names to C++ now,
and no C++ code observes declaration kind metadata from LocalVariable.
Store local names directly as Utf16FlyString values and remove the stale
C++ wrapper type.
2026-06-15 02:41:57 +02:00
Andreas Kling
1979d24783 LibJS: Remove unused bytecode instruction stream iterator
InstructionStreamIterator no longer has any C++ users now that bytecode
block collection has moved to Rust. Remove the iterator and include the
bytecode field types needed by generated C++ instruction definitions
directly in Instruction.h.
2026-06-15 02:41:57 +02:00
Andreas Kling
e5bcffc3d5 LibJS: Move bytecode block counting to Rust
Use the Rust bytecode dumper's basic block collection logic for the
metadata block count. This removes the last C++ bytecode label walk and
lets us delete the generated C++ label and operand visitor helpers.
2026-06-15 02:41:57 +02:00
Andreas Kling
984d3033e9 LibJS: Remove obsolete bytecode dump formatting helpers
The Rust bytecode dumper now formats exception handler labels, raw
operands, builtins, labels, and registers. Remove the C++ dump-only
formatters and flatten Operand to expose only the runtime value-array
layout that C++ still observes.
2026-06-15 02:41:57 +02:00
Andreas Kling
7a6af95db3 LibJS: Move bytecode instruction dumping to Rust
Generate Rust bytecode dump helpers from Bytecode.def and route
Executable::dump() through them for instruction stream formatting.

Add a small Rust runtime::value helper for decoding encoded LibJS
Values so immediate Value operands are formatted on the Rust side. C++
callbacks remain only for local names and GC-backed Value payloads that
still need LibJS object access.

Remove the generated C++ to_byte_string_impl() methods and the old
Instruction::to_byte_string() dispatch. The bytecode dump tests cover
output compatibility.
2026-06-15 02:41:57 +02:00
Andreas Kling
a29e1f5cf3 LibJS: Remove unused Executable::dump_to_string
The bytecode dump path only writes directly to stderr now.
Remove the unused string-returning dump API.

Also remove the private helper mode that only existed for that API.
2026-06-14 20:27:59 +02:00
Andreas Kling
ef8ac6ea7d LibJS: Remove unused C++ bytecode block classes
The Rust bytecode generator now owns basic block construction.
The old C++ BasicBlock class no longer has any users.

Label no longer needs to translate from BasicBlock.
Remove the now-empty Label.cpp from the build as well.
2026-06-14 20:27:59 +02:00
Andreas Kling
0af548b27d LibJS: Sync AsmInt program counter before slow paths
Move the execution context program counter update from ASM_TRY() to the
generated slow-path call boundary. Slow paths still enter C++ with the
current bytecode offset visible to stack and source location code, while
ASM_TRY() only handles completion unwrapping and exception dispatch.
2026-06-14 20:27:59 +02:00
Andreas Kling
ad7002ba99 LibJS: Pass instruction pointers to AsmInt slow paths
Have generated AsmInt calls pass the current instruction pointer as a
third argument to slow-path handlers. This lets the C++ handlers use a
typed Op pointer directly instead of refetching bytecode from the VM and
recomputing the instruction address from the program counter.
2026-06-14 20:27:59 +02:00
Andreas Kling
213403542c LibJS: Remove AsmInt slow path stats collection
Remove the optional slow path hit counters from AsmSlowPaths.cpp. This
also drops the registration call from the AsmInt entry path, leaving
slow paths focused on executing the out-of-line instruction behavior.
2026-06-14 20:27:59 +02:00
Andreas Kling
97a6807ffa LibJS: Remove JS_BYTECODE_DEBUG
Remove the stale bytecode execution debug hook from Interpreter.cpp now
that bytecode dispatch always enters AsmInt directly. The remaining
bytecode dump flag is separate and still used by parser/codegen paths.
2026-06-14 20:27:59 +02:00
Andreas Kling
8fee268851 LibJS: Call AsmInt directly from run_executable
Remove the empty AsmInterpreter wrapper and the VM::run_bytecode()
trampoline now that the bytecode interpreter only enters AsmInt. Move
the stack-limit check and generated assembly entry call into
run_executable(), then drop the stale wrapper source file and includes.
2026-06-14 20:27:59 +02:00
Andreas Kling
060ba41a84 LibJS: Remove unused VM interpreter helpers
Remove VM helpers that became unused after bytecode execution stopped
using the generic interpreter path. The AsmInt entry path now owns these
transitions directly.
2026-06-14 20:27:59 +02:00
Andreas Kling
2aa9535635 LibJS: Move final simple opcodes into AsmInt
Move the remaining simple SetLexicalEnvironment, IsCallable and
LeavePrivateEnvironment opcodes into the AsmInt DSL. These handlers do
not need C++ slow-path support.
2026-06-14 20:27:59 +02:00
Andreas Kling
c1415a544f LibJS: Split AsmInt slow paths out of Interpreter.cpp
Move the C++ slow paths used by AsmInt into their own translation unit.
This leaves Interpreter.cpp focused on VM entry and bytecode metadata
helpers instead of carrying the slow-path implementation body.
2026-06-14 20:27:59 +02:00
Andreas Kling
6e6726b612 LibJS: Move instruction bodies into AsmInt slow paths
Move the remaining bytecode instruction implementations out of
execute_impl() and into AsmInt slow paths. Remove the execute_impl()
bodies once their only caller is gone, leaving instruction classes as
bytecode data containers.
2026-06-14 20:27:59 +02:00
Andreas Kling
7c8e3732c7 LibJS: Remove AsmInt generic fallback dispatch
Remove the generic fallback dispatch once every bytecode opcode has a
real AsmInt handler. Invalid dispatch table entries still route through
the fallback function as a defensive trap.
2026-06-14 20:27:59 +02:00
Andreas Kling
5bc002d71c LibJS: Move property and call handlers into AsmInt
Move property access, iterator, object property iterator, import, class
and argument-array call opcodes out of the generic fallback path. Keep
the semantic work in C++ slow paths and dispatch to them from AsmInt.
2026-06-14 20:27:59 +02:00
Andreas Kling
058f63efd2 LibJS: Move control and binding handlers into AsmInt
Move the remaining control-flow, conversion, creation, delete, binding,
private-name and environment-related fallback handlers into AsmInt. This
keeps the generic fallback path shrinking while leaving complex behavior
in C++ slow paths.
2026-06-14 20:27:59 +02:00
Andreas Kling
fff128a8cc LibJS: Move simple bytecode handlers into AsmInt
Move simple fallback handlers into the AsmInt DSL or dedicated slow-path
calls. This covers straightforward allocation, environment setup,
argument creation, completion state, template object, async iterator and
function allocation opcodes.
2026-06-14 20:27:59 +02:00
Andreas Kling
5ca52d2a77 LibJS: Remove generic bytecode interpreter
Remove the C++ bytecode interpreter dispatch loop now that AsmInt is the
only bytecode execution engine. Keep the existing AsmInt fallback path
for instructions that have not yet been moved into assembly or C++ slow
path handlers.
2026-06-14 20:27:59 +02:00
Aliaksandr Kalenik
ac0e0833e7 LibJS: Enable AsmInterpreter on Windows
Enable the asm interpreter on Windows for both x86_64 and ARM64. The
x86_64 backend now emits COFF assembly using the Win64 ABI. It handles
argument registers, non-volatile register saves, shadow space, SEH
unwind directives, and raw-native sret lowering. Its epilogue is left
as normal x64 instructions instead of ARM64-style SEH epilogue
directives, which older ClangCL assemblers reject.

The AArch64 backend now emits Windows ARM64 COFF assembly as well,
including COFF relocations, .rdata dispatch tables, SEH unwind metadata,
frame sizing, handler alignment rules, and raw-native sret lowering.
CMake selects COFF for Windows asmint output and enables generation for
both Windows architectures.

AsmIntGen coverage checks the Windows x64 epilogue output and the ARM64
COFF unwind output. test-js and test262 have no regressions with asmint
enabled compared with the C++ interpreter.
2026-06-14 05:01:27 +02:00
Tim Ledbetter
36ed97128a LibJS: Stop scanning the shape once an enumerable string key is found 2026-06-09 16:56:54 +02:00
Andreas Kling
164ed80244 Meta: Enable exit-time destructor warnings for libraries
Enable -Wexit-time-destructors for all in-tree library targets and
update process-lifetime library statics so they no longer register
exit-time destructors. Long-lived caches, lookup tables, singleton
registries, and generated constants now use NeverDestroyed or leaked
references where the data is intended to live until process exit.

Update LibWeb, LibLine, and the binding generators so regenerated
sources follow the same rule instead of reintroducing destructed
statics.
2026-06-04 19:20:49 +02:00
Luke Wilde
d9e7109d29 LibJS: Use conservative GC containers for unrooted property-key sets 2026-05-28 21:16:23 +02:00
Andreas Kling
fafba20b1a LibJS: Preserve runtime caches across executable swaps
Allow a freshly materialized executable to inherit compatible runtime
caches from the executable it replaces. Store template object caches as
GC cells so they can be shared safely between executable instances.
2026-05-22 10:54:44 +02:00
Andreas Kling
54d0dc1a85 LibJS: Defer Promise jobs during module execution
Avoid draining the Promise job queue from VM::run_executable while a
SourceTextModule is still executing its body. Dynamic import queues the
load/link/evaluate continuation as a Promise job, and running that job
before ModuleEvaluation unwinds can re-enter Link() while the entry
module is still evaluating.

Track module execution depth so standalone script execution still drains
jobs at the same boundary, but module jobs are drained by
VM::run(SourceTextModule) after Link/Evaluate returns. Add coverage for
an entry module dynamically importing itself during evaluation.
2026-05-22 01:56:57 +02:00
Andreas Kling
790386e114 LibJS: Preserve Promise job ordering across calls
Only drain the standalone Promise job queue when unwinding the outermost
bytecode execution. Draining after every nested run_executable() return
let sibling Promise reactions run before current-job reactions.

Plain async function returns were relying on that extra drain because
return values were compiled as implicit awaits. Resolve completed async
functions through the Promise resolving function instead, while keeping
the async generator return-await path.

Add Promise ordering coverage and update the asm inline-call regression
to check the pre-drain state.
2026-05-22 01:56:57 +02:00
Andreas Kling
a9e54bd745 LibJS: Preserve resolved assignment bindings
Resolve dynamic identifier assignment targets before evaluating the
right hand side, then store through that saved environment record. This
matches the ECMA-262 ordering for simple assignment and var initializers
when a with binding is deleted or direct eval creates a nearer var.

Add runtime coverage for with and direct-eval cases and refresh bytecode
expectations for dynamic assignments that now snapshot the binding
before storing.
2026-05-22 01:56:57 +02:00
Andreas Kling
2f99653b33 LibJS: Resolve super constructor before arguments
Match ECMA-262's SuperCall evaluation order by resolving the super
constructor before evaluating the argument list. This preserves the
constructor across argument-side prototype mutations and still lets
abrupt argument evaluation happen before the constructor check.

Add LibJS coverage and update bytecode baselines for the new saved
super-constructor operand. The test262 superCallOrder staging test now
passes.
2026-05-22 01:56:57 +02:00
Andreas Kling
410f0fdbb0 LibJS: Yield delegated iterator results directly
Sync yield* must suspend with the delegated IteratorResult object itself
on non-terminal steps. We were reading value and yielding that value,
causing Generator.prototype.next() to create a fresh result object.

Add a bytecode op for this path so ordinary yield keeps wrapping its
value, and teach generator resume to return the prebuilt result object.
This also avoids touching value before the delegated iterator is done.

Add regression coverage and update the yield* bytecode baseline.
2026-05-22 01:56:57 +02:00
Andreas Kling
bf530ad2af LibJS: Fix destructured primitive string const loops
Mark binding-pattern identifiers with their declaration kind so local
destructuring assignments use the normal TDZ and const assignment path.
This makes local const destructuring match the environment-backed path.

Also teach GetById to expose primitive string virtual index properties
before boxing, matching StringGetOwnProperty and GetByValue. Together
these fix the SpiderMonkey for-in/of const declaration coverage and the
lexical destructuring TDZ test.
2026-05-22 01:56:57 +02:00
Andreas Kling
dafe90a955 LibJS: Handle cached accessors without getter or setter
Accessor cache entries can be reused by objects with the same shape even
when the accessor functions differ. Reusing a getter cache for a later
setter-only object tried to call null instead of returning undefined.

Route cached gets through the missing-getter behavior from OrdinaryGet,
and fall back to the slow set path when a cached accessor has no setter
so strict assignment failures still come from OrdinarySet.

Add IC coverage for the missing accessor half in both directions.
2026-05-22 01:56:57 +02:00
Andreas Kling
7a246b63c7 LibJS: Infer computed property function names
Use a runtime SetFunctionName bytecode operation when object literal
property keys are not known until evaluation. This lets anonymous
function and class expressions, methods, and accessors receive names
from numeric, computed, and Symbol property keys.

Store inferred ECMAScript function names on each function object instead
of mutating shared function data. That keeps repeated evaluations with
different computed keys from leaking names across closures, while still
using the per-instance name for stack metadata.

Add regression coverage for computed object property names, repeated
computed-key evaluations, and preserving unnamed functions that are only
referenced by a computed property value.
2026-05-22 01:56:57 +02:00
Andreas Kling
5c881d1553 LibJS: Close iterators during array destructuring
Route abrupt completions from array destructuring target, default, and
store evaluation through IteratorClose when the iterator is still open.
Keep abrupt completions from iterator stepping itself on the existing
propagation path, matching the spec distinction used by other engines.

Also write back bytecode iterator done state when iterator abstract
operations mark the iterator as completed, so later close decisions see
the updated Iterator Record state. Add regression coverage for target,
default, iterator-next, and generator-return paths.
2026-05-22 01:56:57 +02:00
Shannon Booth
78a4438cd8 LibGC: Default-construct RootHashTable from the global heap 2026-05-20 20:37:55 +02:00
Shannon Booth
387cd6e2e2 LibGC: Default-construct RootVector from the global heap
Similar to GC::Root<T>, make GC::RootVector<T> constructible without
explicitly passing a Heap.

This is implemented by having RootVectorBase use GC::Heap::the() for
heap-free construction.
2026-05-20 20:37:55 +02:00
Luke Wilde
1bae832ce1 LibJS: Root the NewClass element-keys local with RootVector 2026-05-19 19:24:08 +02:00
Luke Wilde
52bf9c5fcb LibJS+LibWeb: Adopt RootHashTable for unrooted GC-pointer storage 2026-05-19 19:24:08 +02:00
Andreas Kling
4ac744082b LibJS: Cache dynamic environment coordinates
Dynamic environment binding opcodes lost the old coordinate warmup.
They were split away from the static coordinate opcodes. Hot closures
and eval-sensitive functions then resolved the same binding by name on
every execution, which regressed JS benchmark throughput badly.

Give each dynamic environment opcode a per-executable coordinate cache
slot. The cache keeps the bytecode stream immutable while letting both
interpreters take a direct declarative environment fast path after the
first lookup. Keep the existing eval invalidation behavior and only warm
caches for declarative-only chains so with environments continue to
observe object shadowing.

Reject cached bytecode that uses the no-cache sentinel for dynamic
environment coordinate cache operands, since execution indexes those
cache arrays unconditionally.

Rebaseline bytecode expectations for the instruction size changes. Add
coverage for with-object shadowing across repeated dynamic lookups and
for rejecting corrupt dynamic environment cache indices.
2026-05-19 15:54:23 +02:00
Andreas Kling
482b548233 LibJS: Pre-size bytecode materialization tables
Reserve bytecode tables and executable vectors from FFI counts before
filling them during Rust bytecode materialization. This avoids repeated
growth for data whose final size is already known.
2026-05-19 11:32:50 +02:00
Andreas Kling
0a49dd1c28 LibJS: Reduce inline cache memory usage
Store bytecode property lookup caches as tiered handles instead of
eagerly allocating four entries for every cache slot. Each slot starts
empty, grows to one monomorphic entry after the first cacheable lookup,
and promotes to the existing four-entry table when another cache key
appears.

Global variable caches keep one inline property entry because they
usually warm up as global object property accesses. This keeps common
global access allocation-free while still avoiding the inherited
polymorphic cache cost.

Generated asm offsets now point at the lazy property-cache storage and
the inline global entry, and asm fast paths bail out for empty property
cache slots.
2026-05-19 01:12:36 +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
3d3e6f4226 LibJS+LibWeb: Keep cached script source text lazy
Avoid decoding warm-cache script responses into full UTF-16 SourceCode
buffers when a bytecode cache sidecar is available. SourceCode now keeps
the original immutable source bytes and source encoding, then decodes
only when full source text or a Function.toString() range is requested.

Compute the bytecode cache source hash while streaming decoded code
points from the response bytes, so cache validation does not force an
intermediate UTF-8 string. Function and class source text metadata now
stores SourceCode ranges instead of views into a materialized buffer.
2026-05-18 09:18:35 +02:00
Andreas Kling
694112d31d LibJS: Move declarative state into rare data
Store the environment serial number and catch-environment flag in
DeclarativeEnvironment::RareData instead of on every environment.
The serial is only used by global variable caches, and the catch flag
is only needed for catch clause environments, so shaped function
environments should not retain dedicated fields for either value.

Teach the asm global cache fast path to load the serial through rare
data and treat a missing sidecar as serial 0.
2026-05-15 14:04:47 +02:00
Andreas Kling
d9d508a53e LibJS: Move declarative metadata into rare data
Move names, local flags, deletion state, binding lookup, dispose
state, and shape construction bookkeeping out of DeclarativeEnvironment
and into a lazily allocated sidecar. Shaped function environments keep
the shape pointer and value vector directly on the object, but do not
retain empty metadata containers after their shared shape has been
installed.

Keep the asm interpreter fast paths direct by loading local binding
flags from the sidecar only for unshaped environments or dynamic
bindings that come after a shared shape.
2026-05-15 14:04:47 +02:00
Andreas Kling
da65105cd6 LibJS: Share declarative binding flags in shapes
Let shaped declarative environments read static binding flags from their
EnvironmentShape instead of retaining a per-environment copy. The local
flag vector now mirrors the local name vector and only stores dynamic
bindings created after the shared shape.

Split EnvironmentShape binding descriptors into separate name and flag
vectors so the shared metadata is compact as well.
2026-05-15 14:04:47 +02:00