Commit graph

65 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
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
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
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
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
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
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
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
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
17d3a285a7 LibJS: Move ScopeData into ScopeArena and reference it by ScopeId
The Rust AST kept every scope in Rc<RefCell<ScopeData>>. The Rc made
the AST !Send (cross-thread codegen needed unsafe impl Send), and the
RefCell added a runtime borrow check on every hot-path read.

AST nodes (Block, FunctionBody, Program, SwitchStatement, SwitchCase)
now hold a ScopeId index into ScopeArena. The scope collector and
codegen take &mut/&ScopeArena, so the borrow checker enforces the
previously-implicit invariant that two phases never touch the same
scope at once.

ParsedProgram is now naturally Send. The unsafe impl Send and the
arc_with_non_send_sync allow go away. CompiledProgram keeps its
hand-rolled Send impl because it carries codegen-time state outside
the AST.

FunctionDeclarationData::is_hoisted was a Cell<bool> only because the
old &[ScopeRecord] traversal couldn't get &mut to the AST. It is now
a plain bool.
2026-05-05 13:53:51 +02:00
Andreas Kling
f2bf914874 LibJS: Intern identifier names in a per-arena string table
Identifier::name was SharedUtf16String (Rc<Utf16String>), so equality
checks against literals walked the slice and the Rc made the AST
!Send.

Replace it with a StringId (u32 index) backed by a StringInterner on
AstArena. Repeated names dedupe to the same id, so name comparisons
collapse to u32 == u32. The lexer's short/recent identifier caches
and the shared_identifier_value field on Token go away; the interner
already deduplicates everything.

Methods that previously took &mut IdentifierArena now also take
&StringInterner so they can resolve names from StringId during
analyze. Codegen helpers in bytecode/codegen.rs uniformly take
&AstArena. Generator gains intern_identifier_id, intern_property_key_id,
and intern_string_id helpers.
2026-05-05 13:53:51 +02:00
Andreas Kling
9ace32e276 LibJS: Drop Cell<> wrappers from Identifier scope-analysis fields
scope_collector now reaches Identifier through &mut IdentifierArena
indexing instead of through Rc<Identifier>'s shared reference, so the
Cell<> wrappers on local_type, local_index, is_global,
is_inside_scope_with_eval, and declaration_kind no longer earn their
keep.

Replace each Cell<T> with a plain T. The borrow checker now enforces
the existing "only scope_collector mutates these post-parse"
invariant. Shrinks Identifier and removes a layer of indirection on
hot-path field reads in codegen and ast_dump.
2026-05-05 13:53:51 +02:00
Andreas Kling
3e15e59cd1 LibJS: Move identifiers into a contiguous IdentifierArena
Replace per-AST-node Rc<Identifier> with a Copy IdentifierId index
into a Vec<Identifier> arena, plumbed through Parser, scope_collector,
codegen, ast_dump, and the FFI. The arena lives on the parser during
parse, ships out via Arc<AstArena> on ParsedProgram, and is shared by
each child Generator and FunctionPayload through Arc clones.

Eliminates the per-occurrence Rc::new in the parser: every identifier
reference, parameter binding, function name, class name, and
binding-pattern target lands in the arena's Vec instead of getting its
own malloc plus Rc control block. Identifier field reads in codegen
become direct array indexing.

Identifier still carries Cell<>-wrapped scope-analysis state, so
AstArena is not yet Send + Sync; the existing unsafe-impl-Send wrapper
on ParsedProgram covers cross-thread handoff. Removing the Cells is
the next step.
2026-05-05 13:53:51 +02:00
Andreas Kling
d9b9925914 LibJS: Make CompiledRegex thread-safe with Arc + AtomicPtr
The regex literal handle is shared between AST clones (e.g. class
field initializers reuse the same compiled regex), so shared ownership
has to stay. Switch from Rc to Arc and from Cell<*mut c_void> to
AtomicPtr<c_void> so the regex can travel with a function payload to
a worker thread without UB on the non-atomic Rc refcount.
2026-05-05 13:53:51 +02:00
Andreas Kling
30394ece8d LibJS: Use natural source positions for parser-synthesized identifiers
The Rust parser used to copy several "rule_start"-derived positions
from the C++ implementation: every identifier inside a binding pattern
inherited the pattern's `[`/`{` position, every property identifier
after `.` inherited the period's position, every spread element
inherited the surrounding `[`/`{` position, and identifier-name
property keys inherited the object/class start position. This was
useful while comparing bytecode against the C++ port; with the C++
side gone, those quirks just hide the actual source positions in
source maps and devtools.

Drop the dedicated `binding_pattern_start` parser field and the
`ident_pos_override` parameter on `parse_property_key`, and capture
each identifier's own start position at the consume site.

Add an AST snapshot test that pins the new per-identifier positions
for object, array, nested, and parameter binding patterns.
2026-04-27 08:04:11 +02:00
Andreas Kling
cec0be6f3d LibJS: Replace in_property_key_context flag with explicit consume helper
The parser used to suppress the arguments/eval reference check via a
state flag that was set during the entire `parse_property_key` call.
That was over-broad: identifiers inside a computed property key like
`{ [arguments]: 1 }` are real references, but the flag silenced their
check too, leaving the function unmarked as needing the arguments
object. Reading the resulting property at runtime crashed.

Replace the flag with a `consume_property_key_token()` method used at
the specific consume sites for the property key token itself, so the
suppression is narrow. Inner consumes inside computed keys now go
through regular `consume()` and run the check normally.

Add a focused AST snapshot test covering plain, shorthand, computed,
binding-pattern, and method-name property-key cases.
2026-04-27 08:04:11 +02:00
Andreas Kling
63d6ef1026 LibJS: Track nested function ids during Rust parsing
FunctionTable::extract_reachable() used to rediscover a function's
nested functions by walking the full body and parameter list during
bytecode generation. This is hot during page loading because creating
every lazy SFD pays for an extra structural AST traversal.

Record each parser-created function's direct child function ids while
parsing instead. Extraction can then recursively move that known
subtree without scanning the enclosing function again.

Keep the old structural scan for codegen-synthesized wrappers, such as
class field initializers, where no parser function context exists.
This preserves the sparse FunctionTable storage while making the common
extraction path proportional to the nested function count.
2026-04-26 21:51:52 +02:00
Andrew Kaster
f26cb24751 Rust: Add a config file for rustfmt
This sets max_width to 120, which causes a lot of reformatting.
2026-04-18 08:05:47 -04:00
Johan Dahlin
d566a81b5c LibJS: Remove redundant name param from register_identifier
Use id.name (SharedUtf16String) directly, eliminating callers .to_vec()
allocations.

WebsitesParse: 1.04x faster
WebsitesRun:   1.05x faster
2026-04-08 16:41:25 +02:00
Johan Dahlin
6e33d36eb5 LibJS: Cache common identifier spellings in the lexer
Add SharedUtf16String (Rc<Utf16String>) for zero-copy sharing. Lexer
caches short ASCII identifiers in a direct-mapped table.

WebsitesParse: 1.03x faster, -5.1% RSS (-164 MB)
WebsitesRun:   1.05x faster, -4.7% RSS (-161 MB)
2026-04-08 16:41:25 +02:00
Johan Dahlin
5ab51b173d LibJS: Box ExpressionKind::Yield variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
c62b7f2f87 LibJS: Box ExpressionKind::ImportCall variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
15097fa8e0 LibJS: Box ExpressionKind::TaggedTemplateLiteral variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
9d0d8129f4 LibJS: Box ExpressionKind::OptionalChain variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
7262ab5880 LibJS: Box ExpressionKind::Member variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
07e187ae6d LibJS: Box ExpressionKind::Conditional variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
333ae7cc6d LibJS: Box ExpressionKind::Assignment variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
0cae77b94a LibJS: Box ExpressionKind::Update variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
4d5df1f10b LibJS: Box ExpressionKind::Logical variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
5a02479704 LibJS: Box ExpressionKind::Binary variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
2c210df618 LibJS: Box ExpressionKind::Object variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
badad22006 LibJS: Box ExpressionKind::Array variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
f3c68d516b LibJS: Box ExpressionKind::Sequence variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
9981b2eaf5 LibJS: Box ExpressionKind::PrivateIdentifier variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
9f0265f953 LibJS: Box ExpressionKind::BigIntLiteral variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
5da800bff2 LibJS: Box ExpressionKind::StringLiteral variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
3d0aaf41a9 LibJS: Box ExpressionKind::RegExpLiteral variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
780605f986 LibJS: Box ExpressionKind::TemplateLiteral variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
102c73a072 LibJS: Box ExpressionKind::SuperCall variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
0a1bf8079c LibJS: Box ExpressionKind::New variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
d2a5c1260d LibJS: Box ExpressionKind::Call variant 2026-03-28 11:55:41 +01:00
Andreas Kling
c8a0a960b5 LibJS: Validate regex literals during parsing
Now that LibRegex is safe to use (for parsing) off the main thread,
we can validate regex literals directly while parsing JavaScript.

This allows us to remove the deferred regex compilation pass that we
previously ran on the main thread after parsing JS in the background.
2026-03-27 17:32:19 +01:00
pwespi
3dc3bcb556 LibJS: Fix expected SyntaxErrors for private fields 2026-03-20 16:06:51 -05:00
Andreas Kling
f5eea4d232 LibJS: Fix catch parameter and new.target regressions
- Restrict catch parameter conflict check to only direct children
  of the catch body block, not nested scopes
- Set new_target_is_valid for dynamic function compilation (new
  Function)
- Move check_parameters_post_body before flag restoration in
  parse_method_definition so generator methods inside static init
  blocks correctly allow 'await' as a parameter name
2026-03-19 23:15:03 -05:00
Andreas Kling
49cc44a3eb LibJS: Reject arguments/eval in strict mode destructuring and arrows
Check identifier name validity for destructuring assignment pattern
bound names, and validate arrow function parameters after the arrow
is confirmed rather than during speculative parameter parsing.

This fixes arguments/eval as destructuring assignment targets and as
arrow function parameter names in strict mode.
2026-03-19 23:15:03 -05:00
Andreas Kling
66dbb355fe LibJS: Reject new.target in arrow functions at global scope
Arrow functions don't have their own new.target binding -- they
inherit from the enclosing scope. At the global level, there is no
enclosing function, so new.target inside a global arrow is invalid.

Add a new_target_is_valid flag to ParserFlags that is set to true
when entering regular (non-arrow) function bodies, method
definitions, and class static init blocks. Arrow functions inherit
the flag from their enclosing scope rather than setting it.
2026-03-19 23:15:03 -05:00
Andreas Kling
6029a3d40e LibJS: Add missing early errors in Rust parser
- Reject `true`, `false`, `null` as label identifiers
- Reject generator declarations in if-statement bodies (not covered
  by Annex B)
- Reject `await` as label in class static init blocks and modules
- Reject `arguments` in class static initialization blocks
- Reject generator shorthand without method body in object literals
- Reject `get constructor()` / `set constructor()` in class bodies
- Reject `super.#private` member access
2026-03-19 23:15:03 -05:00
RubenKelevra
fae2f8f3ba LibJS: Align new-expression paren flags with C++ parser 2026-03-18 17:41:36 -05:00
RubenKelevra
3cb636ca38 LibJS: Keep new call-paren optional chaining valid 2026-03-18 17:41:36 -05:00