Skip to content

LibJS: Preserve outer parameter bindings for arrow arguments access#8883

Open
LucaLeukert wants to merge 1 commit intoLadybirdBrowser:masterfrom
LucaLeukert:libjs-arrow-arguments
Open

LibJS: Preserve outer parameter bindings for arrow arguments access#8883
LucaLeukert wants to merge 1 commit intoLadybirdBrowser:masterfrom
LucaLeukert:libjs-arrow-arguments

Conversation

@LucaLeukert
Copy link
Copy Markdown

When an arrow function accesses the enclosing function's arguments object,
LibJS must keep mapped parameter bindings in the outer environment.

This fixes a case where (() => arguments[0])() caused the outer
parameter binding to be optimized away, which then broke mapped
arguments access.

Added a regression test for that case.

Closes #8805

Propagate arguments-object usage through arrow-function scopes during
scope analysis.

This keeps mapped outer parameters in the environment when an arrow
function reads from the enclosing function's arguments object, such as
in `(() => arguments[0])()`.
Copilot AI review requested due to automatic review settings April 12, 2026 18:34
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a LibJS scope-analysis bug where an arrow function’s access to the enclosing function’s arguments object could allow the outer function’s mapped parameter binding to be optimized away, leading to a crash.

Changes:

  • Adjusts scope-flag propagation so arguments access from arrow functions can be attributed to the enclosing function scope.
  • Adds a runtime regression test covering (() => arguments[0])() inside an outer function.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
Tests/LibJS/Runtime/arguments-object.js Adds a regression test for arrow access to an enclosing arguments object.
Libraries/LibJS/Rust/src/scope_collector.rs Updates scope closing/flag propagation logic to preserve outer parameter bindings when arrows access arguments.
Comments suppressed due to low confidence (1)

Libraries/LibJS/Rust/src/scope_collector.rs:461

  • close_scope() now propagates contains_await_expression out of arrow-function scopes. This will incorrectly mark module code like async () => await 1 as having top-level await, since the await occurs inside a function. Consider keeping function scopes (including arrows) as a boundary for contains_await_expression propagation, and only special-casing arrow functions for propagating the arguments-access flag.
        if let Some(parent_index) = self.records[index].parent
            && (self.records[index].scope_type != ScopeType::Function
                || self.records[index].is_arrow_function)
        {
            let c = &self.records[index];
            let arguments = c.contains_access_to_arguments_object_in_non_strict_mode;
            let eval = c.contains_direct_call_to_eval;
            let contains_await = c.contains_await_expression;
            self.records[parent_index].contains_access_to_arguments_object_in_non_strict_mode |=
                arguments;
            self.records[parent_index].contains_direct_call_to_eval |= eval;
            self.records[parent_index].contains_await_expression |= contains_await;
        }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +22
return (() => arguments[0])();
}

expect(outer(42)).toBe(42);
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This regression test validates the crash fix, but it doesn't actually assert mapped-arguments behavior (an unmapped arguments object would still return the original value). To ensure the outer parameter binding is preserved and mapped, add an assertion that mutating arguments[0] (or the parameter) inside the arrow is reflected in the other binding.

Suggested change
return (() => arguments[0])();
}
expect(outer(42)).toBe(42);
return (() => {
expect(arguments[0]).toBe(value);
arguments[0] = 84;
return value;
})();
}
expect(outer(42)).toBe(84);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LibJS: Crash in arrow function using arguments[0]

2 participants