Commit graph

252 commits

Author SHA1 Message Date
Andreas Kling
b81269e78b Libraries: Clean up UTF-16 source text paths
Store parser errors, source range filenames, source code filenames,
module source, and Rust parser errors as UTF-16 where they flow back
into JavaScript-visible strings. Keep byte-oriented source buffers
byte-backed.

Remove temporary PrimitiveString, ByteString, and UTF-8 detours from
JSON, RegExp, module debug logging, print formatting, and tests.
2026-06-22 19:51:25 +02:00
Andreas Kling
7025dd1fa7 Libraries: Parse JS strings from UTF-16
Thread UTF-16 string input through JSON, script parsing, Date parsing,
Intl option parsing, Temporal parsing, and the helper library boundaries
that feed those parsers. Preserve ASCII fast paths where the source data
is known to be ASCII.
2026-06-22 19:51:25 +02:00
Andreas Kling
e5bcffc3d5 LibJS: Move bytecode block counting to Rust
Use the Rust bytecode dumper's basic block collection logic for the
metadata block count. This removes the last C++ bytecode label walk and
lets us delete the generated C++ label and operand visitor helpers.
2026-06-15 02:41:57 +02:00
Andreas Kling
984d3033e9 LibJS: Remove obsolete bytecode dump formatting helpers
The Rust bytecode dumper now formats exception handler labels, raw
operands, builtins, labels, and registers. Remove the C++ dump-only
formatters and flatten Operand to expose only the runtime value-array
layout that C++ still observes.
2026-06-15 02:41:57 +02:00
Andreas Kling
7a6af95db3 LibJS: Move bytecode instruction dumping to Rust
Generate Rust bytecode dump helpers from Bytecode.def and route
Executable::dump() through them for instruction stream formatting.

Add a small Rust runtime::value helper for decoding encoded LibJS
Values so immediate Value operands are formatted on the Rust side. C++
callbacks remain only for local names and GC-backed Value payloads that
still need LibJS object access.

Remove the generated C++ to_byte_string_impl() methods and the old
Instruction::to_byte_string() dispatch. The bytecode dump tests cover
output compatibility.
2026-06-15 02:41:57 +02:00
Andreas Kling
6ecfcd3e68 LibWeb+LibJS: Cache decoded JS bytecode sidecars
Add a ref-counted decoded bytecode cache backing so bytecode cache
materialization can create fresh script or module records from a shared
decoded sidecar without passing around one-shot raw blob ownership.

Keep that backing in ExecutableBacking for records materialized from
bytecode cache sidecars, so the immutable decoded data stays alive for
as long as the installed record needs it.

Cover the shared backing path with a bytecode-cache test that
materializes and runs two scripts from one decoded backing.
2026-06-06 09:15:09 +02:00
Andreas Kling
ec421d7fa4 LibWeb+LibJS: Validate bytecode cache blobs off-thread
Warm cache hits used to validate bytecode cache blobs on the main
thread. Route script and module sidecars through a worker step that
decodes and validates the cache blob, then returns the validated blob
for main-thread materialization.

Keep the source bytes mmap-backed and avoid decoding the full source on
cache hits. The main thread still computes the UTF-16 source length so
validation can reject stale blobs before materialization.

Remove the decoded source length getter now that sidecar validation uses
the explicit validation API instead.
2026-06-05 21:55:55 +02:00
Andreas Kling
bb20f15965 LibJS: Prepare bytecode cache validation off-thread
Add an explicit validation entry point for decoded bytecode cache blobs.
This lets callers validate a blob before materialization while keeping
the existing validated-before-use invariant in place.

Make validation idempotent so a prevalidated blob is not walked again
when materialization reaches the same check. Keep the existing decoded
source length query for callers that still validate synchronously.

Route decoded cache blob owner destruction through the origin event
loop. This lets a worker decode or reject mapped bytecode without
releasing non-atomically refcounted ImmutableBytes off-thread, while
lazy cached function materialization can still retain mmap-backed
bytecode.
2026-06-05 21:55:55 +02:00
Andreas Kling
0d05c89778 LibJS: Merge bytecode cache validation walks
Cache materialization used to validate source ranges and bytecode in
separate passes. That made function and class tables decode the same
payloads repeatedly before materialization decoded them again.

Make the materialization validator check source ranges, index bounds,
and bytecode in the same recursive walk. Nested cached function
executables are now decoded once for validation instead of once for
ranges and once for bytecode.
2026-06-05 21:55:55 +02:00
Andreas Kling
9143ad4c96 LibJS: Avoid revalidating cached bytecode executables
Cache blobs already validate decoded bytecode before rebuilding C++
Bytecode::Executable objects. Keep that as the only cache validation
pass and mark the decoded cache state once it completes.

Materialization now asserts that cache blobs and lazy cached function
records passed through validation before they can be installed or
decoded. This keeps the invariant without re-running the same validator
from rust_create_executable().
2026-06-05 21:55:55 +02:00
Andreas Kling
afb0fa2413 LibJS: Hash encoded source identity in bytecode cache
Switch bytecode cache source identity from decoded UTF-16 source text to
the original encoded response bytes plus the effective source encoding.
Store the decoded source length in the cache blob header so warm loads
can build lazy SourceCode objects without decoding the source before
checking the sidecar.

This removes the main-thread decoded_source_text_info pass from valid
warm-cache script and module loads. The source is only decoded on cache
miss, or when a rejected sidecar falls back to source compilation.
2026-06-03 14:11:23 +02:00
Andreas Kling
f94b18a987 LibJS: Validate cached bytecode once before materializing
Move Rust-side cached bytecode validation to the decoded blob
boundary. All script, module, and install paths now reject invalid
cached bytecode before attaching lazy function executables to shared
function data.

This avoids validating the same cached function payload when attaching
it, then again during lazy materialization, and then again as an
executable. The C++ executable validation still runs when
materialization constructs an executable from the already validated
cache payload.
2026-06-03 14:11:23 +02:00
Vanand Gasparyan
f3a3488cda Rust: Set import granularity to Item
By default, `rustfmt` persists the import granularity. In practice, most
Rust code has import granularity "Module" due to LSP's actions.

"Item" gets rid of import groupings and achieves cleaner diffs and
better conflict resolution. Better greppability is a positive side
effect.

Note: it's an unstable rustfmt feature. `cargo +nightly fmt` must be
used instead of `cargo fmt`.
2026-05-28 06:52:18 +02:00
Andreas Kling
19c1cae030 LibJS: Preserve strict unresolvable assignment references
Resolve strict global-looking assignment targets before evaluating their
right-hand side instead of lowering them directly to SetGlobal. Preserve
the original unresolvable reference for PutValue so a RHS-created global
cannot hide the ReferenceError.

Add a runtime test for that ordering and update the bytecode expectation
for a strict top-level assignment that now uses SetResolvedBinding.
2026-05-22 14:30:35 +02:00
Andreas Kling
8b64f0079b LibJS: Allow parenthesized dynamic import under new
Parenthesized import calls are valid callees for new expressions. The
unparenthesized form remains a syntax error, but the grouped expression
must parse and then fail at runtime if the produced Promise is not a
constructor.

Add syntax coverage for both forms.
2026-05-22 14:30:35 +02:00
Andreas Kling
795a82d621 LibJS: Track shared function data ownership
Give scripts and modules an explicit list of their shared function data
records, and teach Rust materialization to populate that list directly.
This gives bytecode cache installation a stable set of functions to
update when replacing executable backing storage.
2026-05-22 10:54:44 +02:00
Andreas Kling
790386e114 LibJS: Preserve Promise job ordering across calls
Only drain the standalone Promise job queue when unwinding the outermost
bytecode execution. Draining after every nested run_executable() return
let sibling Promise reactions run before current-job reactions.

Plain async function returns were relying on that extra drain because
return values were compiled as implicit awaits. Resolve completed async
functions through the Promise resolving function instead, while keeping
the async generator return-await path.

Add Promise ordering coverage and update the asm inline-call regression
to check the pre-drain state.
2026-05-22 01:56:57 +02:00
Andreas Kling
a9e54bd745 LibJS: Preserve resolved assignment bindings
Resolve dynamic identifier assignment targets before evaluating the
right hand side, then store through that saved environment record. This
matches the ECMA-262 ordering for simple assignment and var initializers
when a with binding is deleted or direct eval creates a nearer var.

Add runtime coverage for with and direct-eval cases and refresh bytecode
expectations for dynamic assignments that now snapshot the binding
before storing.
2026-05-22 01:56:57 +02:00
Andreas Kling
eeda29d9ed LibJS: Hoist sloppy labelled function declarations
Treat labelled FunctionDeclaration nodes as declaration-bearing items
when collecting block lexical and top-level var scoped declarations.
This lets Annex B declaration instantiation initialize them before
execution and copy block functions back to the var binding.

Add LibJS coverage for function bodies, direct eval, blocks, switch
cases, and duplicate labelled declarations.
2026-05-22 01:56:57 +02:00
Andreas Kling
8a64784d46 LibJS: Check strict destructured parameter names
Run deferred parameter validation whenever the surrounding context is
already strict, not only when a function body contains a Use Strict
Directive. Destructured parameter names are collected while parsing the
parameter list, so eval and arguments bindings in patterns otherwise
escaped the strict-mode early error for functions and methods.

Add parser coverage for sloppy destructuring remaining accepted, strict
source being rejected, and class methods applying their strict context.
2026-05-22 01:56:57 +02:00
Andreas Kling
2f99653b33 LibJS: Resolve super constructor before arguments
Match ECMA-262's SuperCall evaluation order by resolving the super
constructor before evaluating the argument list. This preserves the
constructor across argument-side prototype mutations and still lets
abrupt argument evaluation happen before the constructor check.

Add LibJS coverage and update bytecode baselines for the new saved
super-constructor operand. The test262 superCallOrder staging test now
passes.
2026-05-22 01:56:57 +02:00
Andreas Kling
c23b944340 LibJS: Reject parenthesized nested assignment patterns
Reject parenthesized object and array literals when a destructuring
assignment element is refined into a nested assignment pattern. These
grouped forms do not cover AssignmentPattern, while parenthesized simple
assignment targets such as identifiers and member expressions remain
valid.

Add local syntax coverage for the invalid nested patterns and the valid
simple target cases.
2026-05-22 01:56:57 +02:00
Andreas Kling
99d3b27b17 LibJS: Allow async as a for-await-of target
Apply the `async of` grammar exclusion only to ordinary for-of
statements. The for-await-of production excludes `let` at the start of
the left-hand side, but does not exclude `async of`, so `async` remains
a valid assignment target there.

Add local parser coverage for async functions and async generators.
2026-05-22 01:56:57 +02:00
Andreas Kling
45bd4087c4 LibJS: Reject unparenthesized await before exponentiation
Parse await through the unary-expression path so the existing
exponentiation early error also applies to AwaitExpression. This keeps
parenthesized await expressions valid while rejecting await on the
left-hand side of **.

Add parser coverage for the async-function constructor case.
2026-05-22 01:56:57 +02:00
Andreas Kling
72876d8b93 LibJS: Reject unconditional reserved words as labels
Reject labels whose string value is an unconditional ReservedWord,
while keeping await and yield on their contextual identifier paths.
This makes super labels a syntax error, matching ECMA-262 and V8.

Add coverage for the rejected super label form.
2026-05-22 01:56:57 +02:00
Andreas Kling
62ac304099 LibJS: Avoid naming parenthesized assignment functions
NamedEvaluation for assignment only applies when the left hand side is
an IdentifierRef. Parenthesized identifiers are still assignment
targets, but are not IdentifierRefs, so `(fn) = function () {}` must
leave the function anonymous.

Carry the parser's parenthesized-LHS bit into assignment codegen. Use
it when setting pending_lhs_name for plain and logical assignment, and
cover parenthesized assignment targets in the function-name tests.
2026-05-22 01:56:57 +02:00
Andreas Kling
22e1df58d3 LibJS: Delete through optional chain property references
The delete operator needs the Reference produced by an optional chain
when the chain ends in a property access. We evaluated the chain for
side effects and returned true, so delete object?.property reported
success without removing the property.

Emit delete-specific optional-chain bytecode. It short-circuits to true
for nullish optional hops, but performs DeleteById or DeleteByValue
when the final property reference is reached. Add runtime coverage for
named, computed, and short-circuited optional deletes.
2026-05-22 01:56:57 +02:00
Andreas Kling
94fea5ad2b LibJS: Re-export imported module namespaces indirectly
`import * as ns; export { ns }` was being recorded as a local
export, so two star exports of matching namespace objects resolved
through different fixture modules and became ambiguous.

Emit the same indirect namespace export metadata used by `export * as`
for both normal compilation and bytecode-cache materialization. Add a
module test that merges two matching namespace re-exports.
2026-05-22 01:56:57 +02:00
Andreas Kling
410f0fdbb0 LibJS: Yield delegated iterator results directly
Sync yield* must suspend with the delegated IteratorResult object itself
on non-terminal steps. We were reading value and yielding that value,
causing Generator.prototype.next() to create a fresh result object.

Add a bytecode op for this path so ordinary yield keeps wrapping its
value, and teach generator resume to return the prebuilt result object.
This also avoids touching value before the delegated iterator is done.

Add regression coverage and update the yield* bytecode baseline.
2026-05-22 01:56:57 +02:00
Andreas Kling
6ee59d8bcb LibJS: Scope directive prologue octal tracking
Reset the legacy octal string marker at the start of each directive
prologue scan. Otherwise an octal string parsed later in an outer
function body can leak into a nested function and be treated as part of
that nested function's directive prologue.

Add coverage for a nested strict function following a non-directive
octal string in the outer function body.
2026-05-22 01:56:57 +02:00
Andreas Kling
6c7e642b67 LibJS: Reject non-of for-await loops
Reject for-await loop heads that fall through to normal for-loop or
for-in parsing. ECMA-262 only defines for-await with for-of productions,
and V8 rejects these forms as syntax errors.

Add parser coverage for the valid for-await-of form and the invalid
C-style and for-in forms.
2026-05-22 01:56:57 +02:00
Andreas Kling
bf530ad2af LibJS: Fix destructured primitive string const loops
Mark binding-pattern identifiers with their declaration kind so local
destructuring assignments use the normal TDZ and const assignment path.
This makes local const destructuring match the environment-backed path.

Also teach GetById to expose primitive string virtual index properties
before boxing, matching StringGetOwnProperty and GetByValue. Together
these fix the SpiderMonkey for-in/of const declaration coverage and the
lexical destructuring TDZ test.
2026-05-22 01:56:57 +02:00
Andreas Kling
b42aa88189 LibJS: Preserve generator yield grammar context
YieldExpression carries the surrounding In grammar parameter into its
operand. The parser was always parsing yield operands with In enabled,
accepting invalid generator for-loop heads containing `yield ... in`.

GeneratorExpression parses its optional binding identifier with the
Yield grammar parameter enabled, so `yield` is not a valid name for
a named generator expression. Reject it while still allowing sloppy
generator declarations named `yield`, and add syntax coverage for
both cases.
2026-05-22 01:56:57 +02:00
Andreas Kling
e0ee0990cf LibJS: Preserve no-In context in arrow bodies
ArrowFunction carries the surrounding In grammar parameter into its
ConciseBody. The parser recognized arrows in for-loop init expressions,
but parsed expression bodies with In always enabled, so
`for (x => 0 in 1;;)` was accepted as a normal for loop.

Thread the existing forbidden-token state through arrow parsing so the
body leaves `in` for the for-head parser. That parser then rejects the
arrow as an invalid for-in left-hand side. Add coverage in the for-loop
no-In syntax test.
2026-05-22 01:56:57 +02:00
Andreas Kling
6c2487cc9c LibJS: Allow sloppy labelled functions in function bodies
Annex B.3.2 suppresses the labelled FunctionDeclaration early error
in non-strict code when the host supports that compatibility feature.
Script and block statement lists already enabled that path, but function
bodies parsed their statement lists with it disabled.

Parse function-body statement lists with the same labelled-function
allowance while the existing strict and generator checks continue to
reject the invalid forms. Add coverage for all three cases.
2026-05-22 01:56:57 +02:00
Andreas Kling
4046f65ae7 LibJS: Treat class definition parts as strict
Record the generator's strictness for each emitted bytecode
instruction, instead of applying the executable's final strictness to
every instruction during assembly. This lets class heritage and
computed element names run with strict assignment semantics inside
sloppy scripts.

Also create the class self-binding as a strict immutable binding,
matching ClassDefinitionEvaluation, and cover strict class heritage and
computed names in the runtime tests.
2026-05-22 01:56:57 +02:00
Andreas Kling
6d36517042 LibJS: Infer names for default parameter functions
SingleNameBinding default initializers use NamedEvaluation when the
argument value is undefined and the initializer is an anonymous function
or class definition.

Set the pending inferred name while evaluating default initializers for
identifier parameters, and add coverage for function, generator, async,
arrow, and class expressions.
2026-05-22 01:56:57 +02:00
Andreas Kling
9987035c0a LibJS: Preserve empty with completions
WithStatement evaluation updates empty statement completions to
undefined. The bytecode generator reused the surrounding completion
register while compiling the with body, so an empty break or continue
could leak the enclosing loop's previous completion value.

Give the with body its own completion register initialized to undefined
and add regression coverage for abrupt exits from the body.
2026-05-22 01:56:57 +02:00
Andreas Kling
99596d68ab LibJS: Fix await boundaries in class elements
Treat ordinary and generator function expressions inside class static
blocks as boundaries for await binding names, while still rejecting
await as an arrow parameter in the static block itself.

Parse field initializers without inheriting an enclosing async
function's await-expression context, so script field initializers can
resolve await as an identifier while computed field names still use the
enclosing expression context.

Cover static block function expressions, class field initializers, and
the AST shape for an await arrow inside a nested function.
2026-05-22 01:56:57 +02:00
Andreas Kling
0c6f86c80d LibJS: Evaluate object destructuring targets before GetV
Evaluate member targets in object destructuring before reading the
source property or copying rest properties. Convert computed source
property keys before evaluating the target reference so the generated
bytecode follows the spec ordering.

Cover plain, computed, defaulted, and rest object destructuring.
2026-05-22 01:56:57 +02:00
Andreas Kling
a598e09f22 LibJS: Reject arrow functions in class heritage
ClassHeritage parses an extends clause as a LeftHandSideExpression,
which excludes unparenthesized arrow functions. Reject arrows that start
at the heritage expression while still allowing parenthesized arrows and
arrows nested inside call arguments.

Add class inheritance coverage for declarations, expressions, and nested
arrow arguments.
2026-05-22 01:56:57 +02:00
Andreas Kling
77bf420654 LibJS: Reject duplicate method parameters
Formal parameter early errors depend on the final parameter-list shape.
A duplicate in `function(a, a, ...rest)` was accepted because the
duplicate appeared before the rest parameter was seen.

Track the final simple-parameter-list result before checking duplicate
names, and enforce `UniqueFormalParameters` for method definitions. Add
coverage for sloppy functions with a later rest parameter and sloppy
object methods.
2026-05-22 01:56:57 +02:00
Andreas Kling
a6e642790f LibJS: Validate eval private names at instantiation
Track private identifiers that eval code references outside a local
class body and pass them through EvalDeclarationInstantiation data.
This lets direct eval validate those names against the caller private
environment before execution, while indirect eval and missing names
still throw SyntaxError.

Add coverage for direct eval access from methods, field initializers,
static methods, and the missing-private-name SyntaxError path.
2026-05-22 01:56:57 +02:00
Andreas Kling
7a246b63c7 LibJS: Infer computed property function names
Use a runtime SetFunctionName bytecode operation when object literal
property keys are not known until evaluation. This lets anonymous
function and class expressions, methods, and accessors receive names
from numeric, computed, and Symbol property keys.

Store inferred ECMAScript function names on each function object instead
of mutating shared function data. That keeps repeated evaluations with
different computed keys from leaking names across closures, while still
using the per-instance name for stack metadata.

Add regression coverage for computed object property names, repeated
computed-key evaluations, and preserving unnamed functions that are only
referenced by a computed property value.
2026-05-22 01:56:57 +02:00
Andreas Kling
5c881d1553 LibJS: Close iterators during array destructuring
Route abrupt completions from array destructuring target, default, and
store evaluation through IteratorClose when the iterator is still open.
Keep abrupt completions from iterator stepping itself on the existing
propagation path, matching the spec distinction used by other engines.

Also write back bytecode iterator done state when iterator abstract
operations mark the iterator as completed, so later close decisions see
the updated Iterator Record state. Add regression coverage for target,
default, iterator-next, and generator-return paths.
2026-05-22 01:56:57 +02:00
Andreas Kling
4ac744082b LibJS: Cache dynamic environment coordinates
Dynamic environment binding opcodes lost the old coordinate warmup.
They were split away from the static coordinate opcodes. Hot closures
and eval-sensitive functions then resolved the same binding by name on
every execution, which regressed JS benchmark throughput badly.

Give each dynamic environment opcode a per-executable coordinate cache
slot. The cache keeps the bytecode stream immutable while letting both
interpreters take a direct declarative environment fast path after the
first lookup. Keep the existing eval invalidation behavior and only warm
caches for declarative-only chains so with environments continue to
observe object shadowing.

Reject cached bytecode that uses the no-cache sentinel for dynamic
environment coordinate cache operands, since execution indexes those
cache arrays unconditionally.

Rebaseline bytecode expectations for the instruction size changes. Add
coverage for with-object shadowing across repeated dynamic lookups and
for rejecting corrupt dynamic environment cache indices.
2026-05-19 15:54:23 +02:00
Andreas Kling
948afeb7ee LibJS: Reuse cached constant buffers when materializing
Validate cached constant buffers without decoding them into Rust
ConstantValue objects, then pass the bytes to the C++ executable
factory. Align string constants in the FFI encoding so fresh and cached
buffers use the same layout.
2026-05-19 11:32:50 +02:00
Andreas Kling
afa4a69315 LibJS: Borrow bytecode cache tables when materializing
Pass decoded UTF-16 table entries to the C++ executable factory as
borrowed FFI slices during bytecode cache materialization. This avoids
copying cache-backed names into generator-owned Rust vectors before C++
materializes its own bytecode tables.
2026-05-19 11:32:50 +02:00
Andreas Kling
75b2bcd7e4 LibJS: Track duplicate environment binding slots
Duplicate sloppy block function declarations append duplicate runtime
bindings, and name lookup resolves to the newest slot. The bytecode
coordinate tracker was keeping the first slot instead, so a second
InitializeLexicalBinding could target an already initialized binding
when running through the C++ interpreter.

Record every emitted binding creation and let later duplicates replace
the coordinate map entry, matching the way DeclarativeEnvironment
appends bindings.
2026-05-18 20:35:14 +02:00
Andreas Kling
cd38cdf6cf LibJS: Pass var environment binding counts through FFI
Thread the var environment binding count through the Rust and C++ SFD
metadata helpers wherever the function environment binding count already
travels. This lets CreateVariableEnvironment use the cached var
environment shape for functions with parameter expressions.
2026-05-18 20:35:14 +02:00