Ver código fonte

Implement arrow functions (#601)

Sam Lord 6 anos atrás
pai
commit
644795e8a9
29 arquivos alterados com 752 adições e 224 exclusões
  1. 10 10
      Jint.Tests.Ecma/TestCases/alltests.json
  2. 16 0
      Jint.Tests.Test262/LanguageTests.cs
  3. 18 10
      Jint.Tests.Test262/Test262Test.cs
  4. 179 41
      Jint.Tests.Test262/test/skipped.json
  5. 39 0
      Jint.Tests/Runtime/EngineTests.cs
  6. 9 6
      Jint/Engine.cs
  7. 8 0
      Jint/EsprimaExtensions.cs
  8. 1 1
      Jint/Jint.csproj
  9. 2 3
      Jint/Native/Argument/ArgumentsInstance.cs
  10. 123 0
      Jint/Native/Function/ArrowFunctionInstance.cs
  11. 2 3
      Jint/Native/Function/EvalFunctionInstance.cs
  12. 2 4
      Jint/Native/Function/ScriptFunctionInstance.cs
  13. 9 2
      Jint/Native/Iterator/IteratorProtocol.cs
  14. 3 1
      Jint/Runtime/Descriptors/Specialized/ClrAccessDescriptor.cs
  15. 216 90
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  16. 0 1
      Jint/Runtime/Interop/DelegateWrapper.cs
  17. 7 3
      Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs
  18. 31 0
      Jint/Runtime/Interpreter/Expressions/JintArrowFunctionExpression.cs
  19. 13 0
      Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs
  20. 4 2
      Jint/Runtime/Interpreter/Expressions/JintExpression.cs
  21. 44 34
      Jint/Runtime/Interpreter/JintFunctionDefinition.cs
  22. 3 4
      Jint/Runtime/Interpreter/JintStatementList.cs
  23. 0 1
      Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs
  24. 0 1
      Jint/Runtime/Interpreter/Statements/JintContinueStatement.cs
  25. 1 1
      Jint/Runtime/Interpreter/Statements/JintStatement.cs
  26. 3 4
      Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs
  27. 1 1
      Jint/Runtime/Interpreter/Statements/JintSwitchStatement.cs
  28. 7 0
      Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs
  29. 1 1
      README.md

+ 10 - 10
Jint.Tests.Ecma/TestCases/alltests.json

@@ -16825,8 +16825,8 @@
     source: "ch13/13.2/13.2-14-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "function length is configurable",
     source: "ch13/13.2/13.2-15-1.js"
   },
   {
@@ -24780,8 +24780,8 @@
     source: "ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-186.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "function length is configurable",
     source: "ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-187.js"
   },
   {
@@ -38255,18 +38255,18 @@
     source: "ch15/15.3/15.3.5/S15.3.5.1_A1_T3.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "function length is configurable",
     source: "ch15/15.3/15.3.5/S15.3.5.1_A2_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "function length is configurable",
     source: "ch15/15.3/15.3.5/S15.3.5.1_A2_T2.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "function length is configurable",
     source: "ch15/15.3/15.3.5/S15.3.5.1_A2_T3.js"
   },
   {

+ 16 - 0
Jint.Tests.Test262/LanguageTests.cs

@@ -28,6 +28,22 @@ namespace Jint.Tests.Test262
             RunTestInternal(sourceFile);
         }
 
+        [Theory(DisplayName = "language\\expressions\\arrow-function")]
+        [MemberData(nameof(SourceFiles), "language\\expressions\\arrow-function", false)]
+        [MemberData(nameof(SourceFiles), "language\\expressions\\arrow-function", true, Skip = "Skipped")]
+        protected void ArrowFunction(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+
+        [Theory(DisplayName = "language\\expressions\\function")]
+        [MemberData(nameof(SourceFiles), "language\\expressions\\function", false)]
+        [MemberData(nameof(SourceFiles), "language\\expressions\\function", true, Skip = "Skipped")]
+        protected void Function(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+
         [Theory(DisplayName = "language\\expressions\\call")]
         [MemberData(nameof(SourceFiles), "language\\expressions\\call", false)]
         [MemberData(nameof(SourceFiles), "language\\expressions\\call", true, Skip = "Skipped")]

+ 18 - 10
Jint.Tests.Test262/Test262Test.cs

@@ -22,6 +22,9 @@ namespace Jint.Tests.Test262
         private static readonly Dictionary<string, string> _skipReasons =
             new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 
+        private static readonly HashSet<string> _strictSkips =
+            new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+
         static Test262Test()
         {
             //NOTE: The Date tests in test262 assume the local timezone is Pacific Standard Time
@@ -51,7 +54,12 @@ namespace Jint.Tests.Test262
             var doc = JArray.Parse(content);
             foreach (var entry in doc.Values<JObject>())
             {
-                _skipReasons[entry["source"].Value<string>()] = entry["reason"].Value<string>();
+                var source = entry["source"].Value<string>();
+                _skipReasons[source] = entry["reason"].Value<string>();
+                if (entry.TryGetValue("mode", out var mode) && mode.Value<string>() == "strict")
+                {
+                    _strictSkips.Add(source);
+                }
             }
         }
 
@@ -94,19 +102,15 @@ namespace Jint.Tests.Test262
 
         protected void RunTestInternal(SourceFile sourceFile)
         {
-            RunTestCode(sourceFile.Code);
-        }
-
-        private void RunTestCode(string code)
-        {
-            if (code.IndexOf("onlyStrict", StringComparison.Ordinal) < 0)
+            if (sourceFile.Code.IndexOf("onlyStrict", StringComparison.Ordinal) < 0)
             {
-                RunTestCode(code, strict: false);
+                RunTestCode(sourceFile.Code, strict: false);
             }
 
-            if (code.IndexOf("noStrict", StringComparison.Ordinal) < 0)
+            if (!_strictSkips.Contains(sourceFile.Source)
+                && sourceFile.Code.IndexOf("noStrict", StringComparison.Ordinal) < 0)
             {
-                RunTestCode(code, strict: true);
+                RunTestCode(sourceFile.Code, strict: true);
             }
         }
 
@@ -205,6 +209,10 @@ namespace Jint.Tests.Test262
                                 skip = true;
                                 reason = "async-functions not implemented";
                                 break;
+                            case "new.target":
+                                skip = true;
+                                reason = "MetaProperty not implemented";
+                                break;
                         }
                     }
                 }

+ 179 - 41
Jint.Tests.Test262/test/skipped.json

@@ -91,10 +91,6 @@
     "source": "built-ins/Array/from/items-is-arraybuffer.js",
     "reason": "ArrayBuffer not implemented"
   },
-  {
-    "source": "built-ins/Array/prototype/concat/Array.prototype.concat_non-array.js",
-    "reason": "class keyword not implemented"
-  },
   {
     "source": "built-ins/Array/prototype/concat/Array.prototype.concat_large-typed-array.js",
     "reason": "Uint8Array not implemented"
@@ -183,10 +179,6 @@
     "source": "built-ins/Set/prototype/values/does-not-have-setdata-internal-slot-weakset.js",
     "reason": "WeakSet not implemented"
   },
-  {
-    "source": "built-ins/Set/prototype/forEach/this-arg-explicit-cannot-override-lexical-this-arrow.js",
-    "reason": "arrow functions not implemented"
-  },
   {
     "source": "built-ins/Array/prototype/reverse/length-exceeding-integer-limit-with-proxy.js",
     "reason": "proxies not implemented"
@@ -341,13 +333,9 @@
     "source": "language/statements/for-in/head-var-bound-names-dup.js",
     "reason": "destructing not implemented"
   },
-  {
-    "source": "language/rest-parameters/with-new-target.js",
-    "reason": "classes not implemented"
-  },
   {
     "source": "language/rest-parameters/arrow-function.js",
-    "reason": "arrow functions not implemented"
+    "reason": "Github issue #600 - rest parameters hang when not set"
   },
   {
     "source": "language/rest-parameters/object-pattern.js",
@@ -363,51 +351,94 @@
   },
   {
     "source": "built-ins/Number/prototype/toPrecision/range.js",
-    "reason": "arrow functions not implemented"
+    "reason": "Github issue #599 - dtoa function not precise enough"
   },
   {
     "source": "built-ins/Number/prototype/toFixed/range.js",
-    "reason": "arrow functions not implemented"
+    "reason": "Github issue #599 - dtoa function not precise enough"
   },
   {
     "source": "built-ins/Number/prototype/toExponential/range.js",
-    "reason": "arrow functions not implemented"
+    "reason": "Github issue #599 - dtoa function not precise enough"
   },
   {
     "source": "language/types/number/8.5.1.js",
     "reason": "C# can't distinguish 1.797693134862315808e+308 and 1.797693134862315708145274237317e+308"
   },
+
+  // function behaviour to be implemented
   {
-    "source": "language/statements/for/dstr-const-ary-ptrn-elem-id-init-fn-name-arrow.js",
-    "reason": "arrow functions not implemented"
+    "source": "language/expressions/function/scope-name-var-open-non-strict.js",
+    "reason": "inner binding is immutable (from parameters) Expected SameValue(«null», «function() {{ ... }}») to be true"
   },
   {
-    "source": "language/statements/for/dstr-const-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
+    "source": "language/expressions/function/scope-name-var-open-strict.js",
+    "reason": "inner binding rejects modification (from parameters) Expected a Error to be thrown but no exception was thrown at all"
   },
+
+  // let support
   {
-    "source": "language/statements/for/dstr-let-ary-ptrn-elem-id-init-fn-name-arrow.js",
-    "reason": "arrow functions not implemented"
+    "source": "language/statements/for/dstr-const-ary-ptrn-rest-obj-prop-id.js",
+    "reason": "let not implemented"
   },
   {
-    "source": "language/statements/for/dstr-let-ary-ptrn-elem-id-init-fn-name-class.js",
+    "source": "language/statements/for/dstr-var-ary-ptrn-rest-obj-prop-id.js",
+    "reason": "let not implemented"
+  },
+  {
+    "source": "language/statements/for/dstr-let-ary-ptrn-rest-obj-prop-id.js",
+    "reason": "let not implemented"
+  },
+  {
+    "source": "language/expressions/arrow-function/dstr-ary-ptrn-elem-id-init-fn-name-cover.js",
+    "mode": "strict",
+    "reason": "let not implemented"
+  },
+  {
+    "source": "language/expressions/arrow-function/dstr-ary-ptrn-rest-obj-prop-id.js",
+    "mode": "strict",
+    "reason": "let not implemented"
+  },
+  {
+    "source": "language/expressions/arrow-function/dstr-dflt-ary-ptrn-rest-obj-prop-id.js",
+    "mode": "strict",
+    "reason": "let not implemented"
+  },
+  {
+    "source": "language/expressions/function/dstr-ary-ptrn-rest-obj-prop-id.js",
+    "mode": "strict",
+    "reason": "let not implemented"
+  },
+  {
+    "source": "language/expressions/function/dstr-dflt-ary-ptrn-rest-obj-prop-id.js",
+    "mode": "strict",
+    "reason": "let not implemented"
+  },
+
+  // class support
+  {
+    "source": "language/expressions/arrow-function/dstr-dflt-ary-ptrn-elem-id-init-fn-name-class.js",
     "reason": "class not implemented"
   },
   {
-    "source": "language/statements/for/dstr-let-obj-ptrn-id-init-fn-name-arrow.js",
-    "reason": "arrow functions not implemented"
+    "source": "built-ins/Array/prototype/concat/Array.prototype.concat_non-array.js",
+    "reason": "class not implemented"
   },
   {
-    "source": "language/statements/for/dstr-const-obj-ptrn-id-init-fn-name-arrow.js",
-    "reason": "arrow functions not implemented"
+    "source": "language/rest-parameters/with-new-target.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/expressions/arrow-function/dstr-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
   },
   {
-    "source": "language/statements/for/dstr-var-ary-ptrn-elem-id-init-fn-name-arrow.js",
-    "reason": "arrow functions not implemented"
+    "source": "language/statements/for/dstr-const-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
   },
   {
-    "source": "language/statements/for/dstr-var-obj-ptrn-id-init-fn-name-arrow.js",
-    "reason": "arrow functions not implemented"
+    "source": "language/statements/for/dstr-let-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
   },
   {
     "source": "language/statements/for/dstr-let-obj-ptrn-id-init-fn-name-class.js",
@@ -426,21 +457,56 @@
     "reason": "class not implemented"
   },
   {
-    "source": "language/statements/for/dstr-const-ary-ptrn-rest-obj-prop-id.js",
-    "reason": "let not implemented"
+    "source": "language/expressions/arrow-function/dstr-dflt-obj-ptrn-id-init-fn-name-class.js",
+    "reason": "class not implemented"
   },
   {
-    "source": "language/statements/for/dstr-var-ary-ptrn-rest-obj-prop-id.js",
-    "reason": "let not implemented"
+    "source": "language/expressions/arrow-function/dstr-obj-ptrn-id-init-fn-name-class.js",
+    "reason": "class not implemented"
   },
   {
-    "source": "language/statements/for/dstr-let-ary-ptrn-rest-obj-prop-id.js",
-    "reason": "let not implemented"
+    "source": "language/expressions/arrow-function/lexical-super-property-from-within-constructor.js",
+    "reason": "class not implemented"
   },
-  
-  
-  
-  
+  {
+    "source": "language/expressions/arrow-function/lexical-supercall-from-immediately-invoked-arrow.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/expressions/arrow-function/lexical-super-property.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/expressions/arrow-function/lexical-super-call-from-within-constructor.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/expressions/function/dstr-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/expressions/function/dstr-dflt-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/expressions/function/dstr-obj-ptrn-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/expressions/function/dstr-dflt-obj-ptrn-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+
+
+  {
+    "source": "language/expressions/arrow-function/scope-paramsbody-var-open.js",
+    "reason": "not implemented: Creation of new variable environment for the function body (as distinct from that for the function's parameters)"
+  },
+  {
+    "source": "language/expressions/function/scope-paramsbody-var-open.js",
+    "reason": "not implemented: Creation of new variable environment for the function body (as distinct from that for the function's parameters)"
+  },
+
   // Esprima problems
   
   {
@@ -498,5 +564,77 @@
   {
     "source": "language/white-space/mongolian-vowel-separator-eval.js",
     "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/arrow-function/dflt-params-trailing-comma.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/arrow-function/dstr-dflt-obj-ptrn-rest-getter.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/arrow-function/dstr-dflt-obj-ptrn-rest-skip-non-enumerable.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/arrow-function/dstr-dflt-obj-ptrn-rest-val-obj.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/arrow-function/dstr-obj-ptrn-rest-getter.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/arrow-function/dstr-obj-ptrn-rest-val-obj.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/arrow-function/dstr-obj-ptrn-rest-skip-non-enumerable.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/arrow-function/params-trailing-comma-multiple.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/arrow-function/params-trailing-comma-single.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/function/dflt-params-trailing-comma.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/function/dstr-dflt-obj-ptrn-rest-getter.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/function/dstr-dflt-obj-ptrn-rest-skip-non-enumerable.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/function/dstr-dflt-obj-ptrn-rest-val-obj.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/function/dstr-obj-ptrn-rest-getter.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/function/dstr-obj-ptrn-rest-skip-non-enumerable.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/function/dstr-obj-ptrn-rest-val-obj.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/function/params-trailing-comma-multiple.js",
+    "reason": "Esprima problem"
+  },
+  {
+    "source": "language/expressions/function/params-trailing-comma-single.js",
+    "reason": "Esprima problem"
   }
 ]

+ 39 - 0
Jint.Tests/Runtime/EngineTests.cs

@@ -204,6 +204,45 @@ namespace Jint.Tests.Runtime
             ");
         }
 
+        [Fact]
+        public void ArrowFunctionCall()
+        {
+            RunTest(@"
+                var add = (a, b) => {
+                    return a + b;
+                }
+
+                var x = add(1, 2);
+                assert(x == 3);
+            ");
+        }
+
+        [Fact]
+        public void ArrowFunctionExpressionCall()
+        {
+            RunTest(@"
+                var add = (a, b) => a + b;
+
+                var x = add(1, 2);
+                assert(x === 3);
+            ");
+        }
+
+        [Fact]
+        public void ArrowFunctionScope()
+        {
+            RunTest(@"
+                var bob = {
+                    _name: ""Bob"",
+                    _friends: [""Alice""],
+                    printFriends() {
+                        this._friends.forEach(f => assert(this._name === ""Bob"" && f === ""Alice""))
+                    }
+                };
+                bob.printFriends();
+            ");
+        }
+
         [Fact]
         public void NewObjectsShouldUsePrivateProperties()
         {

+ 9 - 6
Jint/Engine.cs

@@ -429,8 +429,7 @@ namespace Jint
             {
                 DeclarationBindingInstantiation(
                     DeclarationBindingType.GlobalCode,
-                    ref program.HoistingScope.FunctionDeclarations,
-                    ref program.HoistingScope.VariableDeclarations,
+                    program.HoistingScope,
                     functionInstance: null,
                     arguments: null);
 
@@ -762,8 +761,7 @@ namespace Jint
         //  http://www.ecma-international.org/ecma-262/5.1/#sec-10.5
         internal bool DeclarationBindingInstantiation(
             DeclarationBindingType declarationBindingType,
-            ref Esprima.Ast.List<FunctionDeclaration> functionDeclarations,
-            ref Esprima.Ast.List<VariableDeclaration> variableDeclarations,
+            HoistingScope hoistingScope,
             FunctionInstance functionInstance,
             JsValue[] arguments)
         {
@@ -778,7 +776,9 @@ namespace Jint
                 var argsObj = _argumentsInstancePool.Rent(functionInstance, functionInstance._formalParameters, arguments, env, strict);
                 canReleaseArgumentsInstance = true;
 
-                var functionDeclaration = (functionInstance as ScriptFunctionInstance)?.FunctionDeclaration;
+
+                var functionDeclaration = (functionInstance as ScriptFunctionInstance)?.FunctionDeclaration ??
+                    (functionInstance as ArrowFunctionInstance)?.FunctionDeclaration;
 
                 if (!ReferenceEquals(der, null))
                 {
@@ -786,6 +786,7 @@ namespace Jint
                 }
                 else
                 {
+                    // TODO: match functionality with DeclarationEnvironmentRecord.AddFunctionParameters here
                     // slow path
                     var parameters = functionInstance._formalParameters;
                     for (var i = 0; i < parameters.Length; i++)
@@ -806,11 +807,13 @@ namespace Jint
                 }
             }
 
+            var functionDeclarations = hoistingScope.FunctionDeclarations;
             if (functionDeclarations.Count > 0)
             {
                 AddFunctionDeclarations(ref functionDeclarations, env, configurableBindings, strict);
             }
 
+            var variableDeclarations = hoistingScope.VariableDeclarations;
             if (variableDeclarations.Count == 0)
             {
                 return canReleaseArgumentsInstance;
@@ -848,7 +851,7 @@ namespace Jint
         }
 
         private void AddFunctionDeclarations(
-            ref Esprima.Ast.List<FunctionDeclaration> functionDeclarations,
+            ref NodeList<FunctionDeclaration> functionDeclarations,
             EnvironmentRecord env,
             bool configurableBindings,
             bool strict)

+ 8 - 0
Jint/EsprimaExtensions.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Jint.Native.Symbol;
 using Jint.Runtime;
@@ -39,5 +40,12 @@ namespace Jint
 
             return ExceptionHelper.ThrowArgumentException<string>("Unable to extract correct key");
         }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static bool IsFunctionWithName(this INode node)
+        {
+            var type = node.Type;
+            return type == Nodes.FunctionExpression || type == Nodes.ArrowFunctionExpression;
+        }
     }
 }

+ 1 - 1
Jint/Jint.csproj

@@ -7,6 +7,6 @@
     <LangVersion>latest</LangVersion>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Esprima" Version="1.0.0-beta-1178" />
+    <PackageReference Include="Esprima" Version="1.0.0-beta-1182" />
   </ItemGroup>
 </Project>

+ 2 - 3
Jint/Native/Argument/ArgumentsInstance.cs

@@ -118,10 +118,9 @@ namespace Jint.Native.Argument
                     return desc;
                 }
 
-                var isMapped = ParameterMap.GetOwnProperty(propertyName);
-                if (isMapped != PropertyDescriptor.Undefined)
+                if (ParameterMap.TryGetValue(propertyName, out var jsValue) && !jsValue.IsUndefined())
                 {
-                    desc.Value = ParameterMap.Get(propertyName);
+                    desc.Value = jsValue;
                 }
 
                 return desc;

+ 123 - 0
Jint/Native/Function/ArrowFunctionInstance.cs

@@ -0,0 +1,123 @@
+using System.Runtime.CompilerServices;
+using Esprima.Ast;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Environments;
+using Jint.Runtime.Interpreter;
+
+namespace Jint.Native.Function
+{
+    public sealed class ArrowFunctionInstance : FunctionInstance
+    {
+        private readonly JintFunctionDefinition _function;
+        private readonly JsValue _thisBinding;
+
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
+        /// </summary>
+        public ArrowFunctionInstance(
+            Engine engine,
+            IFunction functionDeclaration,
+            LexicalEnvironment scope,
+            bool strict)
+            : this(engine, new JintFunctionDefinition(engine, functionDeclaration), scope, strict)
+        {
+        }
+
+        internal ArrowFunctionInstance(
+            Engine engine,
+            JintFunctionDefinition function,
+            LexicalEnvironment scope,
+            bool strict)
+            : base(engine, "", function._parameterNames, scope, strict)
+        {
+            _function = function;
+
+            Extensible = false;
+            Prototype = Engine.Function.PrototypeObject;
+
+            _length = new PropertyDescriptor(JsNumber.Create(function._length), PropertyFlag.Configurable);
+            _thisBinding = _engine.ExecutionContext.ThisBinding;
+        }
+
+        // for example RavenDB wants to inspect this
+        public IFunction FunctionDeclaration => _function._function;
+
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.1
+        /// </summary>
+        /// <param name="thisArg"></param>
+        /// <param name="arguments"></param>
+        /// <returns></returns>
+        public override JsValue Call(JsValue thisArg, JsValue[] arguments)
+        {
+            var localEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _scope);
+
+            var strict = Strict || _engine._isStrict;
+            using (new StrictModeScope(strict, true))
+            {
+                _engine.EnterExecutionContext(
+                    localEnv,
+                    localEnv,
+                    _thisBinding);
+
+                try
+                {
+                    var argumentInstanceRented = _engine.DeclarationBindingInstantiation(
+                        DeclarationBindingType.FunctionCode,
+                        _function._hoistingScope,
+                        functionInstance: this,
+                        arguments);
+
+                    var result = _function._body.Execute();
+
+                    var value = result.GetValueOrDefault();
+
+                    if (argumentInstanceRented)
+                    {
+                        _engine.ExecutionContext.LexicalEnvironment?._record?.FunctionWasCalled();
+                        _engine.ExecutionContext.VariableEnvironment?._record?.FunctionWasCalled();
+                    }
+
+                    if (result.Type == CompletionType.Throw)
+                    {
+                        var ex = new JavaScriptException(value).SetCallstack(_engine, result.Location);
+                        throw ex;
+                    }
+
+                    if (result.Type == CompletionType.Return)
+                    {
+                        return value;
+                    }
+                }
+                finally
+                {
+                    _engine.LeaveExecutionContext();
+                }
+
+                return Undefined;
+            }
+        }
+
+        public override void Put(string propertyName, JsValue value, bool throwOnError)
+        {
+            AssertValidPropertyName(propertyName);
+            base.Put(propertyName, value, throwOnError);
+        }
+
+        public override JsValue Get(string propertyName)
+        {
+            AssertValidPropertyName(propertyName);
+            return base.Get(propertyName);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private void AssertValidPropertyName(string propertyName)
+        {
+            if (propertyName == "caller" || propertyName ==  "callee" || propertyName == "arguments")
+            {
+                ExceptionHelper.ThrowTypeError(_engine, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them");
+            }
+        }
+    }
+}

+ 2 - 3
Jint/Native/Function/EvalFunctionInstance.cs

@@ -58,9 +58,8 @@ namespace Jint.Native.Function
 
                             bool argumentInstanceRented = Engine.DeclarationBindingInstantiation(
                                 DeclarationBindingType.EvalCode,
-                                ref program.HoistingScope.FunctionDeclarations,
-                                ref program.HoistingScope.VariableDeclarations,
-                                functionInstance: this, 
+                                program.HoistingScope,
+                                functionInstance: this,
                                 arguments);
 
                             var statement = JintStatement.Build(_engine, program);

+ 2 - 4
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -37,8 +37,7 @@ namespace Jint.Native.Function
             Extensible = true;
             Prototype = _engine.Function.PrototypeObject;
 
-            var length = function._hasRestParameter ? _formalParameters.Length - 1 : _formalParameters.Length;
-            _length = new PropertyDescriptor(JsNumber.Create(length), PropertyFlag.AllForbidden);
+            _length = new PropertyDescriptor(JsNumber.Create(function._length), PropertyFlag.Configurable);
 
             var proto = new ObjectInstanceWithConstructor(engine, this)
             {
@@ -98,8 +97,7 @@ namespace Jint.Native.Function
                 {
                     var argumentInstanceRented = _engine.DeclarationBindingInstantiation(
                         DeclarationBindingType.FunctionCode,
-                        ref _function._hoistingScope.FunctionDeclarations,
-                        ref _function._hoistingScope.VariableDeclarations,
+                        _function._hoistingScope,
                         functionInstance: this,
                         arguments);
 

+ 9 - 2
Jint/Native/Iterator/IteratorProtocol.cs

@@ -40,11 +40,11 @@ namespace Jint.Native.Iterator
                     }
 
                     ProcessItem(args, currentValue);
-                } while (true);
+                } while (ShouldContinue);
             }
             catch
             {
-                _iterator.Return();
+                ReturnIterator();
                 throw;
             }
             finally
@@ -55,6 +55,13 @@ namespace Jint.Native.Iterator
             IterationEnd();
         }
 
+        protected void ReturnIterator()
+        {
+            _iterator.Return();
+        }
+
+        protected virtual bool ShouldContinue => true;
+
         protected virtual void IterationEnd()
         {
         }

+ 3 - 1
Jint/Runtime/Descriptors/Specialized/ClrAccessDescriptor.cs

@@ -29,7 +29,9 @@ namespace Jint.Runtime.Descriptors.Specialized
 
         private JsValue DoGet(JsValue n)
         {
-            return _env.GetBindingValue(_name, false);
+            return _env.TryGetBinding(_name, false, out var binding)
+                ? binding.Value
+                : JsValue.Undefined;
         }
 
         private void DoSet(JsValue n, JsValue o)

+ 216 - 90
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -1,14 +1,15 @@
-using System;
-using System.Collections.Generic;
- using System.Runtime.CompilerServices;
- using Esprima.Ast;
+using System;
+using System.Runtime.CompilerServices;
+using Esprima.Ast;
 using Jint.Collections;
 using Jint.Native;
 using Jint.Native.Argument;
+using Jint.Native.Array;
 using Jint.Native.Function;
- using Jint.Runtime.Interpreter.Expressions;
+using Jint.Native.Iterator;
+using Jint.Runtime.Interpreter.Expressions;
 
- namespace Jint.Runtime.Environments
+namespace Jint.Runtime.Environments
 {
     /// <summary>
     /// Represents a declarative environment record
@@ -255,136 +256,211 @@ using Jint.Native.Function;
             return keys;
         }
 
-        /// <summary>
-        /// Optimized version for function calls.
-        /// </summary>
         internal void AddFunctionParameters(
             FunctionInstance functionInstance,
             JsValue[] arguments,
             ArgumentsInstance argumentsInstance,
             IFunction functionDeclaration)
         {
-            var parameters = functionInstance._formalParameters;
+            var parameters = functionDeclaration.Params;
+
             bool empty = _dictionary == null && !_set;
-            if (empty && parameters.Length == 1 && parameters[0].Length != BindingNameArguments.Length)
-            {
-                var jsValue = arguments.Length == 0 ? Undefined : arguments[0];
-                jsValue = HandleAssignmentPatternIfNeeded(functionDeclaration, jsValue, 0);
-                jsValue = HandleRestPatternIfNeeded(_engine, functionDeclaration, arguments, 0, jsValue);
-                HandleObjectPatternIfNeeded(_engine, functionDeclaration, jsValue, 0);
 
-                var binding = new Binding(jsValue, false, true);
-                _set = true;
-                _key = parameters[0];
-                _value = binding;
-            }
-            else
+            if (ReferenceEquals(_argumentsBinding.Value, null)
+                && !(functionInstance is ArrowFunctionInstance))
             {
-                AddMultipleParameters(arguments, parameters, functionDeclaration);
+                _argumentsBinding = new Binding(argumentsInstance, canBeDeleted: false, mutable: true);
             }
 
-            if (ReferenceEquals(_argumentsBinding.Value, null))
+            for (var i = 0; i < parameters.Count; i++)
             {
-                _argumentsBinding = new Binding(argumentsInstance, canBeDeleted: false, mutable: true);
+                SetFunctionParameter(parameters[i], arguments, i, empty);
             }
+
         }
 
-        private void AddMultipleParameters(JsValue[] arguments, string[] parameters, IFunction functionDeclaration)
+        private void SetFunctionParameter(
+            INode parameter,
+            JsValue[] arguments,
+            int index,
+            bool initiallyEmpty)
         {
-            bool empty = _dictionary == null && !_set;
-            for (var i = 0; i < parameters.Length; i++)
+            var argument = arguments.Length > index ? arguments[index] : Undefined;
+
+            if (parameter is Identifier identifier)
+            {
+                SetItemSafely(identifier.Name, argument, initiallyEmpty);
+            }
+            else if (parameter is RestElement restElement)
             {
-                var argName = parameters[i];
-                var jsValue = i + 1 > arguments.Length ? Undefined : arguments[i];
+                // index + 1 == parameters.count because rest is last
+                int restCount = arguments.Length - (index + 1) + 1;
+                uint count = restCount > 0 ? (uint) restCount : 0;
+
+                var rest = _engine.Array.ConstructFast(count);
+
+                uint targetIndex = 0;
+                for (var argIndex = index; argIndex < arguments.Length; ++argIndex)
+                {
+                    rest.SetIndexValue(targetIndex++, arguments[argIndex], updateLength: false);
+                }
 
-                jsValue = HandleAssignmentPatternIfNeeded(functionDeclaration, jsValue, i);
-                if (i == parameters.Length - 1)
+                argument = rest;
+
+                if (restElement.Argument is Identifier restIdentifier)
+                {
+                    SetItemSafely(restIdentifier.Name, argument, initiallyEmpty);
+                }
+                else if (restElement.Argument is BindingPattern bindingPattern)
+                {
+                    SetFunctionParameter(bindingPattern, new [] { argument }, index, initiallyEmpty);
+                }
+                else
                 {
-                    jsValue = HandleRestPatternIfNeeded(_engine, functionDeclaration, arguments, i, jsValue);
+                    ExceptionHelper.ThrowSyntaxError(_engine, "Rest parameters can only be identifiers or arrays");
+                }
+            }
+            else if (parameter is ArrayPattern arrayPattern)
+            {
+                if (argument.IsNull())
+                {
+                    ExceptionHelper.ThrowTypeError(_engine, "Destructed parameter is null");
                 }
-                jsValue = HandleObjectPatternIfNeeded(_engine, functionDeclaration, jsValue, 0);
 
-                if (empty || !TryGetValue(argName, out var existing))
+                ArrayInstance array = null;
+                var arrayContents = ArrayExt.Empty<JsValue>();
+                if (argument.IsArray())
                 {
-                    var binding = new Binding(jsValue, false, true);
-                    if (argName.Length == 9 && argName == BindingNameArguments)
-                    {
-                        _argumentsBinding = binding;
-                    }
-                    else
+                    array = argument.AsArray();
+                }
+                else if (argument.IsObject() && argument.TryGetIterator(_engine, out var iterator))
+                {
+                    array = _engine.Array.ConstructFast(0);
+                    var protocol = new ArrayPatternProtocol(_engine, array, iterator, arrayPattern.Elements.Count);
+                    protocol.Execute();
+                }
+
+                if (!ReferenceEquals(array, null))
+                {
+                    arrayContents = new JsValue[array.Length];
+
+                    for (uint contentsIndex = 0; contentsIndex < array.Length; contentsIndex++)
                     {
-                        SetItem(argName, binding);
+                        arrayContents[contentsIndex] = array.Get(contentsIndex);
                     }
                 }
-                else
+
+                for (uint arrayIndex = 0; arrayIndex < arrayPattern.Elements.Count; arrayIndex++)
+                {
+                    SetFunctionParameter(arrayPattern.Elements[(int) arrayIndex], arrayContents, (int) arrayIndex, initiallyEmpty);
+                }
+            }
+            else if (parameter is ObjectPattern objectPattern)
+            {
+                if (argument.IsNullOrUndefined())
+                {
+                    ExceptionHelper.ThrowTypeError(_engine, "Destructed parameter is null or undefined");
+                }
+
+                if (!argument.IsObject())
+                {
+                    return;
+                }
+
+                var argumentObject = argument.AsObject();
+
+                var jsValues = _engine._jsValueArrayPool.RentArray(1);
+                foreach (var property in objectPattern.Properties)
                 {
-                    if (existing.Mutable)
+                    if (property.Key is Identifier propertyIdentifier)
+                    {
+                        argument = argumentObject.Get(propertyIdentifier.Name);
+                    }
+                    else if (property.Key is Literal propertyLiteral)
                     {
-                        ref var b = ref GetExistingItem(argName);
-                        b.Value = jsValue;
+                        argument = argumentObject.Get(propertyLiteral.Raw);
                     }
-                    else
+                    else if (property.Key is CallExpression callExpression)
                     {
-                        ExceptionHelper.ThrowTypeError(_engine, "Can't update the value of an immutable binding.");
+                        var jintCallExpression = JintExpression.Build(_engine, callExpression);
+                        argument = argumentObject.Get(jintCallExpression.GetValue().AsString());
                     }
+
+                    jsValues[0] = argument;
+                    SetFunctionParameter(property.Value, jsValues, 0, initiallyEmpty);
                 }
+                _engine._jsValueArrayPool.ReturnArray(jsValues);
             }
-        }
-        
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static JsValue HandleObjectPatternIfNeeded(Engine engine, IFunction functionDeclaration, JsValue jsValue, int index)
-        {
-            if (functionDeclaration.Params[index] is ObjectPattern op)
+            else if (parameter is AssignmentPattern assignmentPattern)
             {
-                if (jsValue.IsNullOrUndefined())
+                var idLeft = assignmentPattern.Left as Identifier;
+                if (idLeft != null
+                    && assignmentPattern.Right is Identifier idRight
+                    && idLeft.Name == idRight.Name)
                 {
-                    ExceptionHelper.ThrowTypeError(engine, "Cannot destructure 'undefined' or 'null'.");
+                    ExceptionHelper.ThrowReferenceError(_engine, idRight.Name);
                 }
-            }
 
-            return jsValue;
-        }
+                if (argument.IsUndefined())
+                {
+                    JsValue RunInNewParameterEnvironment(JintExpression exp)
+                    {
+                        var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
+                        var paramVarEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static JsValue HandleAssignmentPatternIfNeeded(IFunction functionDeclaration, JsValue jsValue, int index)
-        {
-            if (jsValue.IsUndefined()
-                && index < functionDeclaration?.Params.Count
-                && functionDeclaration.Params[index] is AssignmentPattern ap
-                && ap.Right is Literal l)
-            {
-                return JintLiteralExpression.ConvertToJsValue(l);
-            }
+                        _engine.EnterExecutionContext(paramVarEnv, paramVarEnv, _engine.ExecutionContext.ThisBinding);;
+                        var result = exp.GetValue();
+                        _engine.LeaveExecutionContext();
 
-            return jsValue;
+                        return result;
+                    }
+
+                    var expression = assignmentPattern.Right.As<Expression>();
+                    var jintExpression = JintExpression.Build(_engine, expression);
+
+                    argument = jintExpression is JintSequenceExpression
+                        ? RunInNewParameterEnvironment(jintExpression)
+                        : jintExpression.GetValue();
+
+                    if (idLeft != null && assignmentPattern.Right.IsFunctionWithName())
+                    {
+                        ((FunctionInstance) argument).SetFunctionName(idLeft.Name);
+                    }
+                }
+
+                SetFunctionParameter(assignmentPattern.Left, new []{ argument }, 0, initiallyEmpty);
+            }
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static JsValue HandleRestPatternIfNeeded(
-            Engine engine,
-            IFunction functionDeclaration,
-            JsValue[] arguments,
-            int index,
-            JsValue defaultValue)
+        private void SetItemSafely(string name, JsValue argument, bool initiallyEmpty)
         {
-            if (index < functionDeclaration?.Params.Count
-                && functionDeclaration.Params[index] is RestElement)
+            if (initiallyEmpty || !TryGetValue(name, out var existing))
             {
-                var count = (uint) (arguments.Length - functionDeclaration.Params.Count + 1);
-                var rest = engine.Array.ConstructFast(count);
-
-                uint targetIndex = 0;
-                for (var i = index; i < arguments.Length; ++i)
+                var binding = new Binding(argument, false, true);
+                if (name.Length == 9 && name == BindingNameArguments)
+                {
+                    _argumentsBinding = binding;
+                }
+                else
                 {
-                    rest.SetIndexValue(targetIndex++, arguments[i], updateLength: false);
+                    SetItem(name, binding);
+                }
+            }
+            else
+            {
+                if (existing.Mutable)
+                {
+                    ref var b = ref GetExistingItem(name);
+                    b.Value = argument;
+                }
+                else
+                {
+                    ExceptionHelper.ThrowTypeError(_engine, "Can't update the value of an immutable binding.");
                 }
-                return rest;
             }
-
-            return defaultValue;
         }
 
-        internal void AddVariableDeclarations(ref Esprima.Ast.List<VariableDeclaration> variableDeclarations)
+        internal void AddVariableDeclarations(ref NodeList<VariableDeclaration> variableDeclarations)
         {
             var variableDeclarationsCount = variableDeclarations.Count;
             for (var i = 0; i < variableDeclarationsCount; i++)
@@ -406,6 +482,21 @@ using Jint.Native.Function;
                 }
             }
         }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static JsValue HandleAssignmentPatternIfNeeded(IFunction functionDeclaration, JsValue jsValue, int index)
+        {
+            // TODO remove this method, overwrite with above SetFunctionParameter logic
+            if (jsValue.IsUndefined()
+                && index < functionDeclaration?.Params.Count
+                && functionDeclaration.Params[index] is AssignmentPattern ap
+                && ap.Right is Literal l)
+            {
+                return JintLiteralExpression.ConvertToJsValue(l);
+            }
+
+            return jsValue;
+        }
         
         internal override void FunctionWasCalled()
         {
@@ -428,5 +519,40 @@ using Jint.Native.Function;
                 _argumentsBindingWasAccessed = null;
             }
         }
+
+        private sealed class ArrayPatternProtocol : IteratorProtocol
+        {
+            private readonly ArrayInstance _instance;
+            private readonly int _max;
+            private long _index = -1;
+
+            public ArrayPatternProtocol(
+                Engine engine,
+                ArrayInstance instance,
+                IIterator iterator,
+                int max) : base(engine, iterator, 0)
+            {
+                _instance = instance;
+                _max = max;
+            }
+
+            protected override void ProcessItem(JsValue[] args, JsValue currentValue)
+            {
+                _index++;
+                var jsValue = ExtractValueFromIteratorInstance(currentValue);
+                _instance.SetIndexValue((uint) _index, jsValue, updateLength: false);
+            }
+
+            protected override bool ShouldContinue => _index < _max;
+
+            protected override void IterationEnd()
+            {
+                if (_index >= 0)
+                {
+                    _instance.SetLength((uint) _index);
+                    ReturnIterator();
+                }
+            }
+        }
     }
-}
+}

+ 0 - 1
Jint/Runtime/Interop/DelegateWrapper.cs

@@ -3,7 +3,6 @@ using System.Globalization;
 using System.Reflection;
 using Jint.Native;
 using Jint.Native.Function;
-using System.Linq;
 
 namespace Jint.Runtime.Interop
 {

+ 7 - 3
Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs

@@ -158,12 +158,13 @@ namespace Jint.Runtime.Interpreter.Expressions
                             && assignmentPattern.Right is Expression expression)
                         {
                             var jintExpression = Build(engine, expression);
+
                             value = jintExpression.GetValue();
                         }
 
                         if (assignmentPattern.Left is Identifier leftIdentifier)
                         {
-                            if (assignmentPattern.Right is FunctionExpression)
+                            if (assignmentPattern.Right.IsFunctionWithName())
                             {
                                 ((FunctionInstance) value).SetFunctionName(leftIdentifier.Name);
                             }
@@ -212,6 +213,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     if (value.IsUndefined() && assignmentPattern.Right is Expression expression)
                     {
                         var jintExpression = Build(engine, expression);
+
                         value = jintExpression.GetValue();
                     }
                     
@@ -222,11 +224,12 @@ namespace Jint.Runtime.Interpreter.Expressions
                     }
                     
                     var target = assignmentPattern.Left as Identifier ?? identifier;
-                    if (assignmentPattern.Right is FunctionExpression)
+
+                    if (assignmentPattern.Right.IsFunctionWithName())
                     {
                         ((FunctionInstance) value).SetFunctionName(target.Name);
                     }
-                    
+
                     AssignToIdentifier(engine, target.Name, value);
                 }
                 else if (left.Value is BindingPattern bindingPattern)
@@ -246,6 +249,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             JsValue rval)
         {
             var env = engine.ExecutionContext.LexicalEnvironment;
+
             var strict = StrictModeScope.IsStrictModeCode;
             if (LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
                 env,

+ 31 - 0
Jint/Runtime/Interpreter/Expressions/JintArrowFunctionExpression.cs

@@ -0,0 +1,31 @@
+using Esprima.Ast;
+using Jint.Native.Function;
+using Jint.Runtime.Environments;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintArrowFunctionExpression : JintExpression
+    {
+        private readonly JintFunctionDefinition _function;
+
+        public JintArrowFunctionExpression(Engine engine, IFunction function)
+            : base(engine, ArrowParameterPlaceHolder.Empty)
+        {
+
+            _function = new JintFunctionDefinition(engine, function);
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var funcEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
+
+            var closure = new ArrowFunctionInstance(
+                _engine,
+                _function,
+                funcEnv,
+                _function._strict);
+
+            return closure;
+        }
+    }
+}

+ 13 - 0
Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs

@@ -1,5 +1,6 @@
 using Esprima.Ast;
 using Jint.Native;
+using Jint.Native.Function;
 using Jint.Runtime.Environments;
 using Jint.Runtime.References;
 
@@ -180,6 +181,12 @@ namespace Jint.Runtime.Interpreter.Expressions
                 lref.AssertValid(_engine);
 
                 var rval = _right.GetValue();
+
+                if (_right._expression.IsFunctionWithName())
+                {
+                    ((FunctionInstance) rval).SetFunctionName(lref.GetReferencedName());
+                }
+
                 _engine.PutValue(lref, rval);
                 _engine._referencePool.Return(lref);
                 return rval;
@@ -206,6 +213,12 @@ namespace Jint.Runtime.Interpreter.Expressions
                     }
 
                     var rval = right.GetValue();
+
+                    if (right._expression.IsFunctionWithName())
+                    {
+                        ((FunctionInstance) rval).SetFunctionName(left._expressionName);
+                    }
+
                     environmentRecord.SetMutableBinding(left._expressionName, rval, strict);
                     return rval;
                 }

+ 4 - 2
Jint/Runtime/Interpreter/Expressions/JintExpression.cs

@@ -1,5 +1,4 @@
 using System.Runtime.CompilerServices;
-using System.Collections.Generic;
 using Esprima.Ast;
 using Jint.Native;
 using Jint.Native.Array;
@@ -15,7 +14,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         protected bool _initialized = true;
 
         protected readonly Engine _engine;
-        protected readonly INode _expression;
+        protected internal readonly INode _expression;
 
         protected JintExpression(Engine engine, INode expression)
         {
@@ -63,6 +62,9 @@ namespace Jint.Runtime.Interpreter.Expressions
                 case Nodes.ArrayExpression:
                     return new JintArrayExpression(engine, (ArrayExpression) expression);
 
+                case Nodes.ArrowFunctionExpression:
+                    return new JintArrowFunctionExpression(engine, (IFunction) expression);
+
                 case Nodes.BinaryExpression:
                     return JintBinaryExpression.Build(engine, (BinaryExpression) expression);
 

+ 44 - 34
Jint/Runtime/Interpreter/JintFunctionDefinition.cs

@@ -1,3 +1,5 @@
+using System.Collections.Generic;
+using System.Linq;
 using Esprima;
 using Esprima.Ast;
 using Jint.Runtime.Interpreter.Statements;
@@ -12,6 +14,7 @@ namespace Jint.Runtime.Interpreter
         internal readonly string[] _parameterNames;
         internal readonly JintStatement _body;
         internal bool _hasRestParameter;
+        internal int _length;
 
         public readonly HoistingScope _hoistingScope;
 
@@ -36,54 +39,61 @@ namespace Jint.Runtime.Interpreter
             _body = JintStatement.Build(engine, bodyStatement);
         }
 
-        private string[] GetParameterNames(IFunction functionDeclaration)
+        private IEnumerable<Identifier> GetParameterIdentifiers(INode parameter)
         {
-            var list = functionDeclaration.Params;
-            var count = list.Count;
-
-            if (count == 0)
+            if (parameter is Identifier identifier)
+            {
+                return new [] { identifier };
+            }
+            if (parameter is RestElement restElement)
             {
-                return System.ArrayExt.Empty<string>();
+                _hasRestParameter = true;
+                return GetParameterIdentifiers(restElement.Argument);
+            }
+            if (parameter is ArrayPattern arrayPattern)
+            {
+                return arrayPattern.Elements.SelectMany(GetParameterIdentifiers);
+            }
+            if (parameter is ObjectPattern objectPattern)
+            {
+                return objectPattern.Properties.SelectMany(property => GetParameterIdentifiers(property.Value));
+            }
+            if (parameter is AssignmentPattern assignmentPattern)
+            {
+                return GetParameterIdentifiers(assignmentPattern.Left);
             }
 
-            var names = new string[count];
-            for (var i = 0; i < count; ++i)
+            return Enumerable.Empty<Identifier>();
+        }
+
+        private string[] GetParameterNames(IFunction functionDeclaration)
+        {
+            var parameterNames = new List<string>();
+            var functionDeclarationParams = functionDeclaration.Params;
+            int count = functionDeclarationParams.Count;
+            bool onlyIdentifiers = true;
+            for (var i = 0; i < count; i++)
             {
-                var node = list[i];
-                if (node is Identifier identifier)
-                {
-                    names[i] = identifier.Name;
-                }
-                else if (node is AssignmentPattern ap)
+                var parameter = functionDeclarationParams[i];
+                if (parameter is Identifier id)
                 {
-                    names[i] = ((Identifier) ap.Left).Name;
-                }
-                else if (node is RestElement re)
-                {
-                    if (re.Argument is Identifier id)
-                    {
-                        names[i] = id.Name;
-                    }
-                    else
+                    parameterNames.Add(id.Name);
+                    if (onlyIdentifiers)
                     {
-                        names[i] = "";
+                        _length++;
                     }
-                    _hasRestParameter = true;
-                }
-                else if (node is BindingPattern)
-                {
-                    names[i] = "";
                 }
                 else
                 {
-                    ExceptionHelper.ThrowArgumentOutOfRangeException(
-                        nameof(functionDeclaration),
-                        "Unable to determine how to handle parameter of type " + node.GetType());
+                    onlyIdentifiers = false;
+                    foreach (var identifier in GetParameterIdentifiers(parameter))
+                    {
+                        parameterNames.Add(identifier.Name);
+                    }
                 }
             }
 
-            return names;
+            return parameterNames.ToArray();
         }
-
     }
 }

+ 3 - 4
Jint/Runtime/Interpreter/JintStatementList.cs

@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-using Esprima.Ast;
+using Esprima.Ast;
 using Jint.Native;
 using Jint.Runtime.Interpreter.Statements;
 
@@ -15,12 +14,12 @@ namespace Jint.Runtime.Interpreter
 
         private readonly Engine _engine;
         private readonly Statement _statement;
-        private readonly Esprima.Ast.List<StatementListItem> _statements;
+        private readonly NodeList<IStatementListItem> _statements;
 
         private Pair[] _jintStatements;
         private bool _initialized;
 
-        public JintStatementList(Engine engine, Statement statement, Esprima.Ast.List<StatementListItem> statements)
+        public JintStatementList(Engine engine, Statement statement, NodeList<IStatementListItem> statements)
         {
             _engine = engine;
             _statement = statement;

+ 0 - 1
Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs

@@ -1,4 +1,3 @@
-using Esprima;
 using Esprima.Ast;
 
 namespace Jint.Runtime.Interpreter.Statements

+ 0 - 1
Jint/Runtime/Interpreter/Statements/JintContinueStatement.cs

@@ -1,4 +1,3 @@
-using Esprima;
 using Esprima.Ast;
 
 namespace Jint.Runtime.Interpreter.Statements

+ 1 - 1
Jint/Runtime/Interpreter/Statements/JintStatement.cs

@@ -124,7 +124,7 @@ namespace Jint.Runtime.Interpreter.Statements
             }
         }
 
-        internal static Completion? FastResolve(StatementListItem statement)
+        internal static Completion? FastResolve(IStatementListItem statement)
         {
             if (statement is ReturnStatement rs && rs.Argument is Literal l)
             {

+ 3 - 4
Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs

@@ -1,4 +1,3 @@
-using System.Collections.Generic;
 using Esprima;
 using Esprima.Ast;
 using Jint.Native;
@@ -9,11 +8,11 @@ namespace Jint.Runtime.Interpreter.Statements
     internal sealed class JintSwitchBlock
     {
         private readonly Engine _engine;
-        private readonly Esprima.Ast.List<SwitchCase> _switchBlock;
+        private readonly NodeList<SwitchCase> _switchBlock;
         private JintSwitchCase[] _jintSwitchBlock;
         private bool _initialized;
 
-        public JintSwitchBlock(Engine engine, in Esprima.Ast.List<SwitchCase> switchBlock)
+        public JintSwitchBlock(Engine engine, NodeList<SwitchCase> switchBlock)
         {
             _engine = engine;
             _switchBlock = switchBlock;
@@ -102,4 +101,4 @@ namespace Jint.Runtime.Interpreter.Statements
             }
         }
     }
-}
+}

+ 1 - 1
Jint/Runtime/Interpreter/Statements/JintSwitchStatement.cs

@@ -13,7 +13,7 @@ namespace Jint.Runtime.Interpreter.Statements
 
         public JintSwitchStatement(Engine engine, SwitchStatement statement) : base(engine, statement)
         {
-            _switchBlock = new JintSwitchBlock(engine, in _statement.Cases);
+            _switchBlock = new JintSwitchBlock(engine, _statement.Cases);
             _discriminant = JintExpression.Build(engine, _statement.Discriminant);
         }
 

+ 7 - 0
Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs

@@ -1,5 +1,6 @@
 using Esprima.Ast;
 using Jint.Native;
+using Jint.Native.Function;
 using Jint.Runtime.Interpreter.Expressions;
 using Jint.Runtime.References;
 
@@ -88,6 +89,12 @@ namespace Jint.Runtime.Interpreter.Statements
                         lhs.AssertValid(_engine);
 
                         var value = declaration.Init.GetValue();
+
+                        if (declaration.Init._expression.IsFunctionWithName())
+                        {
+                            ((FunctionInstance) value).SetFunctionName(lhs.GetReferencedName());
+                        }
+
                         _engine.PutValue(lhs, value);
                         _engine._referencePool.Return(lhs);
                     }

+ 1 - 1
README.md

@@ -152,7 +152,7 @@ This example is using French as the default culture.
 ### ECMAScript 6.0
 
 ES6 features which are being implemented:
-- [ ] [arrows](https://github.com/lukehoban/es6features/blob/master/README.md#arrows)
+- [x] [arrows](https://github.com/lukehoban/es6features/blob/master/README.md#arrows)
 - [ ] [classes](https://github.com/lukehoban/es6features/blob/master/README.md#classes)
 - [ ] [enhanced object literals](https://github.com/lukehoban/es6features/blob/master/README.md#enhanced-object-literals)
 - [x] [template strings](https://github.com/lukehoban/es6features/blob/master/README.md#template-strings)