From d87005736e2522e52702e2c07035c6eab1590f36 Mon Sep 17 00:00:00 2001
From: raskad <32105367+raskad@users.noreply.github.com>
Date: Thu, 19 Dec 2024 19:48:12 +0000
Subject: [PATCH] Skip creation of arguments object if possible (#4087)

* Skip creation of arguments object if possible

* fix lints
---
 core/ast/src/scope.rs                        | 35 ++++++++++++++++++++
 core/engine/src/bytecompiler/declarations.rs |  4 +++
 2 files changed, 39 insertions(+)

diff --git a/core/ast/src/scope.rs b/core/ast/src/scope.rs
index 08481649955..f453299712a 100644
--- a/core/ast/src/scope.rs
+++ b/core/ast/src/scope.rs
@@ -18,6 +18,7 @@ struct Binding {
     lex: bool,
     strict: bool,
     escapes: bool,
+    accessed: bool,
 }
 
 /// A scope maps bound identifiers to their binding positions.
@@ -259,6 +260,7 @@ impl Scope {
                 .iter_mut()
                 .find(|b| &b.name == name)
             {
+                binding.accessed = true;
                 if crossed_function_border || eval_or_with {
                     binding.escapes = true;
                 }
@@ -296,6 +298,7 @@ impl Scope {
             lex: !function_scope,
             strict: false,
             escapes: self.is_global(),
+            accessed: false,
         });
         BindingLocator::declarative(
             name,
@@ -320,6 +323,7 @@ impl Scope {
             lex: true,
             strict,
             escapes: self.is_global(),
+            accessed: false,
         });
     }
 
@@ -583,6 +587,37 @@ impl FunctionScopes {
         &self.function_scope
     }
 
+    /// Returns if the arguments object is accessed in this function.
+    #[must_use]
+    pub fn arguments_object_accessed(&self) -> bool {
+        if self
+            .function_scope
+            .inner
+            .bindings
+            .borrow()
+            .first()
+            .filter(|b| b.name == "arguments" && b.accessed)
+            .is_some()
+        {
+            return true;
+        }
+
+        if let Some(scope) = &self.parameters_eval_scope {
+            if scope
+                .inner
+                .bindings
+                .borrow()
+                .first()
+                .filter(|b| b.name == "arguments" && b.accessed)
+                .is_some()
+            {
+                return true;
+            }
+        }
+
+        false
+    }
+
     /// Returns the parameters eval scope for this function.
     #[must_use]
     pub fn parameters_eval_scope(&self) -> Option<&Scope> {
diff --git a/core/engine/src/bytecompiler/declarations.rs b/core/engine/src/bytecompiler/declarations.rs
index 06b91d9293c..73b9d430722 100644
--- a/core/engine/src/bytecompiler/declarations.rs
+++ b/core/engine/src/bytecompiler/declarations.rs
@@ -974,6 +974,10 @@ impl ByteCompiler<'_> {
             }
         }
 
+        if arguments_object_needed {
+            arguments_object_needed = scopes.arguments_object_accessed();
+        }
+
         // 19-20
         drop(self.push_declarative_scope(scopes.parameters_eval_scope()));