Browse Source

Fix some remaining function parameter destructuring issues (#1349)

Marko Lahma 2 years ago
parent
commit
c1ad0afd47

+ 0 - 2
Jint.Tests.Test262/Test262Harness.settings.json

@@ -185,8 +185,6 @@
 
     // failing tests in new test suite (due to updating to latest and using whole set)
     "language/arguments-object/mapped/nonconfigurable-descriptors-define-failure.js",
-    "language/destructuring/binding/syntax/destructuring-array-parameters-function-arguments-length.js",
-    "language/destructuring/binding/syntax/destructuring-object-parameters-function-arguments-length.js",
     "language/eval-code/direct/arrow-fn-a-following-parameter-is-named-arguments-arrow-func-declare-arguments-assign-incl-def-param-arrow-arguments.js",
     "language/eval-code/direct/arrow-fn-a-following-parameter-is-named-arguments-arrow-func-declare-arguments-assign.js",
     "language/eval-code/direct/arrow-fn-a-preceding-parameter-is-named-arguments-arrow-func-declare-arguments-assign-incl-def-param-arrow-arguments.js",

+ 48 - 5
Jint.Tests/Runtime/DestructuringTests.cs

@@ -2,15 +2,58 @@ namespace Jint.Tests.Runtime;
 
 public class DestructuringTests
 {
+    private readonly Engine _engine;
+
+    public DestructuringTests()
+    {
+        _engine = new Engine()
+            .SetValue("equal", new Action<object, object>(Assert.Equal));
+    }
+
     [Fact]
-    public void WithStrings()
+    public void WithParameterStrings()
     {
         const string Script = @"
             return function([a, b, c]) {
-              return a === ""a"" && b === ""b"" && c === void undefined;
-            }(""ab"");";
+              equal('a', a);
+              equal('b', b);
+              return c === void undefined;
+            }('ab');";
+
+        Assert.True(_engine.Evaluate(Script).AsBoolean());
+    }
+
+    [Fact]
+    public void WithParameterObjectPrimitives()
+    {
+        const string Script = @"
+            return function({toFixed}, {slice}) {
+              equal(Number.prototype.toFixed, toFixed);
+              equal(String.prototype.slice, slice);
+              return true;
+            }(2,'');";
 
-        var engine = new Engine();
-        Assert.True(engine.Evaluate(Script).AsBoolean());
+        Assert.True(_engine.Evaluate(Script).AsBoolean());
+    }
+
+    [Fact]
+    public void WithParameterComputedProperties()
+    {
+        const string Script = @"
+            var qux = 'corge';
+            return function({ [qux]: grault }) {
+              equal('garply', grault);
+            }({ corge: 'garply' });";
+
+        _engine.Execute(Script);
+    }
+
+    [Fact]
+    public void WithParameterFunctionLengthProperty()
+    {
+        _engine.Execute("equal(0, ((x = 42, y) => {}).length);");
+        _engine.Execute("equal(1, ((x, y = 42, z) => {}).length);");
+        _engine.Execute("equal(1, ((a, b = 39,) => {}).length);");
+        _engine.Execute("equal(2, function({a, b}, [c, d]){}.length);");
     }
 }

+ 155 - 152
Jint.Tests/Runtime/FunctionTests.cs

@@ -4,26 +4,33 @@ using Jint.Native.Function;
 using Jint.Runtime;
 using Jint.Runtime.Interop;
 
-namespace Jint.Tests.Runtime
+namespace Jint.Tests.Runtime;
+
+public class FunctionTests
 {
-    public class FunctionTests
+    private readonly Engine _engine;
+
+    public FunctionTests()
     {
-        [Fact]
-        public void BindCombinesBoundArgumentsToCallArgumentsCorrectly()
-        {
-            var e = new Engine();
-            e.Evaluate("var testFunc = function (a, b, c) { return a + ', ' + b + ', ' + c + ', ' + JSON.stringify(arguments); }");
+        _engine = new Engine()
+            .SetValue("equal", new Action<object, object>(Assert.Equal));
+    }
 
-            Assert.Equal("a, 1, a, {\"0\":\"a\",\"1\":1,\"2\":\"a\"}", e.Evaluate("testFunc('a', 1, 'a');").AsString());
-            Assert.Equal("a, 1, a, {\"0\":\"a\",\"1\":1,\"2\":\"a\"}", e.Evaluate("testFunc.bind('anything')('a', 1, 'a');").AsString());
-        }
+    [Fact]
+    public void BindCombinesBoundArgumentsToCallArgumentsCorrectly()
+    {
+        _engine.Evaluate("var testFunc = function (a, b, c) { return a + ', ' + b + ', ' + c + ', ' + JSON.stringify(arguments); }");
 
-        [Fact]
-        public void ArrowFunctionShouldBeExtensible()
-        {
-            new Engine()
-                .SetValue("assert", new Action<bool>(Assert.True))
-                .Execute(@"
+        Assert.Equal("a, 1, a, {\"0\":\"a\",\"1\":1,\"2\":\"a\"}", _engine.Evaluate("testFunc('a', 1, 'a');").AsString());
+        Assert.Equal("a, 1, a, {\"0\":\"a\",\"1\":1,\"2\":\"a\"}", _engine.Evaluate("testFunc.bind('anything')('a', 1, 'a');").AsString());
+    }
+
+    [Fact]
+    public void ArrowFunctionShouldBeExtensible()
+    {
+        new Engine()
+            .SetValue("assert", new Action<bool>(Assert.True))
+            .Execute(@"
                     var a = () => null
                     Object.defineProperty(a, 'hello', { enumerable: true, get: () => 'world' })
                     assert(a.hello === 'world')
@@ -31,12 +38,12 @@ namespace Jint.Tests.Runtime
                     a.foo = 'bar';
                     assert(a.foo === 'bar');
                 ");
-        }
+    }
 
-        [Fact]
-        public void BlockScopeFunctionShouldWork()
-        {
-            const string script = @"
+    [Fact]
+    public void BlockScopeFunctionShouldWork()
+    {
+        const string Script = @"
 function execute(doc, args){
     var i = doc;
     {
@@ -49,21 +56,21 @@ function execute(doc, args){
 }
 ";
 
-            var engine = new Engine(options =>
-            {
-                options.Strict();
-            });
-            engine.Execute(script);
+        var engine = new Engine(options =>
+        {
+            options.Strict();
+        });
+        engine.Execute(Script);
 
-            var obj = engine.Evaluate("var obj = {}; execute(obj); return obj;").AsObject();
+        var obj = engine.Evaluate("var obj = {}; execute(obj); return obj;").AsObject();
 
-            Assert.Equal("ayende", obj.Get("Name").AsString());
-        }
+        Assert.Equal("ayende", obj.Get("Name").AsString());
+    }
 
-        [Fact]
-        public void ObjectCoercibleForCallable()
-        {
-            const string script = @"
+    [Fact]
+    public void ObjectCoercibleForCallable()
+    {
+        const string Script = @"
 var booleanCount = 0;
 Boolean.prototype.then = function() {
   booleanCount += 1;
@@ -74,59 +81,56 @@ function test() {
 testFunction.call(true);
 assertEqual(booleanCount, 1);
 ";
-            var engine = new Engine();
-            engine
-                .SetValue("testFunction", new ClrFunctionInstance(engine, "testFunction", (thisValue, args) =>
-                {
-                    return engine.Invoke(thisValue, "then", new[] { Undefined.Instance, args.At(0) });
-                }))
-                .SetValue("assertEqual", new Action<object, object>((a, b) => Assert.Equal(b, a)))
-                .Execute(script);
-        }
+        var engine = new Engine();
+        engine
+            .SetValue("testFunction", new ClrFunctionInstance(engine, "testFunction", (thisValue, args) =>
+            {
+                return engine.Invoke(thisValue, "then", new[] { Undefined.Instance, args.At(0) });
+            }))
+            .SetValue("assertEqual", new Action<object, object>((a, b) => Assert.Equal(b, a)))
+            .Execute(Script);
+    }
 
-        [Fact]
-        public void AnonymousLambdaShouldHaveNameDefined()
-        {
-            var engine = new Engine();
-            Assert.True(engine.Evaluate("(()=>{}).hasOwnProperty('name')").AsBoolean());
-        }
+    [Fact]
+    public void AnonymousLambdaShouldHaveNameDefined()
+    {
+        Assert.True(_engine.Evaluate("(()=>{}).hasOwnProperty('name')").AsBoolean());
+    }
 
-        [Fact]
-        public void CanInvokeConstructorsFromEngine()
-        {
-            var engine = new Engine();
+    [Fact]
+    public void CanInvokeConstructorsFromEngine()
+    {
+        _engine.Evaluate("class TestClass { constructor(a, b) { this.a = a; this.b = b; }}");
+        _engine.Evaluate("function TestFunction(a, b) { this.a = a; this.b = b; }");
 
-            engine.Evaluate("class TestClass { constructor(a, b) { this.a = a; this.b = b; }}");
-            engine.Evaluate("function TestFunction(a, b) { this.a = a; this.b = b; }");
+        var instanceFromClass = _engine.Construct("TestClass", "abc", 123).AsObject();
+        Assert.Equal("abc", instanceFromClass.Get("a"));
+        Assert.Equal(123, instanceFromClass.Get("b"));
 
-            var instanceFromClass = engine.Construct("TestClass", "abc", 123).AsObject();
-            Assert.Equal("abc", instanceFromClass.Get("a"));
-            Assert.Equal(123, instanceFromClass.Get("b"));
+        var instanceFromFunction = _engine.Construct("TestFunction", "abc", 123).AsObject();
+        Assert.Equal("abc", instanceFromFunction.Get("a"));
+        Assert.Equal(123, instanceFromFunction.Get("b"));
 
-            var instanceFromFunction = engine.Construct("TestFunction", "abc", 123).AsObject();
-            Assert.Equal("abc", instanceFromFunction.Get("a"));
-            Assert.Equal(123, instanceFromFunction.Get("b"));
+        var arrayInstance = (ArrayInstance) _engine.Construct("Array", "abc", 123).AsObject();
+        Assert.Equal((uint) 2, arrayInstance.Length);
+        Assert.Equal("abc", arrayInstance[0]);
+        Assert.Equal(123, arrayInstance[1]);
+    }
 
-            var arrayInstance = (ArrayInstance) engine.Construct("Array", "abc", 123).AsObject();
-            Assert.Equal((uint) 2, arrayInstance.Length);
-            Assert.Equal("abc", arrayInstance[0]);
-            Assert.Equal(123, arrayInstance[1]);
-        }
+    [Fact]
+    public void FunctionInstancesCanBePassedToHost()
+    {
+        var engine = new Engine();
+        Func<JsValue, JsValue[], JsValue> ev = null;
 
-        [Fact]
-        public void FunctionInstancesCanBePassedToHost()
+        void addListener(Func<JsValue, JsValue[], JsValue> callback)
         {
-            var engine = new Engine();
-            Func<JsValue, JsValue[], JsValue> ev = null;
-
-            void addListener(Func<JsValue, JsValue[], JsValue> callback)
-            {
-                ev = callback;
-            }
+            ev = callback;
+        }
 
-            engine.SetValue("addListener", new Action<Func<JsValue, JsValue[], JsValue>>(addListener));
+        engine.SetValue("addListener", new Action<Func<JsValue, JsValue[], JsValue>>(addListener));
 
-            engine.Execute(@"
+        engine.Execute(@"
                 var a = 5;
 
                 (function() {
@@ -137,29 +141,29 @@ assertEqual(booleanCount, 1);
                 })();
 ");
 
-            Assert.Equal(5, engine.Evaluate("a"));
+        Assert.Equal(5, engine.Evaluate("a"));
 
-            ev(null, new JsValue[0]);
-            Assert.Equal(10, engine.Evaluate("a"));
+        ev(null, new JsValue[0]);
+        Assert.Equal(10, engine.Evaluate("a"));
 
-            ev(null, new JsValue[] { 20 });
-            Assert.Equal(30, engine.Evaluate("a"));
-        }
+        ev(null, new JsValue[] { 20 });
+        Assert.Equal(30, engine.Evaluate("a"));
+    }
 
-        [Fact]
-        public void BoundFunctionsCanBePassedToHost()
-        {
-            var engine = new Engine();
-            Func<JsValue, JsValue[], JsValue> ev = null;
+    [Fact]
+    public void BoundFunctionsCanBePassedToHost()
+    {
+        var engine = new Engine();
+        Func<JsValue, JsValue[], JsValue> ev = null;
 
-            void addListener(Func<JsValue, JsValue[], JsValue> callback)
-            {
-                ev = callback;
-            }
+        void addListener(Func<JsValue, JsValue[], JsValue> callback)
+        {
+            ev = callback;
+        }
 
-            engine.SetValue("addListener", new Action<Func<JsValue, JsValue[], JsValue>>(addListener));
+        engine.SetValue("addListener", new Action<Func<JsValue, JsValue[], JsValue>>(addListener));
 
-            engine.Execute(@"
+        engine.Execute(@"
                 var a = 5;
 
                 (function() {
@@ -169,92 +173,91 @@ assertEqual(booleanCount, 1);
                 })();
             ");
 
-            Assert.Equal(5, engine.Evaluate("a"));
+        Assert.Equal(5, engine.Evaluate("a"));
 
-            ev(null, new JsValue[0]);
-            Assert.Equal(10, engine.Evaluate("a"));
+        ev(null, new JsValue[0]);
+        Assert.Equal(10, engine.Evaluate("a"));
 
-            ev(null, new JsValue[] { 20 });
-            Assert.Equal(30, engine.Evaluate("a"));
-        }
+        ev(null, new JsValue[] { 20 });
+        Assert.Equal(30, engine.Evaluate("a"));
+    }
 
-        [Fact]
-        public void ConstructorsCanBePassedToHost()
-        {
-            var engine = new Engine();
-            Func<JsValue, JsValue[], JsValue> ev = null;
+    [Fact]
+    public void ConstructorsCanBePassedToHost()
+    {
+        var engine = new Engine();
+        Func<JsValue, JsValue[], JsValue> ev = null;
 
-            void addListener(Func<JsValue, JsValue[], JsValue> callback)
-            {
-                ev = callback;
-            }
+        void addListener(Func<JsValue, JsValue[], JsValue> callback)
+        {
+            ev = callback;
+        }
 
-            engine.SetValue("addListener", new Action<Func<JsValue, JsValue[], JsValue>>(addListener));
+        engine.SetValue("addListener", new Action<Func<JsValue, JsValue[], JsValue>>(addListener));
 
-            engine.Execute(@"addListener(Boolean)");
+        engine.Execute(@"addListener(Boolean)");
 
-            Assert.Equal(true, ev(JsValue.Undefined, new JsValue[] { "test" }));
-            Assert.Equal(true, ev(JsValue.Undefined, new JsValue[] { 5 }));
-            Assert.Equal(false, ev(JsValue.Undefined, new JsValue[] { false }));
-            Assert.Equal(false, ev(JsValue.Undefined, new JsValue[] { 0}));
-            Assert.Equal(false, ev(JsValue.Undefined, new JsValue[] { JsValue.Undefined }));
-        }
+        Assert.Equal(true, ev(JsValue.Undefined, new JsValue[] { "test" }));
+        Assert.Equal(true, ev(JsValue.Undefined, new JsValue[] { 5 }));
+        Assert.Equal(false, ev(JsValue.Undefined, new JsValue[] { false }));
+        Assert.Equal(false, ev(JsValue.Undefined, new JsValue[] { 0}));
+        Assert.Equal(false, ev(JsValue.Undefined, new JsValue[] { JsValue.Undefined }));
+    }
 
-        [Fact]
-        public void FunctionsShouldResolveToSameReference()
-        {
-            var engine = new Engine();
-            engine.SetValue("equal", new Action<object, object>(Assert.Equal));
-            engine.Execute(@"
+    [Fact]
+    public void FunctionsShouldResolveToSameReference()
+    {
+        _engine.SetValue("equal", new Action<object, object>(Assert.Equal));
+        _engine.Execute(@"
                 function testFn() {}
                 equal(testFn, testFn);
             ");
-        }
-        
-        [Fact]
-        public void CanInvokeCallForFunctionInstance()
-        {
-            var engine = new Engine();
+    }
 
-            engine.Evaluate(@"
+    [Fact]
+    public void CanInvokeCallForFunctionInstance()
+    {
+        _engine.Evaluate(@"
                 (function () {
                     function foo(a = 123) { return a; }
                     foo()
                 })")
-                .As<FunctionInstance>().Call();
+            .As<FunctionInstance>().Call();
 
-            var result = engine.Evaluate(@"
+        var result = _engine.Evaluate(@"
                 (function () {
                     class Foo { test() { return 123 } }
                     let f = new Foo()
                     return f.test()
                 })")
-                .As<FunctionInstance>().Call();
+            .As<FunctionInstance>().Call();
 
-            Assert.True(result.IsInteger());
-            Assert.Equal(123, result.AsInteger());
-        }
-
-        [Fact]
-        public void CanInvokeFunctionViaEngineInstance()
-        {
-            var engine = new Engine();
+        Assert.True(result.IsInteger());
+        Assert.Equal(123, result.AsInteger());
+    }
 
-            var function = engine.Evaluate("function bar(a) { return a; }; bar;");
+    [Fact]
+    public void CanInvokeFunctionViaEngineInstance()
+    {
+        var function = _engine.Evaluate("function bar(a) { return a; }; bar;");
 
-            Assert.Equal(123, engine.Call(function, 123));
-            Assert.Equal(123, engine.Call("bar", 123));
-        }
+        Assert.Equal(123, _engine.Call(function, 123));
+        Assert.Equal(123, _engine.Call("bar", 123));
+    }
 
-        [Fact]
-        public void CanInvokeFunctionViaEngineInstanceWithCustomThisObj()
-        {
-            var engine = new Engine();
+    [Fact]
+    public void CanInvokeFunctionViaEngineInstanceWithCustomThisObj()
+    {
+        var function = _engine.Evaluate("function baz() { return this; }; baz;");
 
-            var function = engine.Evaluate("function baz() { return this; }; baz;");
+        Assert.Equal("I'm this!", TypeConverter.ToString(_engine.Call(function, "I'm this!", Arguments.Empty)));
+        Assert.Equal("I'm this!", TypeConverter.ToString(function.Call("I'm this!", Arguments.Empty)));
+    }
 
-            Assert.Equal("I'm this!", TypeConverter.ToString(engine.Call(function, "I'm this!", Arguments.Empty)));
-            Assert.Equal("I'm this!", TypeConverter.ToString(function.Call("I'm this!", Arguments.Empty)));
-        }
+    [Fact]
+    public void ArrowFunction()
+    {
+        const string Script = @"var f = (function() { return z => arguments[0]; }(5)); equal(5, f(6));";
+        _engine.Execute(Script);
     }
 }

+ 10 - 32
Jint/HoistingScope.cs

@@ -12,22 +12,19 @@ namespace Jint
 
         internal readonly List<Declaration>? _lexicalDeclarations;
         internal readonly List<string>? _lexicalNames;
-        internal readonly bool _hasArgumentsReference;
 
         private HoistingScope(
             List<FunctionDeclaration>? functionDeclarations,
             List<Key>? varNames,
             List<VariableDeclaration>? variableDeclarations,
             List<Declaration>? lexicalDeclarations,
-            List<string>? lexicalNames,
-            bool hasArgumentsReference)
+            List<string>? lexicalNames)
         {
             _functionDeclarations = functionDeclarations;
             _varNames = varNames;
             _variablesDeclarations = variableDeclarations;
             _lexicalDeclarations = lexicalDeclarations;
             _lexicalNames = lexicalNames;
-            _hasArgumentsReference = hasArgumentsReference;
         }
 
         public static HoistingScope GetProgramLevelDeclarations(
@@ -36,38 +33,28 @@ namespace Jint
             bool collectVarNames = false,
             bool collectLexicalNames = false)
         {
-            var treeWalker = new ScriptWalker(strict, collectVarNames, collectLexicalNames, checkArgumentsReference: false);
+            var treeWalker = new ScriptWalker(strict, collectVarNames, collectLexicalNames);
             treeWalker.Visit(script, null);
+
             return new HoistingScope(
                 treeWalker._functions,
                 treeWalker._varNames,
                 treeWalker._variableDeclarations,
                 treeWalker._lexicalDeclarations,
-                treeWalker._lexicalNames,
-                false);
+                treeWalker._lexicalNames);
         }
 
         public static HoistingScope GetFunctionLevelDeclarations(bool strict, IFunction node)
         {
-            var treeWalker = new ScriptWalker(strict, collectVarNames: true, collectLexicalNames: true, checkArgumentsReference: true);
+            var treeWalker = new ScriptWalker(strict, collectVarNames: true, collectLexicalNames: true);
             treeWalker.Visit(node.Body, null);
 
-            if (!treeWalker._hasArgumentsReference)
-            {
-                ref readonly var parameters = ref node.Params;
-                for (var i = 0; i < parameters.Count; ++i)
-                {
-                    treeWalker.Visit(parameters[i], null);
-                }
-            }
-
             return new HoistingScope(
                 treeWalker._functions,
                 treeWalker._varNames,
                 treeWalker._variableDeclarations,
                 treeWalker._lexicalDeclarations,
-                treeWalker._lexicalNames,
-                treeWalker._hasArgumentsReference);
+                treeWalker._lexicalNames);
         }
 
         public static HoistingScope GetModuleLevelDeclarations(
@@ -75,16 +62,15 @@ namespace Jint
             bool collectVarNames = false,
             bool collectLexicalNames = false)
         {
-            //Modules area always strict
-            var treeWalker = new ScriptWalker(strict: true, collectVarNames, collectLexicalNames, checkArgumentsReference: false);
+            // modules area always strict
+            var treeWalker = new ScriptWalker(strict: true, collectVarNames, collectLexicalNames);
             treeWalker.Visit(module, null);
             return new HoistingScope(
                 treeWalker._functions,
                 treeWalker._varNames,
                 treeWalker._variableDeclarations,
                 treeWalker._lexicalDeclarations,
-                treeWalker._lexicalNames,
-                false);
+                treeWalker._lexicalNames);
         }
 
         public static List<Declaration>? GetLexicalDeclarations(BlockStatement statement)
@@ -224,17 +210,14 @@ namespace Jint
             internal List<Key>? _varNames;
 
             private readonly bool _collectLexicalNames;
-            private readonly bool _checkArgumentsReference;
             internal List<Declaration>? _lexicalDeclarations;
             internal List<string>? _lexicalNames;
-            internal bool _hasArgumentsReference;
 
-            public ScriptWalker(bool strict, bool collectVarNames, bool collectLexicalNames, bool checkArgumentsReference)
+            public ScriptWalker(bool strict, bool collectVarNames, bool collectLexicalNames)
             {
                 _strict = strict;
                 _collectVarNames = collectVarNames;
                 _collectLexicalNames = collectLexicalNames;
-                _checkArgumentsReference = checkArgumentsReference;
             }
 
             public void Visit(Node node, Node? parent)
@@ -242,11 +225,6 @@ namespace Jint
                 foreach (var childNode in node.ChildNodes)
                 {
                     var childType = childNode.Type;
-                    if (_checkArgumentsReference && childType == Nodes.Identifier)
-                    {
-                        _hasArgumentsReference |= ((Identifier) childNode).Name == "arguments";
-                    }
-
                     if (childType == Nodes.VariableDeclaration)
                     {
                         var variableDeclaration = (VariableDeclaration)childNode;

+ 5 - 28
Jint/Runtime/Environments/FunctionEnvironmentRecord.cs

@@ -194,19 +194,15 @@ namespace Jint.Runtime.Environments
                 ExceptionHelper.ThrowTypeError(_functionObject._realm, "Destructed parameter is null or undefined");
             }
 
-            if (!argument.IsObject())
-            {
-                return;
-            }
-
-            var argumentObject = argument.AsObject();
+            var argumentObject = TypeConverter.ToObject(_engine.Realm , argument);
 
-            var processedProperties = objectPattern.Properties.Count > 0 && objectPattern.Properties[objectPattern.Properties.Count - 1] is RestElement
+            ref readonly var properties = ref objectPattern.Properties;
+            var processedProperties = properties.Count > 0 && properties[properties.Count - 1] is RestElement
                 ? new HashSet<JsValue>()
                 : null;
 
             var jsValues = _engine._jsValueArrayPool.RentArray(1);
-            foreach (var property in objectPattern.Properties)
+            foreach (var property in properties)
             {
                 var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
                 var paramVarEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
@@ -217,26 +213,7 @@ namespace Jint.Runtime.Environments
                 {
                     if (property is Property p)
                     {
-                        JsString propertyName = JsString.Empty;
-                        if (p.Key is Identifier propertyIdentifier)
-                        {
-                            propertyName = JsString.Create(propertyIdentifier.Name);
-                        }
-                        else if (p.Key is Literal propertyLiteral)
-                        {
-                            propertyName = JsString.Create(propertyLiteral.Raw);
-                        }
-                        else if (p.Key is CallExpression callExpression)
-                        {
-                            var jintCallExpression = new JintCallExpression(callExpression);
-                            var jsValue = jintCallExpression.GetValue(context);
-                            propertyName = TypeConverter.ToJsString(jsValue);
-                        }
-                        else
-                        {
-                            ExceptionHelper.ThrowArgumentOutOfRangeException("property", "unknown object pattern property type");
-                        }
-
+                        var propertyName = p.GetKey(_engine);
                         processedProperties?.Add(propertyName.ToString());
                         jsValues[0] = argumentObject.Get(propertyName);
                         SetFunctionParameter(context, p.Value, jsValues, 0, initiallyEmpty);

+ 59 - 7
Jint/Runtime/Interpreter/JintFunctionDefinition.cs

@@ -247,7 +247,7 @@ internal sealed class JintFunctionDefinition
         if (state.ArgumentsObjectNeeded)
         {
             // just one extra check...
-            state.ArgumentsObjectNeeded = hoistingScope._hasArgumentsReference;
+            state.ArgumentsObjectNeeded = ArgumentsUsageAstVisitor.HasArgumentsReference(function);
         }
 
         var parameterBindings = new HashSet<Key>(state.ParameterNames);
@@ -417,24 +417,25 @@ internal sealed class JintFunctionDefinition
         hasArguments = false;
         state.IsSimpleParameterList  = true;
 
+        var countParameters = true;
         ref readonly var functionDeclarationParams = ref function.Params;
         var count = functionDeclarationParams.Count;
         var parameterNames = new List<Key>(count);
         for (var i = 0; i < count; i++)
         {
             var parameter = functionDeclarationParams[i];
-            if (parameter is Identifier id)
+            var type = parameter.Type;
+
+            if (type == Nodes.Identifier)
             {
+                var id = (Identifier) parameter;
                 state.HasDuplicates |= parameterNames.Contains(id.Name);
                 hasArguments = id.Name == "arguments";
                 parameterNames.Add(id.Name);
-                if (state.IsSimpleParameterList)
-                {
-                    state.Length++;
-                }
             }
-            else if (parameter.Type != Nodes.Literal)
+            else if (type != Nodes.Literal)
             {
+                countParameters &= type != Nodes.AssignmentPattern;
                 state.IsSimpleParameterList = false;
                 GetBoundNames(
                     parameter,
@@ -445,8 +446,59 @@ internal sealed class JintFunctionDefinition
                     ref state.HasDuplicates,
                     ref hasArguments);
             }
+
+            if (countParameters && type is Nodes.Identifier or Nodes.ObjectPattern or Nodes.ArrayPattern)
+            {
+                state.Length++;
+            }
         }
 
         state.ParameterNames = parameterNames.ToArray();
     }
+
+    private static class ArgumentsUsageAstVisitor
+    {
+        public static bool HasArgumentsReference(IFunction function)
+        {
+            if (HasArgumentsReference(function.Body))
+            {
+                return true;
+            }
+
+            ref readonly var parameters = ref function.Params;
+            for (var i = 0; i < parameters.Count; ++i)
+            {
+                if (HasArgumentsReference(parameters[i]))
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        private static bool HasArgumentsReference(Node node)
+        {
+            foreach (var childNode in node.ChildNodes)
+            {
+                var childType = childNode.Type;
+                if (childType == Nodes.Identifier)
+                {
+                    if (((Identifier) childNode).Name == "arguments")
+                    {
+                        return true;
+                    }
+                }
+                else if (childType != Nodes.FunctionDeclaration && !childNode.ChildNodes.IsEmpty())
+                {
+                    if (HasArgumentsReference(childNode))
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+    }
 }