Commit graph

57 commits

Author SHA1 Message Date
Andreas Kling
b6ac36c200 LibJS: Deduplicate adjacent source map entries during codegen
Avoid emitting consecutive source map entries when they carry the
same source range. The bytecode offset for the previous entry remains
valid for later PCs because source lookup now uses the largest source
map entry whose offset is not greater than the program counter.

This keeps stack traces stable while allowing statement-sized runs of
bytecode to share one source map entry.
2026-05-14 09:41:03 +02:00
Andreas Kling
3604259676 LibJS: Add off-thread function bytecode artifacts
Add Rust and C++ integration points for cloning lazy function compile
payloads, compiling them to GC-free bytecode off the main thread, and
materializing the result later on the main thread.

The cloned payload lets background compilation race with lazy
main-thread compilation without sharing AST ownership between threads.
Compiled function artifacts recursively include nested functions, so
materialization can discard the corresponding AST subtree.
2026-05-06 08:20:06 +02:00
Andreas Kling
d9dd412440 LibJS: Use foldhash in parser and scope-collector hash maps
The std default RandomState (SipHash) was using ~9 percentage points
of CPU on hash_one and write across the parse hot path, with the
string interner adding another ~3 pp on top. The cost was spread
across the interner, the scope collector's IndexMap<Utf16String, _>,
and several parser-side HashSet<Utf16String> declarations.

Use foldhash::quality::RandomState for the parser, scope collector,
and string interner via a new fast_hash module. Quality keeps
HashDoS resistance (keys are lexer tokens, attacker-controlled in a
browser context) while shedding SipHash's per-byte cost, and on this
workload it benchmarks slightly faster than foldhash::fast.
2026-05-05 13:53:51 +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
5b2dd60d11 LibJS: Add AST arena types for identifiers, scopes, and interned strings
Introduce IdentifierArena, ScopeArena, and StringInterner plus their
opaque IdentifierId/ScopeId/StringId index newtypes, bundled under
AstArena.
2026-05-05 13:53:51 +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
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
b542617e09 LibJS: Box StatementKind::ClassFieldInitializer variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
057725c731 LibJS: Box StatementKind::FunctionDeclaration variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
247b9a0a87 LibJS: Box StatementKind::UsingDeclaration variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
0167c83b35 LibJS: Box StatementKind::VariableDeclaration variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
938ec6ca18 LibJS: Box StatementKind::Labelled variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
345dbab49a LibJS: Box StatementKind::With variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
a36d7e5d88 LibJS: Box StatementKind::ForInOf variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
64ea365379 LibJS: Box StatementKind::For variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
31ae13eec6 LibJS: Box StatementKind::DoWhile variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
f29b7ab579 LibJS: Box StatementKind::While variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
3496379d09 LibJS: Box StatementKind::If variant 2026-03-28 11:55:41 +01: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
8dda68e642 LibJS: Box StatementKind::Program 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
bb44d3bb38 LibJS: Box StatementKind::Export variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
9d29703962 LibJS: Box StatementKind::Import variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
b922b4a31b LibJS: Box StatementKind::Try variant 2026-03-28 11:55:41 +01:00
Johan Dahlin
ff047383e4 LibJS: Box StatementKind::Switch 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
juhotuho10
3342e2c125 LibJS/Rust: Apply lint clippy::semicolon_if_nothing_returned 2026-03-16 13:55:16 -05:00