Commit Graph

1317 Commits

Author SHA1 Message Date
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
Andreas Kling
c9ec9696af LibJS: Drop AST after creating SharedFunctionInstanceData for builtins
parse_builtin_file() previously returned FunctionDeclaration AST nodes
stored in static vectors, keeping the full AST alive for the entire
process lifetime. Change it to return SharedFunctionInstanceData
objects directly, allowing the parsed Program and its AST nodes to be
freed when the function returns.

Each SharedFunctionInstanceData holds its own ref to the function body
AST via m_ecmascript_code, which is automatically dropped when
clear_compile_inputs() runs after first bytecode compilation.
2026-02-11 23:57:41 +01:00
Andreas Kling
680487fa9a LibJS: Rewrite eval declaration instantiation using metadata
Change eval_declaration_instantiation to take EvalDeclarationData&
instead of Program const&. The function body now iterates
pre-computed name lists instead of walking the AST.

Both callers (perform_eval and perform_shadow_realm_eval) now build
EvalDeclarationData before calling eval_declaration_instantiation.
This decouples the runtime declaration-instantiation API from AST
types, matching the pattern already used by Script for global
declaration instantiation.
2026-02-11 23:57:41 +01:00
Andreas Kling
87b9795d75 LibJS: Pre-compute eval declaration instantiation data
Add EvalDeclarationData struct that holds pre-computed metadata
extracted from the Program AST: var names, functions to initialize,
declared function names, var scoped names, AnnexB candidates, and
lexical bindings.

This mirrors the pattern used by Script for global declaration
instantiation, and prepares for decoupling
eval_declaration_instantiation from the AST.
2026-02-11 23:57:41 +01:00
Andreas Kling
4c7a349b62 LibJS: Remove #include <AST.h> from SharedFunctionInstanceData.h
Extract FunctionParsingInsights into its own header and introduce
FunctionLocal as a standalone mirror of Identifier::Local. This
allows SharedFunctionInstanceData.h to avoid pulling in the full
AST type hierarchy, reducing transitive include bloat.

The AST.h include is kept in SharedFunctionInstanceData.cpp where
it's needed for the constructor that accesses AST node types.
2026-02-11 23:57:41 +01:00
Andreas Kling
5e577bec6a LibJS: Clear m_formal_parameters and m_ecmascript_code after compilation
After bytecode compilation, the formal parameters and ecmascript code
AST nodes are no longer needed at runtime. Clear these RefPtrs in
clear_compile_inputs() to allow the AST subtrees to be freed.
2026-02-11 23:57:41 +01:00
Andreas Kling
9ea5aa93f8 LibJS: Pre-store formal parameter runtime data
Replace the runtime uses of formal_parameters() with pre-computed data:
- m_formal_parameter_count stores the parameter count
- m_parameter_names_for_mapped_arguments stores ordered parameter names
  for simple parameter lists (used by create_mapped_arguments_object)

Change create_mapped_arguments_object to take Span<Utf16FlyString>
instead of NonnullRefPtr<FunctionParameters const>.

Remove virtual formal_parameters() from FunctionObject as it is no
longer needed.
2026-02-11 23:57:41 +01:00
Andreas Kling
712d3fc54f LibJS: Pre-compute ScopeNode queries in SharedFunctionInstanceData
Pre-compute the data that emit_function_declaration_instantiation
previously obtained by querying ScopeNode methods at codegen time:
- m_has_scope_body: whether ecmascript_code is a ScopeNode
- m_has_non_local_lexical_declarations: from ScopeNode query
- m_lexical_bindings: non-local lexically-scoped identifier names and
  their constant-declaration status

After this change, emit_function_declaration_instantiation no longer
casts m_ecmascript_code to ScopeNode or calls any ScopeNode methods.
2026-02-11 23:57:41 +01:00
Andreas Kling
d36521a698 LibJS: Replace m_functions_to_initialize with pre-created data
Replace Vector<FunctionDeclaration const&> with a FunctionToInitialize
struct that stores a pre-created SharedFunctionInstanceData, function
name, and local index. The SharedFunctionInstanceData for each hoisted
function is created eagerly during the parent's construction, removing
the need to reference FunctionDeclaration AST nodes after construction.
2026-02-11 23:57:41 +01:00
Andreas Kling
7cc392551b LibJS: Replace VariableNameToInitialize with value-type VarBinding
Replace VariableNameToInitialize (which holds Identifier const&) with a
VarBinding struct that stores pre-extracted values: name, local index,
parameter_binding, and function_name. This removes a reference to AST
Identifier nodes from SharedFunctionInstanceData, allowing the AST to
be freed after compilation.
2026-02-11 23:57:41 +01:00
Andreas Kling
ec2f4e4a7b LibJS: Wire NewClass to ClassBlueprint
Replace the ClassExpression const& reference in the NewClass
instruction with a u32 class_blueprint_index. The interpreter now
reads from the ClassBlueprint stored on the Executable and calls
construct_class() instead of the AST-based create_class_constructor().

Literal field initializers (numbers, booleans, null, strings, negated
numbers) are used directly in construct_class() without creating an
ECMAScriptFunctionObject, avoiding function creation overhead for
common field patterns like `x = 0` or `name = "hello"`.

Set class_field_initializer_name on SharedFunctionInstanceData at
codegen time for statically-known field keys (identifiers, private
identifiers, string literals, and numeric literals). For computed
keys, the name is set at runtime in construct_class().

ClassExpression AST nodes are no longer referenced from bytecode.
2026-02-11 23:57:41 +01:00
Andreas Kling
fa6a3f31dc LibJS: Add AST-free construct_class()
Add a standalone construct_class() function that builds a class from
a ClassBlueprint and an Executable, replacing the virtual dispatch
through ClassElement::class_element_evaluation() with a direct switch
on ClassElementDescriptor::Kind.

This function reads pre-compiled SharedFunctionInstanceData indices
from the blueprint, creates ECMAScriptFunctionObjects at runtime, and
handles all class element types: methods, getters, setters, fields
(with initializers), and static initializers.

The function exists but is not yet called. No behavioral change.
2026-02-11 23:57:41 +01:00
Andreas Kling
658ba1d023 LibJS: Clear compile-only data from SharedFunctionInstanceData
After successful bytecode compilation, the m_functions_to_initialize
and m_var_names_to_initialize_binding vectors are no longer needed
as they are only consumed by emit_function_declaration_instantiation()
during code generation.

Add clear_compile_inputs() to release these vectors post-compile,
and call it from both ECMAScriptFunctionObject::get_stack_frame_size()
and NativeJavaScriptBackedFunction::bytecode_executable() after their
respective lazy compilation succeeds.

Also add a pre-compile assertion in Generator::generate_from_function()
to verify we never try to compile the same function data twice, and a
VERIFY in ECMAScriptFunctionObject::ecmascript_code() to guard against
null dereference.
2026-02-11 23:57:41 +01:00