Commit graph

2027 commits

Author SHA1 Message Date
Andreas Kling
0aec6a12b4 LibJS: Use binary search for exception handler lookup
The exception handler table is sorted by start_offset, so use
binary_search instead of a linear scan. This matches the pattern
already used by source_range_at() in the same file.
2026-02-09 16:35:39 +01:00
Andreas Kling
51f67febe8 LibJS: Remove stale comment on Yield handler
This comment referenced the old runtime unwind context stack behavior
where a flag had to be set to prevent yield from going through a
finally statement. That mechanism was removed and finally is now
handled purely through explicit completion records in bytecode.
2026-02-09 16:35:39 +01:00
Andreas Kling
720fd567b1 LibJS: Collapse handler/finalizer into single exception handler target
After replacing the runtime unwind context stack with explicit
completion records for try/finally dispatch, the distinction between
"handler" (catch) and "finalizer" (finally) in the exception handler
table is no longer meaningful at runtime.

handle_exception() checked handler first, then finalizer, but they
did the exact same thing (set the PC). When both were present, the
finalizer was dead code.

Collapse both fields into a single handler_offset (now non-optional,
since an entry always has a target), remove the finalizer concept
from BasicBlock, UnwindContext, and ExceptionHandlers, and simplify
handle_exception() to a direct assignment.
2026-02-09 16:35:39 +01:00
Andreas Kling
4fa4ecf31b LibJS: Inline ExecutionContextRareData fields into ExecutionContext
After removing the unwind context stack, ExecutionContextRareData only
held two GC::Ptr fields — both trivially destructible. The indirection
cost more than it saved: a GC cell allocation per EC, an extra pointer
chase on every source range lookup, and unnecessary complexity.

Replace the rare data cell with two inline fields on ExecutionContext:
cached_source_range and context_owner.
2026-02-09 16:35:39 +01:00
Andreas Kling
6a3b71397b LibJS: Remove runtime unwind context stack and UnwindInfo struct
The runtime unwind context stack was pushed by EnterUnwindContext
and popped by LeaveUnwindContext. With both opcodes removed, it is
no longer read or written by anything.

Remove UnwindInfo, the unwind_contexts vector, its GC visit loop,
its copy in ExecutionContext::copy(), and the VERIFY assertions that
referenced it in handle_exception() and catch_exception().
2026-02-09 16:35:39 +01:00
Andreas Kling
cbca493b28 LibJS: Remove BlockBoundaryType::Unwind
With LeaveUnwindContext gone, the Unwind boundary type has no purpose.
Remove it from the enum and all start/end boundary calls.
2026-02-09 16:35:39 +01:00
Andreas Kling
5abe40874a LibJS: Remove LeaveUnwindContext opcode
LeaveUnwindContext popped the runtime unwind context stack. With the
stack being removed, all emission sites become dead code. Remove the
opcode and all its emissions.
2026-02-09 16:35:39 +01:00
Andreas Kling
e84a1fd6ad LibJS: Remove EnterUnwindContext opcode
EnterUnwindContext pushed an UnwindInfo and jumped to entry_point.
Without the stack push, it's just a Jump. Replace the single emission
site with a Jump and remove the opcode entirely.
2026-02-09 16:35:39 +01:00
Andreas Kling
c5956c78ce LibJS: Remove handler_called from UnwindInfo
This field was set but never read, making it dead code.
2026-02-09 16:35:39 +01:00
Andreas Kling
7f89158d20 LibJS: Replace implicit environment stack with explicit registers
Replace the saved_lexical_environments stack in ExecutionContextRareData
with explicit register-based environment tracking. Environments are now
stored in registers and restored via SetLexicalEnvironment, making the
environment flow visible in bytecode.

Key changes:
- Add GetLexicalEnvironment and SetLexicalEnvironment opcodes
- CreateLexicalEnvironment takes explicit parent and dst operands
- EnterObjectEnvironment stores new environment in a dst register
- NewClass takes an explicit class_environment operand
- Remove LeaveLexicalEnvironment opcode (instead: SetLexicalEnvironment)
- Remove saved_lexical_environments from ExecutionContextRareData
- Use a reserved register for the saved lexical environment to avoid
  dominance issues with lazily-emitted GetLexicalEnvironment
2026-02-09 16:35:39 +01:00
Andreas Kling
a439dc8490 LibJS: Use explicit completion records for try/finally dispatch
Each finally scope gets two registers (completion_type and
completion_value) that form an explicit completion record. Every path
into the finally body sets these before jumping, and a dispatch chain
after the finally body routes to the correct continuation.

This replaces the old implicit protocol that relied on the exception
register, a saved_return_value register, and a scheduled_jump field
on ExecutionContext, allowing us to remove:

- 5 opcodes (ContinuePendingUnwind, ScheduleJump, LeaveFinally,
  RestoreScheduledJump, PrepareYield)
- 1 reserved register (saved_return_value)
- 2 ExecutionContext fields (scheduled_jump, previously_scheduled_jumps)
2026-02-09 08:51:12 +01:00
Andreas Kling
5cefa59116 LibJS: Fix evaluation order of computed property keys in object literals
The spec for PropertyDefinitionEvaluation requires that when evaluating
a property definition with a computed key (PropertyDefinition :
PropertyName : AssignmentExpression), the PropertyName is fully
evaluated (including ToPropertyKey, which calls ToPrimitive) before the
value's AssignmentExpression is evaluated.

Our bytecode compiler was evaluating the key expression first, then
the value expression, and only performing ToPropertyKey later inside
PutByValue at runtime. This meant user-observable side effects from
ToPrimitive (such as calling Symbol.toPrimitive or toString on the key
object) would fire after the value expression had already been
evaluated.

Fix this by using a new ToPrimitiveWithStringHint instruction that
performs ToPrimitive with string hint(!), and emitting it between the
key and value evaluations in ObjectExpression codegen.
After ToPrimitive, the key is already a primitive, so the subsequent
ToPropertyKey inside PutByValue becomes a no-op from the perspective
of user-observable side
effects.

Also update an existing test that was asserting the old (incorrect)
evaluation order, and add comprehensive new tests for computed property
key evaluation order.
2026-02-09 01:23:48 +01:00
Andreas Kling
bef09b899c LibJS: Fix object rest destructuring with MemberExpression target
When the rest element in an object destructuring assignment targets a
MemberExpression (e.g. `({a, ...t.rest} = obj)`), we were incorrectly
storing the original source object to the reference instead of the
rest object produced by CopyObjectExcludingProperties.

For example, `({a, ...t.rest} = {a:1, b:2, c:3})` would set t.rest
to `{a:1, b:2, c:3}` instead of the correct `{b:2, c:3}`.

The fix is to pass the result of CopyObjectExcludingProperties
to emit_store_to_reference instead of the original RHS.
2026-02-09 01:23:48 +01:00
Andreas Kling
7997267942 LibJS: Remove outdated FIXME comments about ToPropertyKey ordering
The FIXME comments suggested that ToPropertyKey was called at the wrong
time for computed super property access. However, extensive testing
shows that both Ladybird and V8 implement the correct ordering according
to the ECMA262 specification.

Remove the outdated FIXME comments and add comprehensive test coverage
for super property computed keys with Symbol.toPrimitive to prevent
regressions.
2026-02-09 01:23:48 +01:00
Andreas Kling
3eb03b4817 LibJS: Preserve this binding for tagged with identifiers
Route tagged template identifier lookup through
GetCalleeAndThisFromEnvironment only when the identifier is non-local.
Keep local and global identifiers on Identifier::generate_bytecode so
TDZ checks and ordinary undefined-this behavior stay intact.

Expand runtime coverage with a tagged-template TDZ regression case,
sequential with-binding calls, and getter-returned tag functions.
2026-02-09 01:23:48 +01:00
Andreas Kling
0c843b04e3 LibJS: Remove stale FIXME about sloppy-mode this in CallExpression
For non-Reference calls (e.g. (0, fn)(), (cond ? fn : x)()), the
codegen correctly passes undefined as the thisValue, matching step 2b
of EvaluateCall in the spec. OrdinaryCallBindThis then coerces
undefined to the global object in sloppy mode at runtime. Replace the
stale FIXME with a clarifying comment.

Also add comprehensive tests for this-value behavior in non-Reference
call patterns (comma, ternary, logical, assignment, nullish coalescing)
in both sloppy and strict mode.
2026-02-08 20:59:20 +01:00
Andreas Kling
8c541380dd LibJS: Remove stale FIXMEs about SetFunctionName and MakeConstructor
Both SetFunctionName and MakeConstructor are already performed by
ECMAScriptFunctionObject::initialize() when the object is created
via create_from_function_node:

- SetFunctionName: The name is passed to SharedFunctionInstanceData,
  and initialize() creates the "name" property from it.
- MakeConstructor: has_constructor() returns true for all normal
  non-arrow functions, m_constructor_kind defaults to Base, and
  m_may_need_lazy_prototype_instantiation handles the prototype
  property creation lazily.
2026-02-08 20:59:20 +01:00
Andreas Kling
b7091ba35a LibJS: Remove stale FIXME about NamedEvaluation in assignment
The FIXME claimed that IsAnonymousFunctionDefinition + NamedEvaluation
was missing for simple assignment expressions like `x = function() {}`.
However, the code directly below the FIXME already implements this
correctly via emit_named_evaluation_if_anonymous_function.
2026-02-08 20:59:20 +01:00
Andreas Kling
690ba5299b LibJS: Pass the global object as receiver in GetGlobal
The GetGlobal bytecode optimization bypasses the normal environment
record lookup for global variable access. When a global property is
an accessor (getter), the receiver passed to the getter must be the
global object, not undefined.

The spec's Get(O, P) abstract operation is defined as O.[[Get]](P, O),
meaning the object itself is always the receiver. The global
environment's GetBindingValue delegates to its object record's
GetBindingValue, which calls Get(bindingObject, N), so the receiver
should be the binding object (the global object).

Both the cached path (calling the getter directly from get_direct)
and the non-cached path (calling internal_get) were passing
js_undefined() as the receiver. This caused strict-mode getters on
global properties to receive undefined as their this-value instead
of globalThis.

Notably, the corresponding SetGlobal paths already correctly passed
&binding_object for setter calls.
2026-02-08 20:59:20 +01:00
Andreas Kling
710f1bdb74 LibJS: Produce negative zero from i32 multiplication fast path
The i32 multiplication fast path in Mul::execute_impl was producing
+0 instead of -0 when one operand was negative and the other was
zero (e.g. `var a = -1, b = 0; a * b`).

This happened because i32 can't represent -0, so `Value(0)` was
always positive zero. We now fall through to the double path when
the i32 result is zero, which correctly handles the sign.

Also add comprehensive multiplication tests covering negative zero,
basic arithmetic, large integers, type coercion, NaN, and Infinity.
2026-02-08 20:59:20 +01:00
Andreas Kling
40430d6087 LibJS: Detect direct eval calls in parse_call_expression()
The parser previously detected direct eval() calls at the end of
parse_expression(), by checking if the final expression was a
CallExpression with "eval" as the callee. This missed cases where
eval() appeared as a subexpression, e.g. `eval(code) | 0`, since
the final expression would be a BinaryExpression, not a
CallExpression.

Move the detection into parse_call_expression() where the
CallExpression is actually created. This ensures we always set the
contains_direct_call_to_eval flag regardless of surrounding
operators, so local variables are correctly placed in the
declarative environment where eval'd code can find them.
2026-02-07 18:05:41 +01:00
Timothy Flynn
72a6f59df5 LibJS+LibUnicode: Support Intl.MathematicalValue in Intl.PluralRules
This is a normative change in the ECMA-402 spec. See:
7344f42

The main difference here is that Intl.PluralRules now supports BigInt.
2026-02-06 12:19:46 -05:00
Timothy Flynn
a109adebeb LibJS: Add [[CompactDisplay]] slot to Intl.PluralRules
This is a normative change in the ECMA-402 spec. See:
33893b3
2026-02-06 12:19:46 -05:00
Timothy Flynn
ed89b42b41 LibJS: Allow languageDisplay to be undefined in DisplayNames options
This is an editorial change in the ECMA-402 spec. See:
7dc4a6d

Note that we had already worked around this issue.
2026-02-06 12:19:46 -05:00
Timothy Flynn
d8f8bbada8 LibJS: Fix typo in Intl.NumberFormat GetNumberOption parameter name
This is an editorial change in the ECMA-402 spec. See:
759e671
2026-02-06 12:19:46 -05:00
Luke Wilde
e5f5329f9d LibJS: Consume identifier when matching an identifier name in bindings
Otherwise we'll fail to recognise that the identifier token is invalid
(e.g. a keyword such as null) in a binding pattern and that it may not
be a binding pattern after all, but an object expression.

Fixes some scripts on Discord failing to parse.
2026-02-06 16:09:33 +01:00
Andreas Kling
74a80b7bfc LibJS: Mark JS::Cell::initialize() as MUST_UPCALL
Intermediate classes in the initialize() chain set up prototypes and
define properties. Forgetting to call Base::initialize() in any
override would silently skip that setup.
2026-02-06 13:50:54 +01:00
Adam Colvin
2df5a7bb31 LibJS: Add source locations to console.trace()
LibJS+DevTools: Implement console.trace() with source locations

- Add Console::TraceFrame struct with source location data
- Implement Console::trace() to gather stack information
- Add WebView::StackFrame and ConsoleTrace for IPC
- Implement DevToolsConsoleClient::printer() for traces
- Update FrameActor to format traces for DevTools
- Update WorkerDebugConsoleClient trace handling
- Update ReplConsoleClient to format trace output
2026-02-06 11:58:07 +00:00
Ali Mohammad Pur
ac979648bd AK+LibJS: Zero out new Vector allocs instead of calling trivial ctor
As JS::Value is marked "trivial" without actually being trivial, make
the one user that would lead to garbage JS::Value entries provide a
default value instead.
2026-02-02 14:11:49 +01:00
dosisod
dab739771f LibJS: Reduce number of template literal op codes
There is no need to concat empty string literals when building template
literals. Now strings will only be concatenated if they need to be.

To handle the edge case where the first segment is not a string
literal, a new `ToString` op code has been added to ensure the value is
a string concatenating more strings.

In addition, basic const folding is now supported for template literal
constants (templates with no interpolated values), which is commonly
used for multi-line string constants.
2026-01-31 18:24:02 +01:00
dosisod
2c3077b878 LibJS: Dead code elimination for always truthy/falsey conditions
This improves and expands the ability to do dead code elimination on
conditions which are always truthy or falsey.

The following cases are now optimized:
* `if (true){}` -> Only emit `if` block, ignore `else`
* `if (false){}` -> Only emit `else if`/`else` block
* `while (false){}` -> Ignore `while` loop entirely
* `for (x;false;){}` -> Only emit `x` (if it exists), skip `for` block
* Ternary -> Directly return left/right hand side if condition is const
2026-01-31 18:22:40 +01:00
Andreas Kling
d89f3fc5e6 LibGC+ClangPlugins: Forbid non-trivial destructors in Cell subclasses
Add a clang plugin check that flags GC::Cell subclasses (and their
base classes within the Cell hierarchy) that have destructors with
non-trivial bodies. Such logic should use Cell::finalize() instead.

Add GC_ALLOW_CELL_DESTRUCTOR annotation macro for opting out in
exceptional cases (currently only JS::Object).

This prevents us from accidentally adding code in destructors that
runs after something we're pointing to may have been destroyed.
(This could become a problem when the garbage collector sweeps
objects in an unfortunate order.)

This new check uncovered a handful of bugs which are then also fixed
in this commit. :^)
2026-01-30 20:57:42 +01:00
Timothy Flynn
ef1b4a4d57 LibJS: Forbid adding/subtracting non-year/month units in PlainYearMonth
This is a normative change in the Temporal proposal. See:
41faca4
2026-01-30 08:01:45 +01:00
Timothy Flynn
7ac2e7deea LibJS: Split ISODateSurpasses into a couple of AOs
This is an editorial change in the Temporal proposal. See:
196a319
2026-01-30 08:01:45 +01:00
Timothy Flynn
eff429d490 Revert "LibJS: Move ambiguous time string handling to a separate parser"
This reverts commit 3f75cf270a.

This change was meant to be editorial, but it caused a regression. See:
3ffa677

A separate test was not added here to catch this regression, because it
affects non-ISO-8601 calendars, which we do not yet implement.
2026-01-30 08:01:45 +01:00
Andreas Kling
90e0334202 LibJS: Keep dictionary-mode shapes cacheable after property deletion
Previously, deleting a property from a cacheable dictionary would
transition it to an uncacheable dictionary, defeating inline caching
for all subsequent property accesses on that object.

This was particularly impactful for the global object, which becomes a
dictionary due to its large number of properties. Test frameworks that
delete global properties (like test-js) would cause the global object
to become uncacheable, preventing global variable caching from working.

The fix is simple: instead of transitioning to uncacheable, we continue
using the existing remove_property_without_transition() which already
remaps all property offsets greater than the deleted offset. Combined
with the dictionary_generation counter (which is already incremented),
this ensures caches are properly invalidated while keeping the shape
cacheable for future accesses.
2026-01-27 10:58:39 +01:00
Andreas Kling
5238841da2 LibJS: Mark named function expression identifiers at individual level
Previously, when parsing a named function expression like
`Oops = function Oops() { Oops }`, the parser set a group-level flag
`might_be_variable_in_lexical_scope_in_named_function_assignment` that
propagated to the parent scope. This incorrectly prevented ALL `Oops`
identifiers from being marked as global, including those outside the
function expression.

Fix this by marking identifiers individually using
`set_is_inside_scope_with_eval()` only for identifiers inside the
function scope. This allows identifiers outside the function expression
to correctly use GetGlobal/SetGlobal while identifiers inside still
use GetBinding (since they may refer to the function's name binding).
2026-01-27 10:58:39 +01:00
Andreas Kling
871d93355b LibJS: Stop propagating is_inside_scope_with_eval across functions
Previously, when a nested function contained eval(), the parser would
mark all identifiers in parent functions as "inside scope with eval".
This prevented those identifiers from being marked as global, forcing
them to use GetBinding instead of GetGlobal.

However, eval() can only inject variables into its containing function's
scope, not into parent function scopes. So a parent function's reference
to a global like `Number` should still be able to use GetGlobal even if
a nested function contains eval().

This change adds a new flag `m_eval_in_current_function` that propagates
through block scopes within the same function but stops at function
boundaries. This flag is used for marking identifiers, while the
existing `m_screwed_by_eval_in_scope_chain` continues to propagate
across functions for local variable deoptimization (since eval can
access closure variables).

Before: `new Number(42)` in outer() with eval in inner() -> GetBinding
After:  `new Number(42)` in outer() with eval in inner() -> GetGlobal
2026-01-27 10:58:39 +01:00
Andreas Kling
5674f8bbe0 LibJS: Limit eval() deoptimization to the containing function scope
Previously, when direct eval() was called, we would mark the entire
environment chain as "permanently screwed by eval", disabling variable
access caching all the way up to the global scope.

This was overly conservative. According to the ECMAScript specification,
a sloppy direct eval() can only inject var declarations into its
containing function's variable environment - it cannot inject variables
into parent function scopes.

This patch makes two changes:

1. Stop propagating the "screwed by eval" flag at function boundaries.
   When set_permanently_screwed_by_eval() hits a FunctionEnvironment or
   GlobalEnvironment, it no longer continues to outer environments.

2. Check each environment during cache lookup traversal. If any
   environment in the path is marked as screwed, we bail to the slow
   path. This catches the case where we're inside a function with eval
   and have a cached coordinate pointing to an outer scope.

The second change is necessary because eval can create local variables
that shadow outer bindings. When looking up a variable from inside a
function that called eval, we can't trust cached coordinates that point
to outer scopes, since eval may have created a closer binding.

This improves performance for code with nested functions where an inner
function uses eval but parent functions perform many variable accesses.
The parent functions can now use cached environment coordinates.

All 29 new tests verify behavior matches V8.
2026-01-27 10:58:39 +01:00
Andreas Kling
81bee185e6 LibJS: Replace source map HashMap with sorted Vector
Bytecode source map entries are always added in order of increasing
bytecode offset, and lookups only happen during error handling (a cold
path). This makes a sorted vector with binary search a better fit than
a hash map.

This change reduces memory overhead and speeds up bytecode generation
by avoiding hash table operations during compilation. Lookups remain
fast via binary search, and since source_range_at() is only called
when generating stack traces, the O(log n) lookup is acceptable.
2026-01-26 19:37:42 +01:00
Andreas Kling
d488f9f12f LibJS: Narrow bytecode source map offsets from size_t to u32
Add VERIFY guards to catch bytecode programs that exceed u32::max bytes
and narrow the bytecode_offset parameter in add_source_map_entry() to
u32. This is a preparatory change for optimizing source map storage.
2026-01-26 19:37:42 +01:00
Andreas Kling
88d715fc68 LibJS: Eliminate HashMap operations in SFID by caching parser data
Cache necessary data during parsing to eliminate HashMap operations
in SharedFunctionInstanceData construction.

Before: 2 HashMap copies + N HashMap insertions with hash computations
After: Direct vector iteration with no hashing

Build FunctionScopeData for function scopes in the parser containing:
- functions_to_initialize: deduplicated var-scoped function decls
- vars_to_initialize: var decls with is_parameter/is_function_name
- var_names: HashTable for AnnexB extension checks
- Pre-computed counts for environment size calculation
- Flags for "arguments" handling

Add ScopeNode::ensure_function_scope_data() to compute the data
on-demand for edge cases that don't go through normal parser flow
(synthetic class constructors, static initializers, module wrappers).

Use this cached data directly in SFID with zero HashMap operations.
2026-01-25 23:08:36 +01:00
Colleirose
bf7fd80140 LibCrypto+AK: Merge LibCrypto/SecureRandom into AK/Random
AK/Random is already the same as SecureRandom. See PR for more details.

ProcessPrng is used on Windows for compatibility w/ sandboxing measures
See e.g. https://crbug.com/40277768
2026-01-23 15:53:27 +01:00
Jelle Raaijmakers
e3faa9b5ad LibJS: Move tests to /Tests/LibJS 2026-01-22 07:46:48 -05:00
dosisod
ac8cc6d24b LibJS: Constant fold LogicalExpression
Logical expressions like `true || false` are now constant folded. This
also allows for dead code elimination if we know the right-hand side of
the expression will never be evaluated (such as `false && f()` or
`true || f()`).

In the test suites, the values are now being constant folded at compile
time. To ensure that the actual evaluation logic is being called
properly, I had to duplicate the tests and call them via a function so
the compiler would not optimize the evaluation logic away.

This also demotes `NaN` and `Infinity` identifiers to `nan` and
`inf` double literals, which will further help with const folding.
2026-01-22 08:47:18 +01:00
dosisod
5a8d71fb02 LibJS: Optimize double boolean not (!!) operation
This is a common way to convert a value to a boolean. Instead of doing
a boolean conversion and 2 negate operations, we replace this with a
single `ToBoolean` op code.
2026-01-22 08:45:42 +01:00
dosisod
bc823878c7 LibJS: Add bytecode IR regression tests
This adds a new `test-js-bytecode` target which ensures that codegen
changes do not impact emitted bytecode IR, or if it does, it is known
and the tests are updated accordingly.

Similar to the LibWeb tests, the tests are stored in the following
format:

* `Libraries/LibJS/Bytecode/Tests/input`: Input `.js` files
* `Libraries/LibJS/Bytecode/Tests/expected`: Expected `.txt` bytecode
* `Libraries/LibJS/Bytecode/Tests/output`: Emitted `.txt` bytecode

The `output` dir is git-ignored, but stores the output so you can diff
and inspect failed tests more easily.

There is only one test so far, which is a baseline test that should not
change dramatically unless we change the bytecode output format.
2026-01-22 08:44:24 +01:00
Andreas Kling
986e30a140 LibJS: Fix object literal shape caching for numeric string keys
Numeric string keys like "0" are converted to numeric property keys and
stored in indexed storage rather than shape-based storage. The shape
caching optimization introduced in 505fe0a977 didn't account for this,
causing properties with numeric keys to be lost on subsequent calls.

The fix excludes object literals with numeric string keys from the
shape caching fast path by checking if any key would become a numeric
property index.
2026-01-21 10:49:34 +01:00
Luke Wilde
babfd70ca7 LibGC: Enforce that a Cell type must declare the allocator to use
This ensures that we are explicitly declaring the allocator to use when
allocating a cell(-inheriting) type, instead of silently falling back
to size-based allocation.

Since this is done in allocate_cell, this will only be detected for
types that are actively being allocated. However, since that means
they're _not_ being allocated, that means it's safe to not declare
an allocator to use for those. For example, the base TypedArray<T>,
which is never directly allocated and only the defined specializations
are ever allocated.
2026-01-20 12:00:11 +01:00
Andreas Kling
4d92c4d71a LibJS: Skip initializing constant slots in ExecutionContext
Every function call allocates an ExecutionContext with a trailing array
of Values for registers, locals, constants, and arguments. Previously,
the constructor would initialize all slots to js_special_empty_value(),
but constant slots were then immediately overwritten by the interpreter
copying in values from the Executable before execution began.

To eliminate this redundant initialization, we rearrange the layout from
[registers | constants | locals] to [registers | locals | constants].
This groups registers and locals together at the front, allowing us to
initialize only those slots while leaving constant slots uninitialized
until they're populated with their actual values.

This reduces the per-call initialization cost from O(registers + locals
+ constants) to O(registers + locals).

Also tightens up the types involved (size_t -> u32) and adds VERIFYs to
guard against overflow when computing the combined slot counts, and to
ensure the total fits within the 29-bit operand index field.
2026-01-19 10:48:12 +01:00