Commit Graph

2156 Commits

Author SHA1 Message Date
Andreas Kling
4724b08a26 LibJS: Make MemberExpression::to_string_approximation() recursive
Previously, the function only handled a single level of member access,
producing strings like "<object>.isWall" for chained expressions like
"graphSet[j][k].isWall". Now it recurses through nested member
expressions, identifiers, string/numeric literals, and `this`.
2026-02-15 23:21:46 +01:00
Andreas Kling
49f2f1e7cd LibJS: Skip unnecessary Mov in emit_load_from_reference for reads
When MemberExpression::generate_bytecode calls emit_load_from_reference,
it only uses the loaded_value and discards the reference operands. For
computed member expressions (e.g. a[0]), this was generating an
unnecessary Mov to save the property register for potential store-back.

Add a ReferenceMode parameter to emit_load_from_reference. When LoadOnly
is passed, the computed property path skips the register save and Mov.
2026-02-15 23:21:46 +01:00
Andreas Kling
c0f38c82d8 LibJS: Fix evaluation order in array destructuring assignment
Per AssignmentRestElement and AssignmentElement in the specification,
the DestructuringAssignmentTarget reference must be evaluated before
iterating or stepping the iterator. We were doing it in the wrong
order, which caused observable differences when the target evaluation
has side effects, and could lead to infinite loops when the iterator
never completes.

Add Generator::emit_evaluate_reference() to evaluate a member
expression's base and property into ReferenceOperands without performing
a load or store, then use the pre-evaluated reference for the store
after iteration completes.
2026-02-15 23:21:46 +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
af57184627 LibJS: Fix scoping of function declarations with destructured params
When a function has parameter expressions (e.g. destructured params with
defaults), CreateVariableEnvironment creates a separate variable
environment for function declarations and sets it as the current lexical
environment at runtime. However, the bytecode generator's
m_lexical_environment_register_stack was not updated to reflect this, so
subsequent CreateLexicalEnvironment ops would parent themselves to the
old (pre-variable-environment) lexical environment, skipping the
variable environment entirely.

This meant function declarations hoisted into the variable environment
were invisible to closures created in the function body.

Fix this by capturing the new lexical environment into a register after
CreateVariableEnvironment and pushing it onto the environment register
stack.

This fixes a problem where https://tumblr.com/ wouldn't load the feed.
2026-02-12 16:59:47 +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
ecadf3ce21 LibJS: Inline AsyncIteratorClose with proper Await in for-await-of
The AsyncIteratorClose bytecode op calls async_iterator_close() which
uses synchronous await() internally. This spins the event loop while
execution contexts are on the stack, violating the microtask checkpoint
assertion in LibWeb.

Replace AsyncIteratorClose op emissions in for-await-of close handlers
with inline bytecode that uses the proper Await op, allowing the async
function to yield and resume naturally through the event loop.

For the non-throw path (break/return/continue-to-outer): emit
GetMethod, Call, Await, and ThrowIfNotObject inline.

For the throw path: wrap the close steps in an exception handler so
that any error from GetMethod/Call/Await is discarded and the original
exception is rethrown, per spec step 5.
2026-02-12 11:37:43 +01:00
Andreas Kling
1f71a11131 LibJS: Remove emit_todo() from AssignmentExpression codegen
The else branch already throws ReferenceError and switches to a dead
basic block, so the emit_todo() in the PutValue section is unreachable.
Return early after the throw and replace emit_todo() with
VERIFY_NOT_REACHED().
2026-02-12 11:37:43 +01:00
Andreas Kling
322ad1363e LibJS: Throw ReferenceError for invalid assignment targets like foo()=x
CallExpression is accepted as an assignment target for web compatibility
(Annex B), but must throw ReferenceError at runtime. We were incorrectly
throwing TypeError with a TODO message.

Replace emit_todo() calls in three codegen paths (simple assignment,
compound assignment/update, and for-in/of) with proper ReferenceError
using the "Invalid left-hand side in assignment" message, matching the
behavior of V8 and JSC.
2026-02-12 11:37:43 +01:00
Andreas Kling
9a7f836d27 LibJS: Call IteratorClose on abrupt completion in for-of loops
When a for-of or for-await-of loop exits via break, return, throw,
or continue-to-outer-loop, we now correctly call IteratorClose
(or AsyncIteratorClose) to give the iterator a chance to clean
up resources.

This uses a synthetic FinallyContext that wraps the LHS assignment
and loop body, reusing the existing try/finally completion record
machinery. The ReturnToFinally boundary is placed between Break
and Continue so that continue-to-same-loop bypasses the close
(zero overhead on normal iteration) while all other abrupt exits
route through the iterator close dispatch chain.

for-in (enumerate) does not require iterator close per spec.
2026-02-12 11:37:43 +01:00
Andreas Kling
94cef3228f LibJS: Make IteratorClose/AsyncIteratorClose take Operand for value
Change the completion_value field from Optional<Value> to Operand
in both IteratorClose and AsyncIteratorClose bytecode instructions.

This allows passing a dynamic value from a register, which is needed
for iterator close on abrupt completion where the exception value
is not known at codegen time.
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
20f50d1f71 LibJS: Convert builtin validation codegen errors to VERIFY
These checks validate engine-internal usage of builtin abstract
operations (arity, argument types, known operation names), not user JS
code. Replace CodeGenerationError returns with VERIFY() assertions:

- Spread argument check becomes VERIFY(!argument.is_spread)
- Arity checks become VERIFY(arguments.size() == N)
- StringLiteral type checks become VERIFY(message)
- Unknown operation/constant fallthroughs become VERIFY_NOT_REACHED()
2026-02-12 11:37:43 +01:00
Andreas Kling
de1b6d4f07 LibJS: Convert unreachable codegen error sites to VERIFY
Replace CodeGenerationError returns with VERIFY_NOT_REACHED() or
VERIFY() at sites that are provably unreachable:

- Non-computed member expression fallbacks in emit_load_from_reference,
  emit_store_to_reference, and emit_delete_reference (member expression
  properties are always computed, identifier, or private identifier)
- Two non-computed member expression fallbacks in AssignmentExpression
- Default case in compound assignment switch (all 15 AssignmentOp values
  are handled)
- BindingPattern Empty/Expression name+alias pair (computed property
  names always require an alias)
- Two assignment+destructuring combinations in for-in/of body evaluation
  (is_destructuring is only set for VariableDeclaration lhs, which
  always has VarBinding or LexicalBinding kind, never Assignment)
2026-02-12 11:37:43 +01:00
Andreas Kling
2fd75d948b LibJS: Remove unused Program::global_declaration_instantiation() 2026-02-11 23:57:41 +01:00
Andreas Kling
35674df48a LibJS: Handle BigInt literal keys in class field initializer naming
When a class field has a BigInt literal key like `128n = class {}`,
the anonymous class should get the name "128". The codegen path
handles Identifier, StringLiteral, and NumericLiteral keys but was
missing BigInt keys, causing the name to be empty.

Parse the BigInt literal value at codegen time and convert it to a
decimal string for both the field_name (anonymous function naming)
and class_field_initializer_name (eval("arguments") checking) paths.
2026-02-11 23:57:41 +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
09a11a1a5c LibJS: Drop AST after first compilation on SourceTextModule
Now that initialize_environment() uses pre-computed data and
execute_module() caches its executable / uses TLA shared data,
we can drop the AST reference after it's no longer needed.

For TLA modules, the AST is dropped immediately after constructing
the SharedFunctionInstanceData (which takes its own ref). For non-TLA
modules, the AST is dropped after the first bytecode compilation.

Also remove the m_default_export field (replaced by the pre-computed
m_default_export_binding_name) and extract default export info in
parse() instead of the constructor.
2026-02-11 23:57:41 +01:00
Andreas Kling
ef943505e2 LibJS: Cache compiled executable and pre-create TLA shared data
For non-TLA modules, cache the compiled Bytecode::Executable on the
SourceTextModule so we only compile once.

For TLA modules, pre-create the SharedFunctionInstanceData for the
async wrapper function at construction time. The wrapper function's
first invocation will compile it to bytecode and then automatically
drop the AST via clear_compile_inputs().
2026-02-11 23:57:41 +01:00
Andreas Kling
fa07fd9951 LibJS: Pre-compute module declaration metadata from AST
Extract the data needed by initialize_environment() from the AST at
construction time, following the pattern already used by Script for
global declaration instantiation.

This pre-computes var declared names, lexical bindings (with function
indices), functions to initialize (with SharedFunctionInstanceData),
and the default export binding name.
2026-02-11 23:57:41 +01:00
Andreas Kling
6082de6487 LibJS: Make ImportEntry and ExportEntry own their ModuleRequest
Change ImportEntry and ExportEntry to store Optional<ModuleRequest>
by value instead of raw pointers into AST storage. This decouples the
entry records from AST node lifetimes, preparing for dropping the AST
from SourceTextModule after first 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
6d0b54dd0b LibJS: Drop AST after first compilation on Script
After compiling the bytecode executable on first run, null out the
AST (m_parse_node) and clear AnnexB candidates since they are no
longer needed. This frees the memory held by the entire AST for the
script's lifetime.

The parse_node() accessor now returns a nullable pointer. Callers
(js.cpp for AST dumping, Interpreter for first compilation) access
the AST before it is dropped.
2026-02-11 23:57:41 +01:00
Andreas Kling
f87429a9e7 LibJS: Rewrite global declaration instantiation using metadata
Add Script::global_declaration_instantiation() that performs the GDI
algorithm using pre-computed name lists and shared function data
instead of walking the AST.

Runtime checks (has_lexical_declaration, can_declare_global_function,
etc.) remain since they depend on global environment state. AnnexB
iterates pre-collected candidates and calls
set_should_do_additional_annexB_steps() on stored refs.

The Interpreter::run(Script&) now calls the Script method instead of
the Program method.
2026-02-11 23:57:41 +01:00
Andreas Kling
1c68bc7533 LibJS: Pre-compute global declaration instantiation data on Script
Extract all data that Program::global_declaration_instantiation()
needs from the AST into value-type metadata stored on the Script
object, computed once during construction. This includes lexical
names, var names, functions to initialize, var scoped names, AnnexB
candidates, and lexical bindings.

This prepares for rewriting GDI to use the pre-computed metadata
instead of walking the AST.
2026-02-11 23:57:41 +01:00
Andreas Kling
d72ade6fa2 LibJS: Cache compiled executable on Script
Instead of recompiling from AST on every run, cache the compiled
bytecode executable after first compilation and reuse it on
subsequent runs.
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