Commit Graph

1317 Commits

Author SHA1 Message Date
Andreas Kling
074a12f6e5 LibJS: Only create one Utf16View from input string in RegExp exec()
We kept creating new Utf16Views for the input string, which looks
harmless but actually ends up losing the cached "length in code points"
value. So we ended up recomputing it multiple times per exec call.
This was particularly noticeable on test262.
2026-02-11 19:58:25 +01:00
Andreas Kling
a8a1aba3ba LibJS: Replace ScopePusher with ScopeCollector
Replace the ScopePusher RAII class (which performed scope analysis
in its destructor chain during parsing) with a two-phase approach:

1. ScopeCollector builds a tree of ScopeRecord nodes during parsing
   via RAII ScopeHandle objects. It records declarations, identifier
   references, and flags, but does not resolve anything.

2. After parsing completes, ScopeCollector::analyze() walks the tree
   bottom-up and performs all resolution: propagate eval/with
   poisoning, resolve identifiers to locals/globals/arguments, hoist
   functions (Annex B.3.3), and build FunctionScopeData.

Key design decisions:
- ScopeRecord::ast_node is a RefPtr<ScopeNode> to prevent
  use-after-free when synthesize_binding_pattern re-parses an
  expression as a binding pattern (the original parse's scope records
  survive with stale AST node pointers).
- Parser::scope_collector() returns the override collector if set
  (for synthesize_binding_pattern's nested parser), ensuring all
  scope operations route to the outer parser's scope tree.
- FunctionNode::local_variables_names() delegates to its body's
  ScopeNode rather than copying at parse time, since analysis runs
  after parsing.
2026-02-10 02:05:20 +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
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
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
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
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
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
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
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
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
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
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
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
Jelle Raaijmakers
d00571719f LibGC+LibJS+LibWeb: Add Visitor::visit(Optional<T>)
No functional changes.
2026-01-06 10:55:56 +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
CountBleck
d44b239621 LibJS: Crudely implement growable SharedArrayBuffers
We treat any mention of [[ArrayBufferByteLengthData]] and related
atomic operations as FIXMEs to be fixed at a later date. We also add the
HostGrowSharedArrayBuffer abstract operation, which will be overridden
by LibWeb to grow shared WebAssembly memories.
2026-01-04 07:47:55 +01:00
Shannon Booth
5eac5a94cf LibJS: Implement an as_if<T> helper for JS::Value
This is to make it much easier when trying to pull out an object
of a specific type from a JS::Value, instead of needing to do the
repetitive checking that it is an object, getting that object,
checking that object is of a specific type, then casting to that
type.
2025-12-30 12:40:27 +01:00