Commit Graph

1329 Commits

Author SHA1 Message Date
Andreas Kling
766567a9d5 LibJS: Compare Rust and C++ bytecode for lazily compiled functions
LIBJS_COMPARE_PIPELINES previously only compared top-level
script/eval/module bytecodes. Function bodies are compiled lazily
via compile_function(), and that path had no comparison at all.

Fix this by pairing each Rust-compiled SharedFunctionInstanceData
with its C++ counterpart during top-level compilation. When a
function is later lazily compiled, compile_function() runs both
pipelines and compares the bytecodes (crashing on mismatch, same
as the top-level comparisons). The pairing is done recursively so
nested functions are also covered.
2026-03-01 21:20:54 +01:00
Shannon Booth
502ae99102 LibJS: Make more use of Value::is and Value::as_if 2026-02-28 10:24:37 -05:00
Shannon Booth
4c8723e2d8 LibJS: Implement convenience helper Value::as<T>()
For simplifying code that has an is<T> assertion followed by a
cast, analagous to AK::as<T>.
2026-02-28 10:24:37 -05:00
Shannon Booth
bc0cff8a59 LibJS: Support Value::as_if for JS::Object 2026-02-28 10:24:37 -05:00
Timothy Flynn
d7e828a366 LibJS: Make more use of Value::is and Value::as_if in JS runtime 2026-02-27 17:19:33 +01:00
Timothy Flynn
cadba4c0e7 LibJS: Make more use of Value::is and Value::as_if in Temporal 2026-02-27 17:19:33 +01:00
Timothy Flynn
cbf25177bb LibJS: Make more use of Value::is and Value::as_if in Intl 2026-02-27 17:19:33 +01:00
Timothy Flynn
e95db70d2d LibJS+LibWeb: Return GC::Ptr from Value::as_if 2026-02-27 17:19:33 +01:00
Timothy Flynn
9d1f727f43 LibJS: Add Value::is<T> to align with Value::as_if<T>
We can also use concepts in the template type instead of static asserts.
2026-02-27 17:19:33 +01:00
Timothy Flynn
100ed4c6a4 LibJS: Implement Iterator.zipKeyed
This is from the Joint Iteration proposal:
https://tc39.es/proposal-joint-iteration/
2026-02-26 13:54:35 -05:00
Timothy Flynn
aa7eb95f98 LibJS: Implement Iterator.zip
This is from the Joint Iteration proposal:
https://tc39.es/proposal-joint-iteration/
2026-02-26 13:54:35 -05:00
aplefull
d95baf67f6 LibJS: Prevent escaped surrogates from combining in Unicode regexes
Escaped surrogate sequences should not combine with adjacent literal
surrogates in Unicode mode.

We now use `\u{XXXX}` braces instead of `\uXXXX` when escaping code
units in Unicode mode, so LibRegex treats each as a standalone code
point. Also prevent GenericLexer from combining `\uXXXX` and `\u{XXXX}`.
2026-02-26 13:50:11 +01:00
Timothy Flynn
cb6a4683ce LibJS: Return UTF16String's ASCII storage when applicable
If we have a UTF-16 string with ASCII storage, we don't need to convert
the string to UTF-8 to get a StringView. This fast-path is hit over 76k
times during test-js and over 31 million times during test262.
2026-02-26 00:02:17 +01:00
Andreas Kling
6cdfbd01a6 LibJS: Add alternative source-to-bytecode pipeline in Rust
Implement a complete Rust reimplementation of the LibJS frontend:
lexer, parser, AST, scope collector, and bytecode code generator.

The Rust pipeline is built via Corrosion (CMake-Cargo bridge) and
linked into LibJS as a static library. It is gated behind a build
flag (ENABLE_RUST, on by default except on Windows) and two runtime
environment variables:

- LIBJS_CPP: Use the C++ pipeline instead of Rust
- LIBJS_COMPARE_PIPELINES=1: Run both pipelines in lockstep,
  aborting on any difference in AST or bytecode generated.

The C++ side communicates with Rust through a C FFI layer
(RustIntegration.cpp/h) that passes source text to Rust and receives
a populated Executable back via a BytecodeFactory interface.
2026-02-24 09:39:42 +01:00
Timothy Flynn
3355fb39ae AK+LibJS: Replace home-grown Ryu implementation with fmt's dragonbox
In the benchmark added here, fmt's dragonbox is ~3x faster than our own
Ryu implementation (1197ms for dragonbox vs. 3435ms for Ryu).

Daniel Lemire recently published an article about these algorithms:
https://lemire.me/blog/2026/02/01/converting-floats-to-strings-quickly/

In this article, fmt's dragonbox implementation is actually one of the
slower ones (with the caveat that some comments note that the article is
a bit out-of-date). I've gone with fmt here because:

1. It has a readily available recent version on vcpkg.
2. It provides the methods we need to actually convert a floating point
   to decimal exponential form.
3. There is an ongoing effort to replace dragonbox with a new algorithm,
   zmij, which promises to be faster.
4. It is one of the only users of AK/UFixedBigInt, so we can potentially
   remove that as well soon.
5. Bringing in fmt opens the door to replacing a bunch of AK::format
   facilities with fmt as well.
2026-02-23 18:30:40 +01:00
Timothy Flynn
451ac7d5d2 LibJS: Handle power-of-10 boundaries in Number.toExponential/toPrecision
When the computed significand lands exactly on 10 ^ (precision - 1), the
value sits right on a power-of-10 boundary where two representations are
possible. For example, consider 1e-21. The nearest double value to 1e-21
is actually slightly less than 1e-21. So with `toPrecision(16)`, we must
choose between:

    exponent=-21 significand=1000000000000000 -> 1.000000000000000e-21
    exponent=-22 significand=9999999999999999 -> 9.999999999999999e-22

The spec dictates that we must pick the value that is closer to the true
value. In this case, the second value is actually closer.
2026-02-22 09:39:10 -05:00
Timothy Flynn
a4d5b78fee LibJS: Use exact integer arithmetic in Number.prototype.toExponential
The arithmetic here nearly exactly matches that of toPrecision from
commit cf180bd4da.

The only difference is test262 contains tests for which our exponent
estimate is off-by-1. We now handle this by detecting this inaccuracy.
adjusting the exponent, and recomputing the significand.
2026-02-22 09:39:10 -05:00
Timothy Flynn
33a39c89c1 LibJS: Move NumberPrototype helper higher in the file
This will be needed by Number.prototype.toExponential. It will also need
some changes, so moving it up ahead of time will make that diff more
practical to read.
2026-02-22 09:39:10 -05:00
Jelle Raaijmakers
1745926fc6 AK+Everywhere: Use MurmurHash3 for int/u64 hashing
Rework our hash functions a bit for significant better performance:

* Rename int_hash to u32_hash to mirror u64_hash.
* Make pair_int_hash call u64_hash instead of multiple u32_hash()es.
* Implement MurmurHash3's fmix32 and fmix64 for u32_hash and u64_hash.

On my machine, this speeds up u32_hash by 20%, u64_hash by ~290%, and
pair_int_hash by ~260%.

We lose the property that an input of 0 results in something that is not
0. I've experimented with an offset to both hash functions, but it
resulted in a measurable performance degradation for u64_hash. If
there's a good use case for 0 not to result in 0, we can always add in
that offset as a countermeasure in the future.
2026-02-20 22:47:24 +01:00
Timothy Flynn
cf180bd4da LibJS: Use exact integer arithmetic in Number.prototype.toPrecision
Our previous implementation produced incorrect results for values near
the limits of double precision. This new implementation avoid floating-
point arithmetic entirely by:

1. Decomposing the double value into its exact binary form.
2. Computing the formulas from the spec using bigints.
3. Using Ryu to calculate the decimal exponent.
2026-02-20 13:40:40 -05:00
Timothy Flynn
d1ed361239 LibJS: Cache the result of parsing time zone identifiers
The result of parsing an identifier cannot change. It is not cheap to do
so, so let's cache the result.

This is hammered in a few test262 tests. On my machine, this reduces the
runtime of each test/staging/sm/Date/dst-offset-caching-{N}-of-8.js by
0.3 to 0.5 seconds. For example:

dst-offset-caching-1-of-8.js: Reduces from 1.2s -> 0.9s
dst-offset-caching-3-of-8.js: Reduces from 1.5s -> 1.1s
2026-02-19 09:20:15 +01:00
Timothy Flynn
6e433f0d10 LibJS: Use infallible time zone parser in a couple more locations 2026-02-19 09:20:15 +01:00
Timothy Flynn
f9fa548d43 LibJS: Pass time zone strings around as String more regularly
These are String from the outset, so this patch is almost entirely just
changing function parameter types. This will allow us to cache time zone
parse results without invoking any extra allocations.
2026-02-19 09:20:15 +01:00
Callum Law
6294fb1f7a LibJS: Mark MapIterator for export 2026-02-17 12:25:27 +00:00
Shannon Booth
e859402ea1 LibJS: Remove duplicate property_key_to_value helper function
An equivalent already exists as a member of PropertyKey.
2026-02-16 18:49:15 +01:00
Timothy Flynn
206a18acd4 LibJS: Use single-line if/else steps in Temporal operations
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/978311d
2026-02-14 19:47:29 +01:00
Timothy Flynn
a8345693aa LibJS: Avoid "or if" in if-clauses in Temporal
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/e85b20b
2026-02-14 19:47:29 +01:00
Timothy Flynn
78ff593cc8 LibJS: Use "either" / "one of" when comparing multiple items in Temporal
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/5b02980
2026-02-14 19:47:29 +01:00
Timothy Flynn
d9ce90978c LibJS: Use = for Temporal mathematical value and BigInt comparisons
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/67ca9de
2026-02-14 19:47:29 +01:00
Timothy Flynn
25ac3d2403 LibJS: Adjust language around Temporal list lengths
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/1e043d6
2026-02-14 19:47:29 +01:00
Timothy Flynn
f3f8311462 LibJS: Prefer "a new empty List" instead of "an empty List" in Temporal
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/c538d83
2026-02-14 19:47:29 +01:00
Timothy Flynn
dde73e9eb7 LibJS: Remove editor's note from Temporal's ISODateToEpochDays
We don't tend to copy these as they won't appear in the final spec.
(These note types are different than the standard NOTE: signifiers.)
2026-02-14 19:47:29 +01:00
Timothy Flynn
1d8e03b236 LibJS: Remove redundant "the value of x" in Temporal operations
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/09e36d3
2026-02-14 19:47:29 +01:00
Timothy Flynn
ececb8c38c LibJS: Improve note about precise division in NudgetToCalendarUnit
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/0e8220c
2026-02-14 19:47:29 +01:00
Timothy Flynn
21079748ff LibJS: Work around inconsistency in CLDR Temporal data sources
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/94a7487
https://github.com/tc39/proposal-temporal/commit/64a2395
2026-02-14 19:47:29 +01:00
Timothy Flynn
46c864fdd3 LibJS: Remove an unreachable path in Temporal's TimeZoneEquals AO
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/a3cb4bb
2026-02-14 19:47:29 +01:00
Timothy Flynn
e73b54532f LibJS: Refactor CalendarResolveFields parallel to NonISOResolveFields
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/b2e8299
2026-02-14 19:47:29 +01:00
Timothy Flynn
74048336df LibJS: Remove else clauses after early returns in Temporal operations
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/e6e031f
2026-02-14 19:47:29 +01:00
Timothy Flynn
8c5fd82da9 LibJS: Replace "otherwise" with "else" in Temporal operations
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/1ae3e42
2026-02-14 19:47:29 +01:00
Timothy Flynn
1fdcb5cfe5 LibJS: Fix typo in ToTemporalMonthDay
This is an editorial change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/56828a2
2026-02-14 19:47:29 +01:00
Timothy Flynn
cb8bf665d7 LibJS: Format plain Temporal objects without applying time zones
This is a normative change in the Temporal proposal. See:
https://github.com/tc39/proposal-temporal/commit/377fb80
2026-02-14 19:47:29 +01:00
Timothy Flynn
fc997d74cb LibJS: Update spec numbers for Intl Temporal operations
Several Intl overrides were removed from the Temporal proposal some time
ago. See:

https://github.com/tc39/proposal-temporal/commit/c08f6a8
https://github.com/tc39/proposal-temporal/commit/87ba571
https://github.com/tc39/proposal-temporal/commit/b52e320
https://github.com/tc39/proposal-temporal/commit/686b216
https://github.com/tc39/proposal-temporal/commit/b125fce
2026-02-14 19:47:29 +01:00
pwespi
ae9106a29d LibJS: Reject surrogate code points in URI decoding 2026-02-14 17:29:13 +01:00
Andreas Kling
1c3a36e2a9 LibJS: Remove AsyncIteratorClose bytecode op and C++ implementation
AsyncIteratorClose is now fully inlined as bytecode in ASTCodegen.cpp,
using the Await bytecode op to yield naturally. The C++ implementation
used synchronous await() which spins the event loop, violating
assertions when execution contexts are on the stack.
2026-02-12 11:37:43 +01:00
Andreas Kling
7281091fdb LibJS: Make bytecode generation infallible
Remove CodeGenerationError and make all bytecode generation functions
return their results directly instead of wrapping them in
CodeGenerationErrorOr.

For the few remaining sites where codegen encounters an unimplemented
or unexpected AST node, we now use a new emit_todo() helper that emits
a NewTypeError + Throw sequence at compile time (preserving the runtime
behavior) and then switches to a dead basic block so subsequent codegen
for the same function can continue without issue.

This allows us to remove error handling from all callers of the
bytecode compiler, simplifying the code significantly.
2026-02-12 11:37:43 +01:00
Andreas Kling
ef992b2010 LibJS: Take SharedFunctionInstanceData in NativeJavaScriptBackedFunction
Change NativeJavaScriptBackedFunction::create() to accept an
already-created GC::Ref<SharedFunctionInstanceData> instead of a
FunctionNode const&, removing another point of AST-runtime coupling.
2026-02-11 23:57:41 +01:00
Andreas Kling
7e85bb251f LibJS: Keep SourceCode alive in SharedFunctionInstanceData
SharedFunctionInstanceData::m_source_text is a Utf16View into the
SourceCode that the function was parsed from. Previously, the
SourceCode was kept alive transitively through the AST nodes.

As we move towards dropping AST nodes after compilation, we need to
ensure the SourceCode outlives the view. Add a RefPtr<SourceCode>
to SharedFunctionInstanceData so m_source_text remains valid even
after the AST is gone. This is needed for Function.prototype.toString
which accesses source_text() at any point during the function's
lifetime.
2026-02-11 23:57:41 +01:00
Andreas Kling
e308e73120 LibJS: Move SharedFunctionInstanceData creation out of FunctionNode
Add static factory methods create_for_function_node() on
SharedFunctionInstanceData and update all callers to use them instead
of FunctionNode::ensure_shared_data().

This removes the GC::Root<SharedFunctionInstanceData> cache from
FunctionNode, eliminating the coupling between the RefCounted AST
and GC-managed runtime objects. The cache was effectively dead code:
hoisted declarations use m_functions_to_initialize directly, and
function expressions always create fresh instances during codegen.
2026-02-11 23:57:41 +01:00
Andreas Kling
f05e326d78 LibJS: Remove vestigial AST.h includes from runtime files 2026-02-11 23:57:41 +01:00
Andreas Kling
90405a4c2b LibJS: Remove unused Badge-gated DeclarativeEnvironment methods
Remove create_for_per_iteration_bindings(Badge<ForStatement>) and
initialize_or_set_mutable_binding(Badge<ScopeNode>) which have zero
callers anywhere in the codebase.
2026-02-11 23:57:41 +01:00