Commit Graph

29 Commits

Author SHA1 Message Date
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
5374f0a85c LibJS: Add more early errors in Rust parser
- Reject duplicate bindings in catch parameter patterns
- Reject redeclaration of catch parameter with let/const/function
- Reject binding patterns with initializers in for-in heads (AnnexB
  only permits simple BindingIdentifier with initializer)
- Reject 'await' as binding identifier in class static init blocks
  and module code
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
RubenKelevra
ea8fa63e79 LibJS: Reject optional chaining on unparenthesized new 2026-03-18 17:41:36 -05:00
RubenKelevra
04b27429de LibJS: Isolate super validity in nested function scopes 2026-03-18 17:41:36 -05:00
RubenKelevra
d8469c384d LibJS: Reject invalid bare private identifier usage 2026-03-18 17:41:36 -05:00
RubenKelevra
d6229a1cc8 LibJS: Fix async arrow and for-of async parsing 2026-03-18 17:41:36 -05:00
RubenKelevra
af777b5d86 LibJS: Align duplicate parameter early errors 2026-03-18 17:41:36 -05:00
RubenKelevra
40984d0f39 LibJS: Enforce const initializers in declarations 2026-03-18 17:41:36 -05:00
juhotuho10
21d1cfdd63 LibJS/Rust: Apply lint clippy::redundant_clone 2026-03-16 13:55:16 -05:00
juhotuho10
dfa7993780 LibJS/Rust: Apply lint clippy::elidable_lifetime_names 2026-03-16 13:55:16 -05:00
juhotuho10
277177484e LibJS/Rust: Apply lint clippy::ref_option 2026-03-16 13:55:16 -05:00
juhotuho10
57c2e40f0e LibJS/Rust: Apply lint clippy::unnecessary_wraps 2026-03-16 13:55:16 -05:00
juhotuho10
3342e2c125 LibJS/Rust: Apply lint clippy::semicolon_if_nothing_returned 2026-03-16 13:55:16 -05:00
juhotuho10
671306d260 LibJS/Rust: Apply lint clippy::uninlined_format_args 2026-03-16 13:55:16 -05:00
Andreas Kling
c55418bc60 LibJS: Fix AssignmentTargetType for NewExpression and strict mode
Fix the static semantics for AssignmentTargetType in both the C++ and
Rust parsers:

- NewExpression is never a valid assignment target. Previously, the C++
  parser's is<CallExpression> check matched NewExpression since it
  inherits from CallExpression in our AST. Add explicit
  !is<NewExpression> guards everywhere.

- CallExpression as assignment target is only valid in non-strict mode
  (web-compat "runtime error for function call assignment targets").
  Pass strict_mode to is_simple_assignment_target and reject call
  expressions in strict mode for assignment, compound assignment,
  prefix/postfix update, and for-in/of LHS positions.

- Parenthesized ObjectLiteral/ArrayLiteral (e.g. `({}) = 1`) must not
  be treated as destructuring patterns. Track whether the primary
  expression was parenthesized and skip binding pattern synthesis.

Update existing tests that were testing incorrect behavior:
- `'use strict'; foo() = 'foo'` is now correctly a SyntaxError
- for-in/of with call expression LHS: use toEval() instead of
  toEvalTo() (which runs eval inside a class method, i.e. strict mode)
2026-03-13 13:08:22 -05:00
Andreas Kling
b2b72a1884 LibJS: Defer regex literal compilation to post-parse step
Move regex compilation out of the parsing hot path. Both the C++ and
Rust parsers now collect raw regex pattern+flags strings during parsing
and batch-compile them after parsing completes.

This is a prerequisite for moving the Rust parser to a background
thread, since LibRegex is thread-unsafe and FFI calls during parsing
prevent parallelization.

Flag validation remains in the parser since it's trivial string
checking with no LibRegex dependency.
2026-03-06 13:06:05 +01:00
Andreas Kling
00ffc340bc LibJS: Wrap CompiledRegex in Rc to allow AST cloning
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.
2026-02-25 21:54:30 +01:00
Andreas Kling
f19d00ca9e LibJS: Memoize failed arrow function attempts in Rust parser
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).
2026-02-24 18:42:13 +01:00
xnacly
48e906edfd Meta: Add 'cargo clippy -- -D clippy::all' to lint-ci.sh 2026-02-24 16:35:51 +01:00
xnacly
bbb6121df4 LibJs/Rust: Migrate to edition 2024 2026-02-24 16:35:51 +01:00
xnacly
e897b77e83 LibJS/Rust: Cargo fmt on all source files 2026-02-24 16:35:51 +01:00
Andreas Kling
6cdfbd01a6 LibJS: Add alternative source-to-bytecode pipeline in Rust
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.
2026-02-24 09:39:42 +01:00