Commit Graph

1986 Commits

Author SHA1 Message Date
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
Jelle Raaijmakers
ae20ecf857 AK+Everywhere: Add Vector::contains(predicate) and use it
No functional changes.
2026-01-08 15:27:30 +00:00
Shannon Booth
8ea37c4de4 LibJS: Remove unnecessary return in Object::define_native_accessor 2026-01-08 12:57:17 +01:00
Andreas Kling
f558cd9f0d LibJS: Add inline caching for object literal instantiation
When instantiating an object literal, we go through the list of
properties and add them one by one to the new object. However, we were
totally neglecting to cache the shape transitions incurred by this
process, even though we had allocated a PropertyLookupCache for it.

1.25x speedup on Octane/splay.js and just generally very useful.
2026-01-08 00:26:57 +01:00
Andreas Kling
7dad7e10ee LibJS: Pre-size arrays created by NewArray and NewPrimitiveArray
We know the length they're gonna end up with up front since we're
instantiating array literals. Pre-sizing them allows us to skip
incremental resizing of the property storage.
2026-01-08 00:26:57 +01:00
Andreas Kling
12e49ad053 LibJS: Visit entire SimpleIndexedProperties packed vector in bulk 2026-01-08 00:26:57 +01:00
Andreas Kling
8b19992f8c LibGC: Make MarkingVisitor better at bulk-visiting Vector<JS::Value>
When passing a Vector<JS::Value> to the MarkingVisitor, we were
iterating over the vector and visiting one value at a time. This led
to a very inefficient way of building up the GC's work queue.

By adding a new visit_impl() virtual to Cell::Visitor, we can now
grow the work queue capacity once, and then add without incrementally
growing the storage.
2026-01-07 20:51:17 +01:00
Andreas Kling
7a4e74be96 LibJS: Don't skip indexed property storage switching in Array fast path
If we call put() directly on the underlying indexed property storage
like we were doing here, we skip the checks that switch from flat to
sparse property storage when a huge index is suddenly accessed.

This was caught by folks hitting memory issues when running test-js.
2026-01-07 07:52:03 -05:00
Andreas Kling
4c10f44e3e LibJS: Remove bogus VERIFY_NOT_REACHED() in WeakRef::remove_dead_cells()
It's harmless if the WeakRef is asked to remove dead cells when it has
no dead cells to remove. This can happen if the WeakRef lives longer
due to someone referencing it. Caught by test-js with different GC
frequency.
2026-01-07 07:52:03 -05:00
Andreas Kling
a9cc425cde LibJS+LibWeb: Add missing GC marking visits
This adds visit_edges(Cell::Visitor&) methods to various helper structs
that contain GC pointers, and makes sure they are called from owning
GC-heap-allocated objects as needed.

These were found by our Clang plugin after expanding its capabilities.
The added rules will be enforced by CI going forward.
2026-01-07 12:48:58 +01:00
Luke Wilde
c4c9ac08ad LibJS: Follow the spec more closely for tagged template literals
This resolves a FIXME in its code generation, particularly for:
- Caching the template object
- Setting the correct property attributes
- Freezing the resulting objects

This allows archive.org to load, which uses the Lit library.

The Lit library caches these template objects to determine if a
template has changed, allowing it to determine to do a full template
rerender or only partially update the rendering. Before, we would
always cause a full rerender on update because we didn't return the
same template object.

This caused issues with archive.org's code, I believe particularly with
its router library, where we would constantly detach and reattach nodes
unexpectedly, ending up with the page content not being attached to the
router's custom element.
2026-01-06 23:25:36 +01:00
Luke Wilde
d766e41c94 LibJS: Store tagged template literal raw strings as StringLiterals 2026-01-06 23:25:36 +01:00
Jelle Raaijmakers
d00571719f LibGC+LibJS+LibWeb: Add Visitor::visit(Optional<T>)
No functional changes.
2026-01-06 10:55:56 +01:00
Andreas Kling
c516715e62 LibJS: Fix AddOwnProperty IC cache applying to non-extensible objects
The AddOwnProperty inline cache would incorrectly apply to frozen,
sealed, or non-extensible objects because it only checked if the
object's shape matched the cached "from_shape", not whether the object
was actually extensible.

Since Object.freeze(), Object.seal(), and Object.preventExtensions()
don't change the object's shape, a normal empty object {} and a
frozen Object.freeze({}) would share the same shape. The IC cache
populated from adding a property to the normal object would then be
incorrectly used for the frozen object, allowing property addition
to what should be a non-extensible object.

The fix adds an extensibility check before applying the AddOwnProperty
cache. Also adds comprehensive tests for dictionary shapes and
non-extensible object IC behavior.
2026-01-06 00:11:28 +01:00
Andreas Kling
eef63f2095 LibJS: Add comprehensive indexed access stress tests
This adds 103 tests covering indexed property access optimizations:
- Dense array read/write
- Sparse arrays and holes
- Prototype lookup through holes
- Out of bounds access
- Index type coercion (float, string, Symbol, NaN, Infinity, -0)
- Object with numeric properties
- Array-like objects and arguments
- All TypedArray types (Int8, Uint8, Uint8Clamped, Int16, Uint16,
  Int32, Uint32, Float32, Float64, BigInt64, BigUint64)
- TypedArray overflow, clamping, and special values
- Mixed type access patterns (array/object/TypedArray alternation)
- Accessor properties at indices
- Frozen, sealed, and non-extensible arrays
- Detached ArrayBuffer handling
- Array length interactions
- Proxy with indexed access
- String character indexing
- Index computation edge cases
- Concurrent modification during iteration
- Integer overflow edge cases
- Performance-critical patterns (matrix, strided, binary search)
- Array methods that use indexed access
- TypedArray-specific edge cases (byteOffset, subarray, etc.)

These tests exercise the fast paths in GetByValue and PutByValue
for arrays, objects with numeric keys, and TypedArrays.
2026-01-06 00:11:28 +01:00
Andreas Kling
8e1a24fb28 LibJS: Add comprehensive inline cache stress tests
This adds 119 tests covering various inline caching scenarios:
- Basic property get/set
- Prototype chain access at various depths
- Shape transitions
- Property deletion and re-addition
- Getters and setters (own and inherited)
- Object.defineProperty edge cases
- Prototype manipulation at runtime
- Proxy objects in various configurations
- Special object types (arrays, typed arrays, strings, functions)
- Symbol properties
- Computed property access
- Constructor and class patterns
- Edge cases (frozen/sealed objects, null prototypes, etc.)
- IC invalidation scenarios
- Polymorphic IC boundary tests (1-5+ shapes)
- IC cache thrashing with many shapes

These tests exercise the polymorphic inline cache implementation,
particularly around the 4-shape limit where caches transition
from polymorphic to megamorphic behavior.
2026-01-06 00:11:28 +01:00
Andreas Kling
d6fbde43f8 LibJS: Track eval() scope membership per-identifier
The previous fix prevented eval() in sibling function scopes from
affecting each other, but it still had a limitation: when identifiers
from multiple scopes were merged into the same identifier group at
Program scope, the presence of eval() anywhere would taint all
identifiers in the group.

This change tracks per-identifier whether it was inside a scope with
eval() in the scope chain. When a scope closes, if it contains eval()
or has eval() in its parent chain, each identifier in that scope is
marked with `is_inside_scope_with_eval`. At Program scope finalization,
only identifiers that are NOT marked can be optimized to global lookups.

This allows code like:
```js
var x = undefined;  // Can be optimized (program scope)
(function() {
    function walk() { undefined; }  // Cannot be optimized
    eval('');
})();
```

Before: Neither `undefined` could be optimized
After: The program-scope `undefined` is optimized, while the one inside
       the function with eval() correctly uses dynamic lookup.
2026-01-06 00:11:28 +01:00
InvalidUsernameException
831cecd644 LibJS+LibWeb: Add missing visits for cached cross-origin properties
This was causing GC-related crashes on various websites, most
prominently on any site that contains embedded YouTube videos. The issue
can be reproduced by going to any YouTube video, using the _Share_
button below it and pasting the embed code into an empty HTML file and
loading it through localhost.

This is technically a regression from
89dbdd3411 in that the problem became
visible with that commit. However, there is nothing wrong with the
commit by itself. It just happens that `Origin::is_same_origin_domain()`
prior to that commit was completely bogus and would mistakenly return
true in almost all cases, so the cross-origin code paths were not
exercised.

I am uncertain how to make a automatic test case for this problem, given
the nature of it being GC- and cross-origin-related. So there is no
regression test included in this commit.
2026-01-06 00:01:01 +01:00
Ali Mohammad Pur
3f35d84785 LibRegex+LibJS: Flatten the bytecode buffer before regex execution
This makes it so we don't have to unnecessarily check for having a
flattened buffer; significant performance increase.
2026-01-05 18:22:11 +01:00
CountBleck
b42402514e LibJS: Don't crash on enormous maxByteLengths for SharedArrayBuffer
I mistakenly used ensure_capacity upon first implementing this, leading
to a crash when the requested memory couldn't be allocated. Instead,
we now pass the maxByteLength to create_shared_byte_data_block (which
throws the RangeError for us) and shrink the data block back down to the
proper size.
2026-01-05 06:38:55 -05:00
Andreas Kling
1f5e209032 LibJS: Fix overly conservative eval() deopt for undeclared identifiers
The parser was preventing identifiers like `undefined` from being
optimized to global constants when eval() was present in any function
scope, even when eval() could not affect the identifier's binding.

This change makes the parser smarter by preventing eval() in one
function scope from affecting global identifier optimization in sibling
function scopes. The key insight is that eval() in function A cannot
shadow globals in function B.
2026-01-04 13:13:26 +01:00