Commit Graph

2156 Commits

Author SHA1 Message Date
Timothy Flynn
72a6f59df5 LibJS+LibUnicode: Support Intl.MathematicalValue in Intl.PluralRules
This is a normative change in the ECMA-402 spec. See:
https://github.com/tc39/ecma402/commit/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:
https://github.com/tc39/ecma402/commit/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:
https://github.com/tc39/ecma402/commit/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:
https://github.com/tc39/ecma402/commit/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:
https://github.com/tc39/proposal-temporal/commit/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:
https://github.com/tc39/proposal-temporal/commit/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:
https://github.com/tc39/proposal-temporal/commit/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
Andreas Kling
f1cf79cac4 LibJS: Make Error stack strings UTF-16 from the get-go
We were creating these as UTF-8 and then converting it to UTF-16 moments
later when someone tried using the string for something.
2026-01-18 19:00:32 +01:00
Andreas Kling
cec0d3eae8 LibJS: Avoid some unnecessary object copies in Error::stack_string() 2026-01-18 19:00:32 +01:00
Andreas Kling
b5fc557709 LibJS: Cache the formatted Error.prototype.stack string
Generating the stack trace string is expensive as it involves
formatting each frame with function names, file paths, and line
numbers. Since the stack trace is immutable after error creation,
we can cache the formatted string on first access.

This matches the caching behavior of V8 and JavaScriptCore, which
also return the same cached string on subsequent accesses to the
stack property.
2026-01-18 19:00:32 +01:00
Andreas Kling
b6b06f691a LibJS: Make PropertyAttributes and default_attributes constexpr
This allows it to be inlined everywhere instead of occurring a fajillion
times in separate BSS locations.
2026-01-18 10:10:04 +01:00
ayeteadoe
5279e0ce73 CMake: Remove ENABLE_WINDOWS_CI option and adjust presets to match Unix
ENABLE_WINDOWS_CI and the *_CI presets were initially added back when
the AK library and all the AK Test* executables were the only targets
that supported building and running in CI. Since then, almost all the
targets in the codebase are built on Windows besides the following:
    - LibLine
    - test-262-runner

Since these targets above are not required to actually run or test the
browser on Windows in its current experimental state, fully disabling
them should be fine for now.

ENABLE_WINDOWS_CI was also used to exclude test-web from ctest. This
can be fully disabled on Windows for now until proper runtime support
is added.

The remaining locations were all using ENABLE_WINDOWS_CI as a proxy for
ENABLE_ADDRESS_SANITIZER, so we can just be explicit instead.

The new presets map much more directly to the unix Release, Debug, and
Sanitizer presets which should make setting up ladybird on Windows less
confusing.

We also make the new Windows_Experimental_Release preset the default in
ladybird.py to match Unix.
2026-01-16 11:15:16 -07:00
aplefull
e4572aa9d7 LibRegex: Add support for regex modifiers
This commit implements the regexp-modifiers proposal. It allows us to
use modification of i,m,s flags within groups using
`(?flags:subpattern)` and `(?flags-flags:subpattern)` syntax.
2026-01-16 15:00:00 +01:00
Timothy Flynn
3fb0e69c20 LibJS: Flip validity check to an assertion in CalendarMonthDayFromFields
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/163d589
2026-01-16 14:31:31 +01:00
Timothy Flynn
076bbd4c33 LibJS: Hoist early return in CalendarDateUntil to the top
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/264e4b8
2026-01-16 14:31:31 +01:00
Timothy Flynn
6cd765c19c LibJS: Flip validity check to an assertion in IsValidDuration
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/e65f411
2026-01-16 14:31:31 +01:00
Ali Mohammad Pur
ea65181444 Meta+LibJS: Upgrade simdjson to the latest version
5e0ee26e8b pulls in an old version of
simdjson, this commit upgrades to the latest release.
This commit also pins `dav1d` to the version it was before this change.
2026-01-16 13:11:05 +01:00
Luke Wilde
f77b72040e LibJS/Temporal: Implement finding time zone transitions 2026-01-16 07:00:02 -05:00
Luke Wilde
23659b1130 LibJS: Floor nanoseconds to seconds conversion when finding TZ offsets
Otherwise, for example, -5,000,000,000 and -5,000,000,001 nanoseconds
are indistinguishable when a nanosecond could cross into a new time
zone offset.
2026-01-16 07:00:02 -05:00
Luke Wilde
8395db7e84 LibCrypto+LibJS: Add to_i64 method for SignedBigInteger and use it 2026-01-16 07:00:02 -05:00
aplefull
4938d24cef LibJS: Add test262 regression tests for RegExp 2026-01-16 01:11:24 +01:00
Timothy Flynn
b517e5b947 LibJS: Avoid sign negations in Temporal's DifferenceZonedDateTime
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/407fa01
2026-01-13 13:02:22 -05:00
Timothy Flynn
3eabdcf460 LibJS: Replace Temporal's BalanceISODate with AddDaysToISODate
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/b28ef08
2026-01-13 13:02:22 -05:00
Andreas Kling
0710b24f2d LibJS: Optimize JSON.stringify with single StringBuilder
This patch improves JSON.stringify performance through three changes:

1. Use a single StringBuilder for the entire operation instead of
   building up intermediate strings and concatenating them.

2. Format numbers directly into the StringBuilder via a new public
   number_to_string(StringBuilder&, ...) overload, avoiding temporary
   String allocations.

3. Track indentation as a depth counter instead of repeatedly
   concatenating the gap string.
2026-01-12 13:53:28 -05:00
Andreas Kling
5e0ee26e8b LibJS: Use simdjson for JSON.parse
Replace the custom AK JSON parser with simdjson for parsing JSON in
LibJS. This eliminates the intermediate AK::JsonValue object graph,
going directly from JSON text to JS::Value.

simdjson's on-demand API parses at ~4GB/s and only materializes values
as they are accessed, making this both faster and more memory efficient
than the previous approach.

The AK JSON parser is still used elsewhere (WebDriver protocol, config
files, etc.) but LibJS now uses simdjson exclusively for JSON.parse()
and JSON.rawJSON().
2026-01-12 13:53:28 -05:00
Andreas Kling
505fe0a977 LibJS: Add shape caching for object literal instantiation
When a function creates object literals with simple property names,
we now cache the resulting shape after the first instantiation. On
subsequent calls, we create the object with the cached shape directly
and write property values at their known offsets.

This avoids repeated shape transitions and property offset lookups
for a common JavaScript pattern.

The optimization uses two new bytecode instructions:
- CacheObjectShape: Captures the final shape after object construction
- InitObjectLiteralProperty: Writes properties using cached offsets

Only "simple" object literals are optimized (string literal keys with
simple value expressions). Complex cases like computed properties,
getters/setters, and spread elements use the existing slow path.

3.4x speedup on a microbenchmark that repeatedly instantiates an object
literal with 26 properties. Small progressions on various benchmarks.
2026-01-10 00:56:51 +01:00
Andreas Kling
4b556feecf LibJS: Take snapshot of prototype chain validity later in GetById
We were snapshotting it a bit earlier than necessary, and if we ended up
hitting one of the inline caches, copying the validity was wasted work.
2026-01-09 09:16:50 +01:00
Andreas Kling
d66273b12a LibJS: Add % (modulo) fast path in bytecode interpreter
We can avoid the expensive call to fmod() in various cases when we have
Int32 values on both sides of the operator.
2026-01-09 09:16:50 +01:00