CompiledRegex held an FFI handle with unique ownership and panicked
on clone. This caused a crash when a class field initializer contained
a regex literal, since the codegen wraps field initializers in a
synthetic function body by cloning the expression.
Wrapping CompiledRegex in Rc makes the clone a cheap refcount bump.
The take() semantics are preserved: the first codegen path to call
take() gets the handle, and Drop frees it if nobody took it.
Cache failed arrow function attempts by token offset. Once we
determine that '(' at offset N is not the start of an arrow
function, skip re-attempting at the same offset.
Without memoization, nested expressions like (a=(b=(c=(d=0))))
cause exponential work: each failed arrow attempt at an outer '('
re-parses all inner '(' positions during grouping expression
re-parse, and each inner position triggers its own arrow
attempts. With n nesting levels, the innermost position is
processed O(2^n) times.
The C++ parser already has this optimization (via the
try_parse_arrow_function_expression_failed_at_position()
memoization cache).
Small changes but many of them:
- all codegen now directly writes into the target file instead of
creating intermediate Strings via the Write trait
- all unwraps are now a combination of Results and ?
- field_type_info now returns a structure instead of a tuple.
- rebuilding now no longer appends the same code again, but truncates
before codegen
Add the rust-stable SDK extension, pre-download Corrosion and all crate
dependencies, and set up cargo vendoring so the Flatpak build can
compile Rust code without network access during the build phase.
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.