Ver código fonte

Support let and const (#722)

Marko Lahma 5 anos atrás
pai
commit
284411b25a
95 arquivos alterados com 3226 adições e 1610 exclusões
  1. 1 1
      Jint.Benchmark/Jint.Benchmark.csproj
  2. 1 1
      Jint.Benchmark/UncacheableExpressionsBenchmark.cs
  3. 2 2
      Jint.Tests.Ecma/TestCases/alltests.json
  4. 15 0
      Jint.Tests.Test262/Language/BlockScopeTests.cs
  5. 15 0
      Jint.Tests.Test262/Language/FunctionCodeTests.cs
  6. 15 0
      Jint.Tests.Test262/Language/GlobalCodeTests.cs
  7. 15 0
      Jint.Tests.Test262/Language/Statements/ConstTests.cs
  8. 15 0
      Jint.Tests.Test262/Language/Statements/LetTests.cs
  9. 20 8
      Jint.Tests.Test262/Test262Test.cs
  10. 63 0
      Jint.Tests.Test262/test/skipped.json
  11. 6 6
      Jint/Collections/HybridDictionary.cs
  12. 7 7
      Jint/Collections/ListDictionary.cs
  13. 6 6
      Jint/Collections/StringDictionarySlim.cs
  14. 474 123
      Jint/Engine.cs
  15. 78 6
      Jint/EsprimaExtensions.cs
  16. 0 62
      Jint/EvalCodeScope.cs
  17. 200 0
      Jint/HoistingScope.cs
  18. 1 1
      Jint/Jint.csproj
  19. 42 40
      Jint/Native/Argument/ArgumentsInstance.cs
  20. 1 1
      Jint/Native/Array/ArrayConstructor.cs
  21. 1 1
      Jint/Native/Boolean/BooleanConstructor.cs
  22. 1 1
      Jint/Native/Date/DateConstructor.cs
  23. 1 1
      Jint/Native/Error/ErrorConstructor.cs
  24. 10 19
      Jint/Native/Function/ArrowFunctionInstance.cs
  25. 3 1
      Jint/Native/Function/BindFunctionInstance.cs
  26. 64 64
      Jint/Native/Function/EvalFunctionInstance.cs
  27. 5 6
      Jint/Native/Function/FunctionConstructor.cs
  28. 21 28
      Jint/Native/Function/FunctionInstance.cs
  29. 1 1
      Jint/Native/Function/FunctionPrototype.cs
  30. 0 17
      Jint/Native/Function/FunctionShim.cs
  31. 47 36
      Jint/Native/Function/ScriptFunctionInstance.cs
  32. 2 1
      Jint/Native/Function/ThrowTypeError.cs
  33. 8 8
      Jint/Native/Global/GlobalObject.cs
  34. 1 1
      Jint/Native/Iterator/IteratorConstructor.cs
  35. 4 3
      Jint/Native/Iterator/IteratorInstance.cs
  36. 1 1
      Jint/Native/JsValue.cs
  37. 4 4
      Jint/Native/Json/JsonParser.cs
  38. 1 1
      Jint/Native/Map/MapConstructor.cs
  39. 1 1
      Jint/Native/Number/NumberConstructor.cs
  40. 3 1
      Jint/Native/Object/ObjectConstructor.cs
  41. 7 7
      Jint/Native/Object/ObjectInstance.cs
  42. 1 1
      Jint/Native/Proxy/ProxyConstructor.cs
  43. 1 1
      Jint/Native/Proxy/ProxyInstance.cs
  44. 2 2
      Jint/Native/RegExp/RegExpConstructor.cs
  45. 2 2
      Jint/Native/RegExp/RegExpPrototype.cs
  46. 1 1
      Jint/Native/Set/SetConstructor.cs
  47. 3 3
      Jint/Native/String/StringConstructor.cs
  48. 1 1
      Jint/Native/Symbol/SymbolConstructor.cs
  49. 8 6
      Jint/Pooling/ArgumentsInstancePool.cs
  50. 4 4
      Jint/Runtime/Debugger/DebugHandler.cs
  51. 5 5
      Jint/Runtime/Descriptors/Specialized/ClrAccessDescriptor.cs
  52. 8 2
      Jint/Runtime/Environments/Binding.cs
  53. 80 362
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  54. 42 4
      Jint/Runtime/Environments/EnvironmentRecord.cs
  55. 11 8
      Jint/Runtime/Environments/ExecutionContext.cs
  56. 430 0
      Jint/Runtime/Environments/FunctionEnvironmentRecord.cs
  57. 192 34
      Jint/Runtime/Environments/GlobalEnvironmentRecord.cs
  58. 14 6
      Jint/Runtime/Environments/LexicalEnvironment.cs
  59. 63 13
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  60. 0 5
      Jint/Runtime/ExceptionHelper.cs
  61. 1 1
      Jint/Runtime/Interop/ClrFunctionInstance.cs
  62. 2 1
      Jint/Runtime/Interop/DelegateWrapper.cs
  63. 2 1
      Jint/Runtime/Interop/GetterFunctionInstance.cs
  64. 2 1
      Jint/Runtime/Interop/MethodInfoFunctionInstance.cs
  65. 2 1
      Jint/Runtime/Interop/SetterFunctionInstance.cs
  66. 1 1
      Jint/Runtime/Interop/TypeReference.cs
  67. 54 45
      Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs
  68. 1 2
      Jint/Runtime/Interpreter/Expressions/JintArrowFunctionExpression.cs
  69. 20 33
      Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs
  70. 2 2
      Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs
  71. 1 1
      Jint/Runtime/Interpreter/Expressions/JintConstantExpression.cs
  72. 32 77
      Jint/Runtime/Interpreter/Expressions/JintExpression.cs
  73. 8 4
      Jint/Runtime/Interpreter/Expressions/JintFunctionExpression.cs
  74. 8 12
      Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs
  75. 2 2
      Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs
  76. 1 1
      Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs
  77. 2 2
      Jint/Runtime/Interpreter/Expressions/JintThisExpression.cs
  78. 14 8
      Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs
  79. 297 59
      Jint/Runtime/Interpreter/JintFunctionDefinition.cs
  80. 48 7
      Jint/Runtime/Interpreter/JintStatementList.cs
  81. 31 5
      Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs
  82. 368 0
      Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs
  83. 0 110
      Jint/Runtime/Interpreter/Statements/JintForInStatement.cs
  84. 0 140
      Jint/Runtime/Interpreter/Statements/JintForOfStatement.cs
  85. 122 23
      Jint/Runtime/Interpreter/Statements/JintForStatement.cs
  86. 31 72
      Jint/Runtime/Interpreter/Statements/JintStatement.cs
  87. 34 0
      Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs
  88. 9 4
      Jint/Runtime/Interpreter/Statements/JintSwitchStatement.cs
  89. 3 2
      Jint/Runtime/Interpreter/Statements/JintTryStatement.cs
  90. 49 21
      Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs
  91. 1 1
      Jint/Runtime/Interpreter/Statements/JintWithStatement.cs
  92. 2 3
      Jint/Runtime/KnownKeys.cs
  93. 5 0
      Jint/Runtime/RefStack.cs
  94. 16 1
      Jint/Runtime/References/Reference.cs
  95. 24 40
      Jint/Runtime/TypeConverter.cs

+ 1 - 1
Jint.Benchmark/Jint.Benchmark.csproj

@@ -14,7 +14,7 @@
     <GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
     <AssemblyOriginatorKeyFile>..\Jint\Jint.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
-    <LangVersion>latest</LangVersion>
+    <LangVersion>8</LangVersion>
   </PropertyGroup>
   <ItemGroup>
     <None Include=".\Scripts\**" CopyToOutputDirectory="PreserveNewest" />

+ 1 - 1
Jint.Benchmark/UncacheableExpressionsBenchmark.cs

@@ -30,7 +30,7 @@ function output(d) {
 
         private const string ArrowFunctionScript = @"
 function output(d) {
-    var doc = d.SubDocuments.find(x => x.Id==='testing');
+    let doc = d.SubDocuments.find(x => x.Id==='testing');
     return { Id : d.Id, Deleted : d.Deleted, SubTestId : (doc!==null&&doc!==undefined)?doc.Id:null, Values : d.SubDocuments.map(x => ({ TargetId:x.TargetId,TargetValue:x.TargetValue,SubDocuments:x.SubDocuments.filter(s => (s!==null&&s!==undefined)).map(s => ({ TargetId: s.TargetId, TargetValue: s.TargetValue}))})) };
 }
 ";

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

@@ -16340,8 +16340,8 @@
     source: "ch13/13.0/S13_A12_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "no longer true with new FunctionDeclarationInstantiation",
     source: "ch13/13.0/S13_A12_T2.js"
   },
   {

+ 15 - 0
Jint.Tests.Test262/Language/BlockScopeTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262.Language
+{
+    public class BlockScopeTests : Test262Test
+    {
+        [Theory(DisplayName = "language\\block-scope")]
+        [MemberData(nameof(SourceFiles), "language\\block-scope", false)]
+        [MemberData(nameof(SourceFiles), "language\\block-scope", true, Skip = "Skipped")]
+        protected void BlockScope(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 15 - 0
Jint.Tests.Test262/Language/FunctionCodeTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262.Language
+{
+    public class FunctionCodeTests : Test262Test
+    {
+        [Theory(DisplayName = "language\\function-code")]
+        [MemberData(nameof(SourceFiles), "language\\function-code", false)]
+        [MemberData(nameof(SourceFiles), "language\\function-code", true, Skip = "Skipped")]
+        protected void FunctionCode(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 15 - 0
Jint.Tests.Test262/Language/GlobalCodeTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262.Language
+{
+    public class GlobalCodeTests : Test262Test
+    {
+        [Theory(DisplayName = "language\\global-code")]
+        [MemberData(nameof(SourceFiles), "language\\global-code", false)]
+        [MemberData(nameof(SourceFiles), "language\\global-code", true, Skip = "Skipped")]
+        protected void GlobalCode(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 15 - 0
Jint.Tests.Test262/Language/Statements/ConstTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262.Language.Statements
+{
+    public class ConstTests : Test262Test
+    {
+        [Theory(DisplayName = "language\\statements\\const")]
+        [MemberData(nameof(SourceFiles), "language\\statements\\const", false)]
+        [MemberData(nameof(SourceFiles), "language\\statements\\const", true, Skip = "Skipped")]
+        protected void For(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 15 - 0
Jint.Tests.Test262/Language/Statements/LetTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262.Language.Statements
+{
+    public class LetTests : Test262Test
+    {
+        [Theory(DisplayName = "language\\statements\\let")]
+        [MemberData(nameof(SourceFiles), "language\\statements\\let", false)]
+        [MemberData(nameof(SourceFiles), "language\\statements\\let", true, Skip = "Skipped")]
+        protected void For(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 20 - 8
Jint.Tests.Test262/Test262Test.cs

@@ -5,7 +5,9 @@ using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Text.RegularExpressions;
+using Esprima;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 using Newtonsoft.Json.Linq;
 using Xunit;
@@ -82,6 +84,24 @@ namespace Jint.Tests.Test262
             engine.Execute(Sources["sta.js"]);
             engine.Execute(Sources["assert.js"]);
             engine.SetValue("print", new ClrFunctionInstance(engine, "print", (thisObj, args) => TypeConverter.ToString(args.At(0))));
+
+            var o = engine.Object.Construct(Arguments.Empty);
+            o.FastSetProperty("evalScript", new PropertyDescriptor(new ClrFunctionInstance(engine, "evalScript", (thisObj, args) =>
+            {
+                if (args.Length > 1)
+                {
+                    throw new Exception("only script parsing supported");
+                }
+
+                var options = new ParserOptions { AdaptRegexp = true, Tolerant = false };
+                var parser = new JavaScriptParser(args.At(0).AsString(), options);
+                var script = parser.ParseScript(strict);
+
+                var value = engine.Execute(script).GetCompletionValue();
+                
+                return value;
+            }), true, true, true));
+            engine.SetValue("$262", o);
             
             var includes = Regex.Match(code, @"includes: \[(.+?)\]");
             if (includes.Success)
@@ -195,10 +215,6 @@ namespace Jint.Tests.Test262
                                 skip = true;
                                 reason = "class keyword not implemented";
                                 break;
-                            case "const":
-                                skip = true;
-                                reason = "const keyword not implemented";
-                                break;
                             case "BigInt":
                                 skip = true;
                                 reason = "BigInt not implemented";
@@ -207,10 +223,6 @@ namespace Jint.Tests.Test262
                                 skip = true;
                                 reason = "generators not implemented";
                                 break;
-                            case "let":
-                                skip = true;
-                                reason = "let not implemented";
-                                break;
                             case "async-functions":
                                 skip = true;
                                 reason = "async-functions not implemented";

+ 63 - 0
Jint.Tests.Test262/test/skipped.json

@@ -33,6 +33,25 @@
   },
 
 
+  // http://www.ecma-international.org/ecma-262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics
+  {
+    "source": "language/statements/let/block-local-closure-set-before-initialization.js",
+    "reason": "not implemented (block level functions)"
+  },
+  {
+    "source": "language/block-scope/shadowing/dynamic-lookup-from-closure.js",
+    "reason": "not implemented (block level functions)"
+  },
+  {
+    "source": "language/block-scope/shadowing/lookup-from-closure.js",
+    "reason": "not implemented (block level functions)"
+  },
+
+  {
+    "source": "language/statements/let/block-local-closure-set-before-initialization.js",
+    "reason": "http://www.ecma-international.org/ecma-262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics not implemented (block level functions)"
+  },
+
   {
     "source": "built-ins/RegExp/S15.10.2.11_A1_T5.js",
     "reason": "Logic difference in .NET RegExp / skipped in ECMA tests too"
@@ -669,6 +688,46 @@
 
 
   // class support
+  {
+    "source": "language/global-code/script-decl-lex.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/global-code/script-decl-lex-deletion.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/global-code/script-decl-var-collision.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/global-code/script-decl-lex-lex.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/global-code/decl-lex.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/let/dstr-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/global-code/decl-lex-deletion.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/let/dstr-obj-ptrn-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/const/dstr-ary-ptrn-elem-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "language/statements/const/dstr-obj-ptrn-id-init-fn-name-class.js",
+    "reason": "class not implemented"
+  },
   {
     "source": "language/expressions/arrow-function/dstr-dflt-ary-ptrn-elem-id-init-fn-name-class.js",
     "reason": "class not implemented"
@@ -888,6 +947,10 @@
 
   // Esprima problems
 
+  {
+    "source": "language/statements/for-of/dstr-obj-id-init-let.js",
+    "reason": "Esprima problem"
+  },
   {
     "source": "language/statements/for/head-lhs-let.js",
     "reason": "Esprima problem"

+ 6 - 6
Jint/Collections/HybridDictionary.cs

@@ -28,7 +28,7 @@ namespace Jint.Collections
             }
         }
 
-        public TValue this[in Key key]
+        public TValue this[Key key]
         {
             get
             {
@@ -59,7 +59,7 @@ namespace Jint.Collections
             }
         }
 
-        public bool TryGetValue(in Key key, out TValue value)
+        public bool TryGetValue(Key key, out TValue value)
         {
             if (_dictionary != null)
             {
@@ -75,7 +75,7 @@ namespace Jint.Collections
             return false;
         }
 
-        private void SwitchToDictionary(in Key key, TValue value)
+        private void SwitchToDictionary(Key key, TValue value)
         {
             var dictionary = new StringDictionarySlim<TValue>(InitialDictionarySize);
             foreach (var pair in _list)
@@ -94,7 +94,7 @@ namespace Jint.Collections
             get => _dictionary?.Count ?? _list?.Count ?? 0;
         }
 
-        public void Add(in Key key, TValue value)
+        public void Add(Key key, TValue value)
         {
             if (_dictionary != null)
             {
@@ -137,7 +137,7 @@ namespace Jint.Collections
             }
         }
 
-        public bool ContainsKey(in Key key)
+        public bool ContainsKey(Key key)
         {
             if (_dictionary != null)
             {
@@ -184,7 +184,7 @@ namespace Jint.Collections
             return Enumerable.Empty<KeyValuePair<Key, TValue>>().GetEnumerator();
         }
 
-        public bool Remove(in Key key)
+        public bool Remove(Key key)
         {
             if (_dictionary != null)
             {

+ 7 - 7
Jint/Collections/ListDictionary.cs

@@ -11,7 +11,7 @@ namespace Jint.Collections
         private int _count;
         private bool _checkExistingKeys;
 
-        public ListDictionary(in Key key, TValue value, bool checkExistingKeys)
+        public ListDictionary(Key key, TValue value, bool checkExistingKeys)
         {
             _checkExistingKeys = checkExistingKeys;
             _head = new DictionaryNode
@@ -22,7 +22,7 @@ namespace Jint.Collections
             _count = 1;
         }
 
-        public TValue this[in Key key]
+        public TValue this[Key key]
         {
             get
             {
@@ -56,7 +56,7 @@ namespace Jint.Collections
             }
         }
 
-        public bool TryGetValue(in Key key, out TValue value)
+        public bool TryGetValue(Key key, out TValue value)
         {
             var node = _head;
             while (node != null)
@@ -80,7 +80,7 @@ namespace Jint.Collections
             get => _count;
         }
 
-        public void Add(in Key key, TValue value)
+        public void Add(Key key, TValue value)
         {
             DictionaryNode last = null;
             DictionaryNode node;
@@ -99,7 +99,7 @@ namespace Jint.Collections
             AddNode(key, value, last);
         }
 
-        private void AddNode(in Key key, TValue value, DictionaryNode last)
+        private void AddNode(Key key, TValue value, DictionaryNode last)
         {
             var newNode = new DictionaryNode
             {
@@ -123,7 +123,7 @@ namespace Jint.Collections
             _head = null;
         }
 
-        public bool ContainsKey(in Key key)
+        public bool ContainsKey(Key key)
         {
             for (var node = _head; node != null; node = node.Next)
             {
@@ -157,7 +157,7 @@ namespace Jint.Collections
             return new NodeEnumerator(this);
         }
 
-        public bool Remove(in Key key)
+        public bool Remove(Key key)
         {
             DictionaryNode last = null;
             DictionaryNode node;

+ 6 - 6
Jint/Collections/StringDictionarySlim.cs

@@ -76,7 +76,7 @@ namespace Jint.Collections
             _entries = InitialEntries;
         }
 
-        public bool ContainsKey(in Key key)
+        public bool ContainsKey(Key key)
         {
             Entry[] entries = _entries;
             for (int i = _buckets[key.HashCode & (_buckets.Length-1)] - 1;
@@ -89,7 +89,7 @@ namespace Jint.Collections
             return false;
         }
 
-        public bool TryGetValue(in Key key, out TValue value)
+        public bool TryGetValue(Key key, out TValue value)
         {
             Entry[] entries = _entries;
             for (int i = _buckets[key.HashCode & (_buckets.Length - 1)] - 1;
@@ -106,7 +106,7 @@ namespace Jint.Collections
             return false;
         }
 
-        public bool Remove(in Key key)
+        public bool Remove(Key key)
         {
             Entry[] entries = _entries;
             int bucketIndex = key.HashCode & (_buckets.Length - 1);
@@ -151,7 +151,7 @@ namespace Jint.Collections
         /// </summary>
         /// <param name="key">Key to look for</param>
         /// <returns>Reference to the new or existing value</returns>
-        public ref TValue GetOrAddValueRef(in Key key)
+        public ref TValue GetOrAddValueRef(Key key)
         {
             Entry[] entries = _entries;
             int bucketIndex = key.HashCode & (_buckets.Length - 1);
@@ -165,14 +165,14 @@ namespace Jint.Collections
             return ref AddKey(key, bucketIndex);
         }
 
-        public ref TValue this[in Key key]
+        public ref TValue this[Key key]
         {
             [MethodImpl(MethodImplOptions.AggressiveInlining)]
             get => ref GetOrAddValueRef(key);
         }
 
         [MethodImpl(MethodImplOptions.NoInlining)]
-        private ref TValue AddKey(in Key key, int bucketIndex)
+        private ref TValue AddKey(Key key, int bucketIndex)
         {
             Entry[] entries = _entries;
             int entryIndex;

+ 474 - 123
Jint/Engine.cs

@@ -33,7 +33,6 @@ using Jint.Runtime.Environments;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interpreter;
 using Jint.Runtime.References;
-using ExecutionContext = Jint.Runtime.Environments.ExecutionContext;
 
 namespace Jint
 {
@@ -57,7 +56,7 @@ namespace Jint
 
         private readonly ExecutionContextStack _executionContexts;
         private JsValue _completionValue = JsValue.Undefined;
-        internal INode _lastSyntaxNode;
+        internal Node _lastSyntaxNode;
 
         // lazy properties
         private ErrorConstructor _error;
@@ -110,7 +109,7 @@ namespace Jint
 
         internal readonly struct ClrPropertyDescriptorFactoriesKey : IEquatable<ClrPropertyDescriptorFactoriesKey>
         {
-            public ClrPropertyDescriptorFactoriesKey(Type type, in Key propertyName)
+            public ClrPropertyDescriptorFactoriesKey(Type type, Key propertyName)
             {
                 Type = type;
                 PropertyName = propertyName;
@@ -192,7 +191,7 @@ namespace Jint
             GlobalEnvironment = LexicalEnvironment.NewGlobalEnvironment(this, Global);
 
             // create the global execution context http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.1.1
-            EnterExecutionContext(GlobalEnvironment, GlobalEnvironment, Global);
+            EnterExecutionContext(GlobalEnvironment, GlobalEnvironment);
 
             Options = new Options();
 
@@ -208,7 +207,7 @@ namespace Jint
             _argumentsInstancePool = new ArgumentsInstancePool(this);
             _jsValueArrayPool = new JsValueArrayPool();
 
-            Eval = new EvalFunctionInstance(this, System.Array.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
+            Eval = new EvalFunctionInstance(this);
             Global.SetProperty(CommonProperties.Eval, new PropertyDescriptor(Eval, PropertyFlag.Configurable | PropertyFlag.Writable));
 
             if (Options._IsClrAllowed)
@@ -269,9 +268,9 @@ namespace Jint
         public event DebugStepDelegate Step;
         public event BreakDelegate Break;
 
-        internal DebugHandler DebugHandler => _debugHandler ?? (_debugHandler = new DebugHandler(this));
+        internal DebugHandler DebugHandler => _debugHandler ??= new DebugHandler(this);
 
-        public List<BreakPoint> BreakPoints => _breakPoints ?? (_breakPoints = new List<BreakPoint>());
+        public List<BreakPoint> BreakPoints => _breakPoints ??= new List<BreakPoint>();
 
         internal StepMode? InvokeStepEvent(DebugInformation info)
         {
@@ -284,17 +283,16 @@ namespace Jint
         }
         #endregion
 
-        public void EnterExecutionContext(
+        public ExecutionContext EnterExecutionContext(
             LexicalEnvironment lexicalEnvironment,
-            LexicalEnvironment variableEnvironment,
-            JsValue thisBinding)
+            LexicalEnvironment variableEnvironment)
         {
             var context = new ExecutionContext(
                 lexicalEnvironment,
-                variableEnvironment,
-                thisBinding);
+                variableEnvironment);
 
             _executionContexts.Push(context);
+            return context;
         }
 
         public Engine SetValue(JsValue name, Delegate value)
@@ -377,13 +375,12 @@ namespace Jint
 
             using (new StrictModeScope(_isStrict || program.Strict))
             {
-                DeclarationBindingInstantiation(
-                    DeclarationBindingType.GlobalCode,
-                    program.HoistingScope,
-                    functionInstance: null,
-                    arguments: null);
+                GlobalDeclarationInstantiation(
+                    program,
+                    GlobalEnvironment);
 
                 var list = new JintStatementList(this, null, program.Body);
+                
                 var result = list.Execute();
                 if (result.Type == CompletionType.Throw)
                 {
@@ -572,11 +569,10 @@ namespace Jint
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-putvalue
+        /// https://tc39.es/ecma262/#sec-putvalue
         /// </summary>
         internal void PutValue(Reference reference, JsValue value)
         {
-            var property = reference.GetReferencedName();
             var baseValue = reference.GetBase();
             if (reference.IsUnresolvableReference())
             {
@@ -585,7 +581,7 @@ namespace Jint
                     ExceptionHelper.ThrowReferenceError(this, reference);
                 }
 
-                Global.Set(property, value);
+                Global.Set(reference.GetReferencedName(), value, throwOnError: false);
             }
             else if (reference.IsPropertyReference())
             {
@@ -594,8 +590,7 @@ namespace Jint
                     baseValue = TypeConverter.ToObject(this, baseValue);
                 }
 
-                var thisValue = GetThisValue(reference);
-                var succeeded = baseValue.Set(property, value, thisValue);
+                var succeeded = baseValue.Set(reference.GetReferencedName(), value, reference.GetThisValue());
                 if (!succeeded && reference.IsStrictReference())
                 {
                     ExceptionHelper.ThrowTypeError(this);
@@ -603,18 +598,17 @@ namespace Jint
             }
             else
             {
-                ((EnvironmentRecord) baseValue).SetMutableBinding(property.ToString(), value, reference.IsStrictReference());
+                ((EnvironmentRecord) baseValue).SetMutableBinding(TypeConverter.ToString(reference.GetReferencedName()), value, reference.IsStrictReference());
             }
         }
-
-        private static JsValue GetThisValue(Reference reference)
+        
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/6.0/#sec-initializereferencedbinding
+        /// </summary>
+        public void InitializeReferenceBinding(Reference reference, JsValue value)
         {
-            if (reference.IsSuperReference())
-            {
-                return ExceptionHelper.ThrowNotImplementedException<JsValue>();
-            }
-
-            return reference.GetBase();
+            var baseValue = (EnvironmentRecord) reference.GetBase();
+            baseValue.InitializeBinding(TypeConverter.ToString(reference.GetReferencedName()), value);
         }
 
         /// <summary>
@@ -688,7 +682,7 @@ namespace Jint
         /// <summary>
         /// Gets the last evaluated <see cref="INode"/>.
         /// </summary>
-        public INode GetLastSyntaxNode()
+        public Node GetLastSyntaxNode()
         {
             return _lastSyntaxNode;
         }
@@ -705,149 +699,508 @@ namespace Jint
             _referencePool.Return(reference);
             return jsValue;
         }
+        
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-resolvebinding
+        /// </summary>
+        internal Reference ResolveBinding(string name, LexicalEnvironment env = null)
+        {
+            env ??= ExecutionContext.LexicalEnvironment;
+            return GetIdentifierReference(env, name, StrictModeScope.IsStrictModeCode);
+        }
 
-        //  http://www.ecma-international.org/ecma-262/5.1/#sec-10.5
-        internal ArgumentsInstance DeclarationBindingInstantiation(
-            DeclarationBindingType declarationBindingType,
-            HoistingScope hoistingScope,
-            FunctionInstance functionInstance,
-            JsValue[] arguments)
+        private Reference GetIdentifierReference(LexicalEnvironment lex, string name, in bool strict)
         {
-            var env = ExecutionContext.VariableEnvironment._record;
-            bool configurableBindings = declarationBindingType == DeclarationBindingType.EvalCode;
-            var strict = StrictModeScope.IsStrictModeCode;
-            ArgumentsInstance argsObj = null;
+            if (lex is null)
+            {
+                return new Reference(JsValue.Undefined, name, strict);
+            }
 
-            var der = env as DeclarativeEnvironmentRecord;
-            if (declarationBindingType == DeclarationBindingType.FunctionCode)
+            var envRec = lex._record;
+            if (envRec.HasBinding(name))
             {
-                // arrow functions don't needs arguments
-                var arrowFunctionInstance = functionInstance as ArrowFunctionInstance;
-                argsObj = arrowFunctionInstance is null
-                    ? _argumentsInstancePool.Rent(functionInstance, functionInstance._formalParameters, arguments, env, strict)
-                    : null;
+                return new Reference(envRec, name, strict);
+            }
 
-                var functionDeclaration = (functionInstance as ScriptFunctionInstance)?.FunctionDeclaration ??
-                                          arrowFunctionInstance?.FunctionDeclaration;
+            return GetIdentifierReference(lex._outer, name, strict);
+        }
 
-                if (!ReferenceEquals(der, null))
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-getthisenvironment
+        /// </summary>
+        internal EnvironmentRecord GetThisEnvironment()
+        {
+            // The loop will always terminate because the list of environments always
+            // ends with the global environment which has a this binding.
+            var lex = ExecutionContext.LexicalEnvironment;
+            while (true)
+            {
+                var envRec = lex._record;
+                var exists = envRec.HasThisBinding();
+                if (exists)
                 {
-                    der.AddFunctionParameters(arguments, argsObj, functionDeclaration);
+                    return envRec;
                 }
-                else
+
+                var outer = lex._outer;
+                lex = outer;
+            }
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-resolvethisbinding
+        /// </summary>
+        internal JsValue ResolveThisBinding()
+        {
+            var envRec = GetThisEnvironment();
+            return envRec.GetThisBinding();
+        }
+        
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
+        /// </summary>
+        private void GlobalDeclarationInstantiation(
+            Script script,
+            LexicalEnvironment env)
+        {
+            var envRec = (GlobalEnvironmentRecord) env._record;
+
+            var hoistingScope = HoistingScope.GetProgramLevelDeclarations(script);
+            var functionDeclarations = hoistingScope._functionDeclarations;
+            var varDeclarations = hoistingScope._variablesDeclarations;
+            var lexDeclarations = hoistingScope._lexicalDeclarations;
+            
+            var functionToInitialize = new LinkedList<FunctionDeclaration>();
+            var declaredFunctionNames = new HashSet<string>();
+            var declaredVarNames = new List<string>();
+
+            if (functionDeclarations != null)
+            {
+                for (var i = functionDeclarations.Count - 1; i >= 0; i--)
                 {
-                    // TODO: match functionality with DeclarationEnvironmentRecord.AddFunctionParameters here
-                    // slow path
-                    var parameters = functionInstance._formalParameters;
-                    for (uint i = 0; i < (uint) parameters.Length; i++)
+                    var d = functionDeclarations[i];
+                    var fn = d.Id.Name;
+                    if (!declaredFunctionNames.Contains(fn))
                     {
-                        var argName = parameters[i];
-                        var v = i + 1 > arguments.Length ? Undefined.Instance : arguments[i];
-                        v = DeclarativeEnvironmentRecord.HandleAssignmentPatternIfNeeded(functionDeclaration, v, i);
+                        var fnDefinable = envRec.CanDeclareGlobalFunction(fn);
+                        if (!fnDefinable)
+                        {
+                            ExceptionHelper.ThrowTypeError(this);
+                        }
 
-                        var argAlreadyDeclared = env.HasBinding(argName);
-                        if (!argAlreadyDeclared)
+                        declaredFunctionNames.Add(fn);
+                        functionToInitialize.AddFirst(d);
+                    }
+                }
+            }
+
+            var boundNames = new List<string>();
+            if (varDeclarations != null)
+            {
+                for (var i = 0; i < varDeclarations.Count; i++)
+                {
+                    var d = varDeclarations[i];
+                    boundNames.Clear();
+                    d.GetBoundNames(boundNames);
+                    for (var j = 0; j < boundNames.Count; j++)
+                    {
+                        var vn = boundNames[j];
+                        if (!declaredFunctionNames.Contains(vn))
                         {
-                            env.CreateMutableBinding(argName, v);
+                            var vnDefinable = envRec.CanDeclareGlobalVar(vn);
+                            if (!vnDefinable)
+                            {
+                                ExceptionHelper.ThrowTypeError(this);
+                            }
+
+                            declaredVarNames.Add(vn);
                         }
+                    }
+                }
+            }
 
-                        env.SetMutableBinding(argName, v, strict);
+            if (lexDeclarations != null)
+            {
+                for (var i = 0; i < lexDeclarations.Count; i++)
+                {
+                    var d = lexDeclarations[i];
+                    boundNames.Clear();
+                    d.GetBoundNames(boundNames);
+                    for (var j = 0; j < boundNames.Count; j++)
+                    {
+                        var dn = boundNames[j];
+                        if (envRec.HasVarDeclaration(dn) 
+                            || envRec.HasLexicalDeclaration(dn)
+                            || envRec.HasRestrictedGlobalProperty(dn))
+                        {
+                            ExceptionHelper.ThrowSyntaxError(this, $"Identifier '{dn}' has already been declared");
+                        }
+                        
+                        if (d.Kind == VariableDeclarationKind.Const)
+                        {
+                            envRec.CreateImmutableBinding(dn, strict: true);
+                        }
+                        else
+                        {
+                            envRec.CreateMutableBinding(dn, canBeDeleted: false);
+                        }
                     }
-                    env.CreateMutableBinding("arguments", argsObj);
                 }
             }
 
-            var functionDeclarations = hoistingScope.FunctionDeclarations;
-            if (functionDeclarations.Count > 0)
+            foreach (var f in functionToInitialize)
             {
-                AddFunctionDeclarations(ref functionDeclarations, env, configurableBindings, strict);
+                var fn = f.Id.Name;
+                var fo = Function.CreateFunctionObject(f, env);
+                envRec.CreateGlobalFunctionBinding(fn, fo, canBeDeleted: false);
             }
 
-            var variableDeclarations = hoistingScope.VariableDeclarations;
-            if (variableDeclarations.Count == 0)
+            for (var i = 0; i < declaredVarNames.Count; i++)
+            {
+                var vn = declaredVarNames[i];
+                envRec.CreateGlobalVarBinding(vn, canBeDeleted: false);
+            }
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-functiondeclarationinstantiation
+        /// </summary>
+        internal ArgumentsInstance FunctionDeclarationInstantiation(
+            FunctionInstance functionInstance,
+            JsValue[] argumentsList,
+            LexicalEnvironment env)
+        {
+            var func = functionInstance._functionDefinition;
+
+            var envRec = (FunctionEnvironmentRecord) env._record;
+            var strict = StrictModeScope.IsStrictModeCode;
+
+            var configuration = func.Initialize(this, functionInstance);
+            var parameterNames = configuration.ParameterNames;
+            var hasDuplicates = configuration.HasDuplicates;
+            var simpleParameterList = configuration.IsSimpleParameterList;
+            var hasParameterExpressions = configuration.HasParameterExpressions;
+
+            var canInitializeParametersOnDeclaration = simpleParameterList && !configuration.HasDuplicates;
+            envRec.InitializeParameters(parameterNames, hasDuplicates, canInitializeParametersOnDeclaration ? argumentsList : null);
+
+            ArgumentsInstance ao = null;
+            if (configuration.ArgumentsObjectNeeded)
             {
-                return argsObj;
+                if (strict || !simpleParameterList)
+                {
+                    ao = CreateUnmappedArgumentsObject(argumentsList);
+                }
+                else
+                {
+                    // NOTE: mapped argument object is only provided for non-strict functions that don't have a rest parameter,
+                    // any parameter default value initializers, or any destructured parameters.
+                    ao = CreateMappedArgumentsObject(functionInstance, parameterNames, argumentsList, envRec, configuration.HasRestParameter);
+                }
+
+                if (strict)
+                {
+                    envRec.CreateImmutableBindingAndInitialize(KnownKeys.Arguments, strict: false, ao);
+                }
+                else
+                {
+                    envRec.CreateMutableBindingAndInitialize(KnownKeys.Arguments, canBeDeleted: false, ao);
+                }
             }
 
-            // process all variable declarations in the current parser scope
-            if (!ReferenceEquals(der, null))
+            if (!canInitializeParametersOnDeclaration)
             {
-                der.AddVariableDeclarations(ref variableDeclarations);
+                // slower set
+                envRec.AddFunctionParameters(func.Function, argumentsList);
+            }
+            
+            // Let iteratorRecord be CreateListIteratorRecord(argumentsList).
+            // If hasDuplicates is true, then
+            //     Perform ? IteratorBindingInitialization for formals with iteratorRecord and undefined as arguments.
+            // Else,
+            //     Perform ? IteratorBindingInitialization for formals with iteratorRecord and env as arguments.
+
+            LexicalEnvironment varEnv;
+            DeclarativeEnvironmentRecord varEnvRec;
+            if (!hasParameterExpressions)
+            {
+                // NOTE: Only a single lexical environment is needed for the parameters and top-level vars.
+                for (var i = 0; i < configuration.VarsToInitialize.Count; i++)
+                {
+                    var pair = configuration.VarsToInitialize[i];
+                    envRec.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, JsValue.Undefined);
+                }
+
+                varEnv = env;
+                varEnvRec = envRec;
             }
             else
             {
-                // slow path
-                var variableDeclarationsCount = variableDeclarations.Count;
-                for (var i = 0; i < variableDeclarationsCount; i++)
+                // NOTE: A separate Environment Record is needed to ensure that closures created by expressions
+                // in the formal parameter list do not have visibility of declarations in the function body.
+                varEnv = LexicalEnvironment.NewDeclarativeEnvironment(this, env);
+                varEnvRec = (DeclarativeEnvironmentRecord) varEnv._record;
+                
+                UpdateVariableEnvironment(varEnv);
+
+                for (var i = 0; i < configuration.VarsToInitialize.Count; i++)
                 {
-                    var variableDeclaration = variableDeclarations[i];
-                    var declarations = variableDeclaration.Declarations;
-                    var declarationsCount = declarations.Count;
-                    for (var j = 0; j < declarationsCount; j++)
+                    var pair = configuration.VarsToInitialize[i];
+                    var initialValue = pair.InitialValue ?? envRec.GetBindingValue(pair.Name, strict: false);
+                    varEnvRec.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, initialValue);
+                }
+            }
+            
+            // NOTE: Annex B.3.3.1 adds additional steps at this point. 
+            // A https://tc39.es/ecma262/#sec-web-compat-functiondeclarationinstantiation
+
+            LexicalEnvironment lexEnv;
+            if (!strict)
+            {
+                lexEnv = LexicalEnvironment.NewDeclarativeEnvironment(this, varEnv);
+                // NOTE: Non-strict functions use a separate lexical Environment Record for top-level lexical declarations
+                // so that a direct eval can determine whether any var scoped declarations introduced by the eval code conflict
+                // with pre-existing top-level lexically scoped declarations. This is not needed for strict functions
+                // because a strict direct eval always places all declarations into a new Environment Record.
+            }
+            else
+            {
+                lexEnv = varEnv;
+            }
+
+            var lexEnvRec = lexEnv._record;
+            
+            UpdateLexicalEnvironment(lexEnv);
+
+            if (configuration.LexicalDeclarations.Length > 0)
+            {
+                InitializeLexicalDeclarations(configuration.LexicalDeclarations, lexEnvRec);
+            }
+
+            if (configuration.FunctionsToInitialize != null)
+            {
+                InitializeFunctions(configuration.FunctionsToInitialize, lexEnv, varEnvRec);
+            }
+
+            return ao;
+        }
+
+        private void InitializeFunctions(
+            LinkedList<FunctionDeclaration> functionsToInitialize,
+            LexicalEnvironment lexEnv,
+            DeclarativeEnvironmentRecord varEnvRec)
+        {
+            foreach (var f in functionsToInitialize)
+            {
+                var fn = f.Id.Name;
+                var fo = Function.CreateFunctionObject(f, lexEnv);
+                varEnvRec.SetMutableBinding(fn, fo, strict: false);
+            }
+        }
+
+        private static void InitializeLexicalDeclarations(
+            JintFunctionDefinition.State.LexicalVariableDeclaration[] lexicalDeclarations, 
+            EnvironmentRecord lexEnvRec)
+        {
+            foreach (var d in lexicalDeclarations)
+            {
+                for (var j = 0; j < d.BoundNames.Count; j++)
+                {
+                    var dn = d.BoundNames[j];
+                    if (d.Kind == VariableDeclarationKind.Const)
                     {
-                        var d = declarations[j];
-                        if (d.Id is Identifier id1)
-                        {
-                            var name = id1.Name;
-                            var varAlreadyDeclared = env.HasBinding(name);
-                            if (!varAlreadyDeclared)
-                            {
-                                env.CreateMutableBinding(name, Undefined.Instance);
-                            }
-                        }
+                        lexEnvRec.CreateImmutableBinding(dn, strict: true);
+                    }
+                    else
+                    {
+                        lexEnvRec.CreateMutableBinding(dn, canBeDeleted: false);
                     }
                 }
             }
+        }
 
-            return argsObj;
+        private ArgumentsInstance CreateMappedArgumentsObject(
+            FunctionInstance func, 
+            Key[] formals,
+            JsValue[] argumentsList, 
+            DeclarativeEnvironmentRecord envRec,
+            bool hasRestParameter)
+        {
+            return _argumentsInstancePool.Rent(func, formals, argumentsList, envRec, hasRestParameter);
+        }
+
+        private ArgumentsInstance CreateUnmappedArgumentsObject(JsValue[] argumentsList)
+        {
+            return _argumentsInstancePool.Rent(argumentsList);
         }
 
-        private void AddFunctionDeclarations(
-            ref NodeList<IFunctionDeclaration> functionDeclarations,
-            EnvironmentRecord env,
-            bool configurableBindings,
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-evaldeclarationinstantiation
+        /// </summary>
+        internal void EvalDeclarationInstantiation(
+            Script script,
+            LexicalEnvironment varEnv,
+            LexicalEnvironment lexEnv,
             bool strict)
         {
-            var functionDeclarationsCount = functionDeclarations.Count;
-            for (var i = 0; i < functionDeclarationsCount; i++)
+            var hoistingScope = HoistingScope.GetProgramLevelDeclarations(script);
+            
+            var lexEnvRec = (DeclarativeEnvironmentRecord) lexEnv._record;
+            var varEnvRec = varEnv._record;
+
+            if (!strict && hoistingScope._variablesDeclarations != null)
             {
-                var f = functionDeclarations[i];
-                var fn = f.Id.Name;
-                var fo = Function.CreateFunctionObject(f);
-                var funcAlreadyDeclared = env.HasBinding(f.Id.Name);
-                if (!funcAlreadyDeclared)
+                if (varEnvRec is GlobalEnvironmentRecord globalEnvironmentRecord)
                 {
-                    env.CreateMutableBinding(fn, configurableBindings);
+                    ref readonly var nodes = ref hoistingScope._variablesDeclarations;
+                    for (var i = 0; i < nodes.Count; i++)
+                    {
+                        var variablesDeclaration = nodes[i];
+                        var identifier = (Identifier) variablesDeclaration.Declarations[0].Id;
+                        if (globalEnvironmentRecord.HasLexicalDeclaration(identifier.Name))
+                        {
+                            ExceptionHelper.ThrowSyntaxError(this, "Identifier '" + identifier.Name + "' has already been declared");
+                        }
+                    }
                 }
-                else
+
+                var thisLex = lexEnv;
+                while (thisLex != varEnv)
                 {
-                    if (ReferenceEquals(env, GlobalEnvironment._record))
+                    var thisEnvRec = thisLex._record;
+                    if (!(thisEnvRec is ObjectEnvironmentRecord))
                     {
-                        var go = Global;
-                        var existingProp = go.GetProperty(fn);
-                        if (existingProp.Configurable)
+                        ref readonly var nodes = ref hoistingScope._variablesDeclarations;
+                        for (var i = 0; i < nodes.Count; i++)
                         {
-                            var flags = PropertyFlag.Writable | PropertyFlag.Enumerable;
-                            if (configurableBindings)
+                            var variablesDeclaration = nodes[i];
+                            var identifier = (Identifier) variablesDeclaration.Declarations[0].Id;
+                            if (thisEnvRec.HasBinding(identifier.Name))
                             {
-                                flags |= PropertyFlag.Configurable;
+                                ExceptionHelper.ThrowSyntaxError(this);
                             }
+                        }
+                    }
+
+                    thisLex = thisLex._outer;
+                }
+            }
+            
+            var functionDeclarations = hoistingScope._functionDeclarations;
+            var functionsToInitialize = new LinkedList<FunctionDeclaration>();
+            var declaredFunctionNames = new HashSet<string>();
 
-                            var descriptor = new PropertyDescriptor(Undefined.Instance, flags);
-                            go.DefinePropertyOrThrow(fn, descriptor);
+            if (functionDeclarations != null)
+            {
+                for (var i = functionDeclarations.Count - 1; i >= 0; i--)
+                {
+                    var d = functionDeclarations[i];
+                    var fn = d.Id.Name;
+                    if (!declaredFunctionNames.Contains(fn))
+                    {
+                        if (varEnvRec is GlobalEnvironmentRecord ger)
+                        {
+                            var fnDefinable = ger.CanDeclareGlobalFunction(fn);
+                            if (!fnDefinable)
+                            {
+                                ExceptionHelper.ThrowTypeError(this);
+                            }
                         }
-                        else
+                        declaredFunctionNames.Add(fn);
+                        functionsToInitialize.AddFirst(d);
+                    }
+                }
+            }
+
+            var boundNames = new List<string>();
+            var declaredVarNames = new List<string>();
+            var variableDeclarations = hoistingScope._variablesDeclarations;
+            var variableDeclarationsCount = variableDeclarations?.Count;
+            for (var i = 0; i < variableDeclarationsCount; i++)
+            {
+                var variableDeclaration = variableDeclarations[i];
+                boundNames.Clear();
+                variableDeclaration.GetBoundNames(boundNames);
+                for (var j = 0; j < boundNames.Count; j++)
+                {
+                    var vn = boundNames[j];
+                    if (!declaredFunctionNames.Contains(vn))
+                    {
+                        if (varEnvRec is GlobalEnvironmentRecord ger)
                         {
-                            if (existingProp.IsAccessorDescriptor() || !existingProp.Enumerable)
+                            var vnDefinable = ger.CanDeclareGlobalFunction(vn);
+                            if (!vnDefinable)
                             {
                                 ExceptionHelper.ThrowTypeError(this);
                             }
                         }
+
+                        declaredVarNames.Add(vn);
+                    }
+                }
+            }
+            
+            var lexicalDeclarations = hoistingScope._lexicalDeclarations;
+            var lexicalDeclarationsCount = lexicalDeclarations?.Count;
+            for (var i = 0; i < lexicalDeclarationsCount; i++)
+            {
+                boundNames.Clear();
+                var d = lexicalDeclarations[i];
+                d.GetBoundNames(boundNames);
+                for (var j = 0; j < boundNames.Count; j++)
+                {
+                    var dn = boundNames[j];
+                    if (d.Kind == VariableDeclarationKind.Const)
+                    {
+                        lexEnvRec.CreateImmutableBinding(dn, strict: true);
+                    }
+                    else
+                    {
+                        lexEnvRec.CreateMutableBinding(dn, canBeDeleted: false);
+                    }
+                }
+            }
+
+            foreach (var f in functionsToInitialize)
+            {
+                var fn = f.Id.Name;
+                var fo = Function.CreateFunctionObject(f, lexEnv);
+                if (varEnvRec is GlobalEnvironmentRecord ger)
+                {
+                    ger.CreateGlobalFunctionBinding(fn, fo, canBeDeleted: true);
+                }
+                else
+                {
+                    var bindingExists = varEnvRec.HasBinding(fn);
+                    if (!bindingExists)
+                    {
+                        varEnvRec.CreateMutableBinding(fn, canBeDeleted: true);
+                        varEnvRec.InitializeBinding(fn, fo);
+                    }
+                    else
+                    {
+                        varEnvRec.SetMutableBinding(fn, fo, strict: false);
                     }
                 }
+            }
 
-                env.SetMutableBinding(fn, fo, strict);
+            foreach (var vn in declaredVarNames)
+            {
+                if (varEnvRec is GlobalEnvironmentRecord ger)
+                {
+                    ger.CreateGlobalVarBinding(vn, true);
+                }
+                else
+                {
+                    var bindingExists = varEnvRec.HasBinding(vn);
+                    if (!bindingExists)
+                    {
+                        varEnvRec.CreateMutableBinding(vn, canBeDeleted: true);
+                        varEnvRec.InitializeBinding(vn, JsValue.Undefined);
+                    }
+                }
             }
+
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -856,12 +1209,10 @@ namespace Jint
             _executionContexts.ReplaceTopLexicalEnvironment(newEnv);
         }
 
-        private static void AssertNotNullOrEmpty(string propertyName, string propertyValue)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void UpdateVariableEnvironment(LexicalEnvironment newEnv)
         {
-            if (string.IsNullOrEmpty(propertyValue))
-            {
-                ExceptionHelper.ThrowArgumentException(propertyName);
-            }
+            _executionContexts.ReplaceTopVariableEnvironment(newEnv);
         }
     }
 }

+ 78 - 6
Jint/EsprimaExtensions.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using System.Globalization;
 using System.Runtime.CompilerServices;
 using Esprima.Ast;
@@ -12,28 +13,28 @@ namespace Jint
     {
         public static JsValue GetKey(this Property property, Engine engine) => GetKey(property.Key, engine, property.Computed);
 
-        private static JsValue GetKey<T>(this T expression, Engine engine, bool computed) where T : class, Expression
+        public static JsValue GetKey<T>(this T expression, Engine engine, bool resolveComputed = false) where T : Expression
         {
             if (expression is Literal literal)
             {
                 return LiteralKeyToString(literal);
             }
 
-            if (!computed && expression is Identifier identifier)
+            if (!resolveComputed && expression is Identifier identifier)
             {
                 return identifier.Name;
             }
 
-            if (!TryGetComputedPropertyKey(expression, engine, out var propertyKey))
+            if (!resolveComputed || !TryGetComputedPropertyKey(expression, engine, out var propertyKey))
             {
-                ExceptionHelper.ThrowArgumentException("Unable to extract correct key, node type: " + expression.Type);
+                return ExceptionHelper.ThrowArgumentException<JsValue>("Unable to extract correct key, node type: " + expression.Type);
             }
 
             return propertyKey;
         }
 
         private static bool TryGetComputedPropertyKey<T>(T expression, Engine engine, out JsValue propertyKey)
-            where T : class, Expression
+            where T : Expression
         {
             if (expression.Type == Nodes.Identifier
                 || expression.Type == Nodes.CallExpression
@@ -50,7 +51,7 @@ namespace Jint
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static bool IsFunctionWithName<T>(this T node) where T : class, INode
+        internal static bool IsFunctionWithName<T>(this T node) where T : Node
         {
             var type = node.Type;
             return type == Nodes.FunctionExpression || type == Nodes.ArrowFunctionExpression || type == Nodes.ArrowParameterPlaceHolder;
@@ -72,5 +73,76 @@ namespace Jint
         {
             return (d - (long) d) == 0 ? ((long) d).ToString() : d.ToString(CultureInfo.InvariantCulture);
         }
+        
+        internal static void GetBoundNames(this VariableDeclaration variableDeclaration, List<string> target)
+        {
+            ref readonly var declarations = ref variableDeclaration.Declarations;
+            for (var i = 0; i < declarations.Count; i++)
+            {
+                var declaration = declarations[i];
+                GetBoundNames(declaration.Id, target);
+            }
+        }
+
+        internal static void GetBoundNames(this Node parameter, List<string> target)
+        {
+            if (parameter is null || parameter.Type == Nodes.Literal)
+            {
+                return;
+            }
+            
+            // try to get away without a loop
+            if (parameter is Identifier id)
+            {
+                target.Add(id.Name);
+                return;
+            }
+
+            while (true)
+            {
+                if (parameter is Identifier identifier)
+                {
+                    target.Add(identifier.Name);
+                    return;
+                }
+                
+                if (parameter is RestElement restElement)
+                {
+                    parameter = restElement.Argument;
+                    continue;
+                }
+                else if (parameter is ArrayPattern arrayPattern)
+                {
+                    ref readonly var arrayPatternElements = ref arrayPattern.Elements;
+                    for (var i = 0; i < arrayPatternElements.Count; i++)
+                    {
+                        var expression = arrayPatternElements[i];
+                        GetBoundNames(expression, target);
+                    }
+                }
+                else if (parameter is ObjectPattern objectPattern)
+                {
+                    ref readonly var objectPatternProperties = ref objectPattern.Properties;
+                    for (var i = 0; i < objectPatternProperties.Count; i++)
+                    {
+                        var property = objectPatternProperties[i];
+                        if (property is Property p)
+                        {
+                            GetBoundNames(p.Value, target);
+                        }
+                        else
+                        {
+                            GetBoundNames((RestElement) property, target);
+                        }
+                    }
+                }
+                else if (parameter is AssignmentPattern assignmentPattern)
+                {
+                    parameter = assignmentPattern.Left;
+                    continue;
+                }
+                break;
+            }
+        }
     }
 }

+ 0 - 62
Jint/EvalCodeScope.cs

@@ -1,62 +0,0 @@
-using System;
-
-namespace Jint
-{
-    public class EvalCodeScope : IDisposable
-    {
-        private readonly bool _eval;
-        private readonly bool _force;
-        private readonly int _forcedRefCount;
-
-        [ThreadStatic] 
-        private static int _refCount;
-
-        public EvalCodeScope(bool eval = true, bool force = false)
-        {
-            _eval = eval;
-            _force = force;
-
-            if (_force)
-            {
-                _forcedRefCount = _refCount;
-                _refCount = 0;
-            }
-
-            if (_eval)
-            {
-                _refCount++;
-            }
-
-        }
-
-        public void Dispose()
-        {
-            if (_eval)
-            {
-                _refCount--;
-            }
-
-            if (_force)
-            {
-                _refCount = _forcedRefCount;
-            } 
-        }
-
-        public static bool IsEvalCode
-        {
-            get { return _refCount > 0; }
-        }
-
-        public static int RefCount
-        {
-            get
-            {
-                return _refCount;
-            }
-            set
-            {
-                _refCount = value;
-            }
-        }
-    }
-}

+ 200 - 0
Jint/HoistingScope.cs

@@ -0,0 +1,200 @@
+using System.Collections.Generic;
+using Esprima.Ast;
+
+namespace Jint
+{
+    internal readonly struct HoistingScope
+    {
+        internal readonly List<FunctionDeclaration> _functionDeclarations;
+        
+        internal readonly List<VariableDeclaration> _variablesDeclarations;
+        internal readonly List<Key> _varNames;
+
+        internal readonly List<VariableDeclaration> _lexicalDeclarations;
+        internal readonly List<string> _lexicalNames;
+
+        private HoistingScope(
+            List<FunctionDeclaration> functionDeclarations,
+            List<Key> varNames,
+            List<VariableDeclaration> variableDeclarations,
+            List<VariableDeclaration> lexicalDeclarations,
+            List<string> lexicalNames)
+        {
+            _functionDeclarations = functionDeclarations;
+            _varNames = varNames;
+            _variablesDeclarations = variableDeclarations;
+            _lexicalDeclarations = lexicalDeclarations;
+            _lexicalNames = lexicalNames;
+        }
+
+        public static HoistingScope GetProgramLevelDeclarations(
+            Script script,
+            bool collectVarNames = false, 
+            bool collectLexicalNames = false)
+        {
+            var treeWalker = new ScriptWalker(StrictModeScope.IsStrictModeCode, collectVarNames, collectLexicalNames);
+            treeWalker.Visit(script, null);
+            return new HoistingScope(
+                treeWalker._functions,
+                treeWalker._varNames,
+                treeWalker._variableDeclarations,
+                treeWalker._lexicalDeclarations,
+                treeWalker._lexicalNames);
+        }
+        
+        public static HoistingScope GetFunctionLevelDeclarations(
+            IFunction node,
+            bool collectVarNames = false,
+            bool collectLexicalNames = false)
+        {
+            var treeWalker = new ScriptWalker(StrictModeScope.IsStrictModeCode, collectVarNames, collectLexicalNames);
+            treeWalker.Visit(node.Body, null);
+            
+            return new HoistingScope(
+                treeWalker._functions,
+                treeWalker._varNames,
+                treeWalker._variableDeclarations,
+                treeWalker._lexicalDeclarations,
+                treeWalker._lexicalNames);
+        }
+
+        public static List<VariableDeclaration> GetLexicalDeclarations(BlockStatement statement)
+        {
+            List<VariableDeclaration> lexicalDeclarations = null ;
+            ref readonly var statementListItems = ref statement.Body;
+            for (var i = 0; i < statementListItems.Count; i++)
+            {
+                var node = statementListItems[i];
+                if (node.Type != Nodes.VariableDeclaration)
+                {
+                    continue;
+                }
+
+                var rootVariable = (VariableDeclaration) node;
+                if (rootVariable.Kind == VariableDeclarationKind.Var)
+                {
+                    continue;
+                }
+
+                lexicalDeclarations ??= new List<VariableDeclaration>();
+                lexicalDeclarations.Add(rootVariable);
+            }
+            
+            return lexicalDeclarations;
+        }
+
+        public static List<VariableDeclaration> GetLexicalDeclarations(SwitchCase statement)
+        {
+            List<VariableDeclaration> lexicalDeclarations = null ;
+            ref readonly var statementListItems = ref statement.Consequent;
+            for (var i = 0; i < statementListItems.Count; i++)
+            {
+                var node = statementListItems[i];
+                if (node.Type != Nodes.VariableDeclaration)
+                {
+                    continue;
+                }
+                
+                var rootVariable = (VariableDeclaration) node;
+                if (rootVariable.Kind == VariableDeclarationKind.Var)
+                {
+                    continue;
+                }
+
+                lexicalDeclarations ??= new List<VariableDeclaration>();
+                lexicalDeclarations.Add(rootVariable);
+            }
+
+            return lexicalDeclarations;
+        }
+
+        private sealed class ScriptWalker
+        {
+            internal List<FunctionDeclaration> _functions;
+
+            private readonly bool _strict;
+            private readonly bool _collectVarNames;
+            internal List<VariableDeclaration> _variableDeclarations;
+            internal List<Key> _varNames;
+
+            private readonly bool _collectLexicalNames;
+            internal List<VariableDeclaration> _lexicalDeclarations;
+            internal List<string> _lexicalNames;
+
+            public ScriptWalker(bool strict, bool collectVarNames, bool collectLexicalNames)
+            {
+                _strict = strict;
+                _collectVarNames = collectVarNames;
+                _collectLexicalNames = collectLexicalNames;
+            }
+
+            public void Visit(Node node, Node parent)
+            {
+                foreach (var childNode in node.ChildNodes)
+                {
+                    if (childNode is null)
+                    {
+                        // array expression can push null nodes in Esprima
+                        continue;
+                    }
+
+                    if (childNode.Type == Nodes.VariableDeclaration)
+                    {
+                        var variableDeclaration = (VariableDeclaration) childNode;
+                        if (variableDeclaration.Kind == VariableDeclarationKind.Var)
+                        {
+                            _variableDeclarations ??= new List<VariableDeclaration>();
+                            _variableDeclarations.Add(variableDeclaration);
+                            if (_collectVarNames)
+                            {
+                                _varNames ??= new List<Key>();
+                                ref readonly var nodeList = ref variableDeclaration.Declarations;
+                                foreach (var declaration in nodeList)
+                                {
+                                    if (declaration.Id is Identifier identifier)
+                                    {
+                                        _varNames.Add(identifier.Name);
+                                    }
+                                }
+                            }
+                        }
+
+                        if (parent is null && variableDeclaration.Kind != VariableDeclarationKind.Var)
+                        {
+                            _lexicalDeclarations ??= new List<VariableDeclaration>();
+                            _lexicalDeclarations.Add(variableDeclaration);
+                            if (_collectLexicalNames)
+                            {
+                                _lexicalNames ??= new List<string>();
+                                ref readonly var nodeList = ref variableDeclaration.Declarations;
+                                foreach (var declaration in nodeList)
+                                {
+                                    if (declaration.Id is Identifier identifier)
+                                    {
+                                        _lexicalNames.Add(identifier.Name);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    else if (childNode.Type == Nodes.FunctionDeclaration 
+                             // in strict mode cannot include function declarations directly under block or case clauses
+                             && (!_strict || parent is null || (node.Type != Nodes.BlockStatement && node.Type != Nodes.SwitchCase)))
+                    {
+                        _functions ??= new List<FunctionDeclaration>();
+                        _functions.Add((FunctionDeclaration) childNode);
+                    }
+
+                    if (childNode.Type != Nodes.FunctionDeclaration
+                        && childNode.Type != Nodes.ArrowFunctionExpression
+                        && childNode.Type != Nodes.ArrowParameterPlaceHolder
+                        && childNode.Type != Nodes.FunctionExpression
+                        && childNode.ChildNodes.Count > 0)
+                    {
+                        Visit(childNode, node);
+                    }
+                }
+            }
+        }
+    }
+}

+ 1 - 1
Jint/Jint.csproj

@@ -7,6 +7,6 @@
     <LangVersion>latest</LangVersion>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Esprima" Version="1.0.1270" />
+    <PackageReference Include="Esprima" Version="2.0.0-beta-1298" />
   </ItemGroup>
 </Project>

+ 42 - 40
Jint/Native/Argument/ArgumentsInstance.cs

@@ -20,11 +20,11 @@ namespace Jint.Native.Argument
         private static readonly ThreadLocal<HashSet<string>> _mappedNamed = new ThreadLocal<HashSet<string>>(() => new HashSet<string>());
 
         private FunctionInstance _func;
-        private string[] _names;
+        private Key[] _names;
         private JsValue[] _args;
-        private EnvironmentRecord _env;
-        private bool _strict;
+        private DeclarativeEnvironmentRecord _env;
         private bool _canReturnToPool;
+        private bool _hasRestParameter;
 
         internal ArgumentsInstance(Engine engine)
             : base(engine, ObjectClass.Arguments, InternalTypes.Object | InternalTypes.RequiresCloning)
@@ -33,17 +33,16 @@ namespace Jint.Native.Argument
 
         internal void Prepare(
             FunctionInstance func, 
-            string[] names, 
+            Key[] names, 
             JsValue[] args, 
-            EnvironmentRecord env, 
-            bool strict)
+            DeclarativeEnvironmentRecord env,
+            bool hasRestParameter)
         {
             _func = func;
             _names = names;
             _args = args;
             _env = env;
-            _strict = strict;
-
+            _hasRestParameter = hasRestParameter;
             _canReturnToPool = true;
 
             ClearProperties();
@@ -52,49 +51,52 @@ namespace Jint.Native.Argument
         protected override void Initialize()
         {
             _canReturnToPool = false;
+            var args = _args;
 
-            SetOwnProperty(CommonProperties.Length, new PropertyDescriptor(_args.Length, PropertyFlag.NonEnumerable));
+            DefinePropertyOrThrow(CommonProperties.Length, new PropertyDescriptor(_args.Length, PropertyFlag.NonEnumerable));
 
-            var args = _args;
-            ObjectInstance map = null;
-            if (args.Length > 0)
+            if (_func is null)
             {
-                HashSet<string> mappedNamed = null;
-                if (!_strict)
+                // unmapped
+                ParameterMap = null;
+                
+                for (uint i = 0; i < (uint) args.Length; i++)
                 {
-                    mappedNamed = _mappedNamed.Value;
-                    mappedNamed.Clear();
+                    var val = args[i];
+                    CreateDataProperty(JsNumber.Create(i), val);
                 }
-
-                for (uint i = 0; i < (uint) args.Length; i++)
+                
+                DefinePropertyOrThrow(CommonProperties.Callee, _engine._getSetThrower);
+            }
+            else
+            {
+                ObjectInstance map = null;
+                if (args.Length > 0)
                 {
-                    SetOwnProperty(i, new PropertyDescriptor(args[i], PropertyFlag.ConfigurableEnumerableWritable));
-                    if (i < _names.Length)
+                    var mappedNamed = _mappedNamed.Value;
+                    mappedNamed.Clear();
+
+                    map = Engine.Object.Construct(Arguments.Empty);
+
+                    for (uint i = 0; i < (uint) args.Length; i++)
                     {
-                        var name = _names[i];
-                        if (!_strict && !mappedNamed.Contains(name))
+                        SetOwnProperty(i, new PropertyDescriptor(args[i], PropertyFlag.ConfigurableEnumerableWritable));
+                        if (i < _names.Length)
                         {
-                            map ??= Engine.Object.Construct(Arguments.Empty);
-                            mappedNamed.Add(name);
-                            map.SetOwnProperty(i, new ClrAccessDescriptor(_env, Engine, name));
+                            var name = _names[i];
+                            if (mappedNamed.Add(name))
+                            {
+                                map.SetOwnProperty(i, new ClrAccessDescriptor(_env, Engine, name));
+                            }
                         }
                     }
                 }
-            }
 
-            ParameterMap = map;
+                ParameterMap = map;
 
-            // step 13
-            if (!_strict)
-            {
+                // step 13
                 DefinePropertyOrThrow(CommonProperties.Callee, new PropertyDescriptor(_func, PropertyFlag.NonEnumerable));
             }
-            // step 14
-            else
-            {
-                DefinePropertyOrThrow(CommonProperties.Caller, _engine._getSetThrower);
-                DefinePropertyOrThrow(CommonProperties.Callee, _engine._getSetThrower);
-            }
 
             var iteratorFunction = new ClrFunctionInstance(Engine, "iterator", _engine.Array.PrototypeObject.Values, 0, PropertyFlag.Configurable);
             DefinePropertyOrThrow(GlobalSymbolRegistry.Iterator, new PropertyDescriptor(iteratorFunction, PropertyFlag.Writable | PropertyFlag.Configurable));
@@ -106,7 +108,7 @@ namespace Jint.Native.Argument
         {
             EnsureInitialized();
 
-            if (!_strict && !ReferenceEquals(ParameterMap, null))
+            if (!ReferenceEquals(ParameterMap, null))
             {
                 var desc = base.GetOwnProperty(property);
                 if (desc == PropertyDescriptor.Undefined)
@@ -167,7 +169,7 @@ namespace Jint.Native.Argument
 
         public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
         {
-            if (_func is ScriptFunctionInstance scriptFunctionInstance && scriptFunctionInstance._function._hasRestParameter)
+            if (_hasRestParameter)
             {
                 // immutable
                 return true;
@@ -175,7 +177,7 @@ namespace Jint.Native.Argument
 
             EnsureInitialized();
 
-            if (!_strict && !ReferenceEquals(ParameterMap, null))
+            if (!(_func is null) && !ReferenceEquals(ParameterMap, null))
             {
                 var map = ParameterMap;
                 var isMapped = map.GetOwnProperty(property);
@@ -216,7 +218,7 @@ namespace Jint.Native.Argument
         {
             EnsureInitialized();
 
-            if (!_strict && !ReferenceEquals(ParameterMap, null))
+            if (!(_func is null) && !ReferenceEquals(ParameterMap, null))
             {
                 var map = ParameterMap;
                 var isMapped = map.GetOwnProperty(property);

+ 1 - 1
Jint/Native/Array/ArrayConstructor.cs

@@ -17,7 +17,7 @@ namespace Jint.Native.Array
     {
         private static readonly JsString _functionName = new JsString("Array");
 
-        private ArrayConstructor(Engine engine) :  base(engine, _functionName, false)
+        private ArrayConstructor(Engine engine) :  base(engine, _functionName)
         {
         }
 

+ 1 - 1
Jint/Native/Boolean/BooleanConstructor.cs

@@ -10,7 +10,7 @@ namespace Jint.Native.Boolean
         private static readonly JsString _functionName = new JsString("Boolean");
 
         private BooleanConstructor(Engine engine)
-            : base(engine, _functionName, strict: false)
+            : base(engine, _functionName)
         {
         }
 

+ 1 - 1
Jint/Native/Date/DateConstructor.cs

@@ -54,7 +54,7 @@ namespace Jint.Native.Date
 
         private static readonly JsString _functionName = new JsString("Date");
 
-        public DateConstructor(Engine engine) : base(engine, _functionName, strict: false)
+        public DateConstructor(Engine engine) : base(engine, _functionName)
         {
         }
 

+ 1 - 1
Jint/Native/Error/ErrorConstructor.cs

@@ -10,7 +10,7 @@ namespace Jint.Native.Error
         private JsString _name;
         private static readonly JsString _functionName = new JsString("Error");
 
-        public ErrorConstructor(Engine engine) : base(engine, _functionName, strict: false)
+        public ErrorConstructor(Engine engine) : base(engine, _functionName)
         {
         }
 

+ 10 - 19
Jint/Native/Function/ArrowFunctionInstance.cs

@@ -2,6 +2,7 @@ using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Environments;
 using Jint.Runtime.Interpreter;
 
@@ -10,7 +11,6 @@ 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/6.0/#sec-arrow-function-definitions
@@ -29,47 +29,38 @@ namespace Jint.Native.Function
             JintFunctionDefinition function,
             LexicalEnvironment scope,
             bool strict)
-            : base(engine, (string) null, function._parameterNames, scope, strict)
+            : base(engine, function, scope, strict ? FunctionThisMode.Strict : FunctionThisMode.Lexical)
         {
             _function = function;
 
             PreventExtensions();
             _prototype = Engine.Function.PrototypeObject;
 
-            _length = new PropertyDescriptor(JsNumber.Create(function._length), PropertyFlag.Configurable);
-            _thisBinding = _engine.ExecutionContext.ThisBinding;
+            _length = new LazyPropertyDescriptor(() => JsNumber.Create(function.Initialize(engine, this).Length), PropertyFlag.Configurable);
         }
 
         // for example RavenDB wants to inspect this
-        public IFunction FunctionDeclaration => _function._function;
+        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);
+                var localEnv = LexicalEnvironment.NewFunctionEnvironment(_engine, this, Undefined);
+                _engine.EnterExecutionContext(localEnv, localEnv);
 
                 try
                 {
-                    _engine.DeclarationBindingInstantiation(
-                        DeclarationBindingType.FunctionCode,
-                        _function._hoistingScope,
+                    _engine.FunctionDeclarationInstantiation(
                         functionInstance: this,
-                        arguments);
+                        arguments,
+                        localEnv);
 
-                    var result = _function._body.Execute();
+                    var result = _function.Body.Execute();
 
                     var value = result.GetValueOrDefault().Clone();
 

+ 3 - 1
Jint/Native/Function/BindFunctionInstance.cs

@@ -6,8 +6,10 @@ namespace Jint.Native.Function
 {
     public sealed class BindFunctionInstance : FunctionInstance, IConstructor
     {
+        private static readonly JsString _name = new JsString("bind");
+
         public BindFunctionInstance(Engine engine)
-            : base(engine, "bind", System.Array.Empty<string>(), null, false)
+            : base(engine, _name, FunctionThisMode.Global)
         {
         }
 

+ 64 - 64
Jint/Native/Function/EvalFunctionInstance.cs

@@ -1,4 +1,5 @@
 using Esprima;
+using Esprima.Ast;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Environments;
@@ -11,8 +12,8 @@ namespace Jint.Native.Function
         private static readonly ParserOptions ParserOptions = new ParserOptions { AdaptRegexp = true, Tolerant = false };
         private static readonly JsString _functionName = new JsString("eval");
 
-        public EvalFunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict) 
-            : base(engine, _functionName, parameters, scope, strict)
+        public EvalFunctionInstance(Engine engine) 
+            : base(engine, _functionName, StrictModeScope.IsStrictModeCode ? FunctionThisMode.Strict : FunctionThisMode.Global)
         {
             _prototype = Engine.Function.PrototypeObject;
             _length = PropertyDescriptor.AllForbiddenDescriptor.NumberOne;
@@ -23,85 +24,84 @@ namespace Jint.Native.Function
             return Call(thisObject, arguments, false);
         }
 
-        public JsValue Call(JsValue thisObject, JsValue[] arguments, bool directCall)
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-performeval
+        /// </summary>
+        public JsValue Call(JsValue thisObject, JsValue[] arguments, bool direct)
         {
-            var arg = arguments.At(0);
-            if (arg.Type != Types.String)
+            if (!(arguments.At(0) is JsString x))
             {
-                return arg;
+                return arguments.At(0);
             }
 
-            var code = TypeConverter.ToString(arg);
-
+            var parser = new JavaScriptParser(x.ToString(), ParserOptions);
+            Script script;
             try
             {
-                var parser = new JavaScriptParser(code, ParserOptions);
-                var program = parser.ParseScript(StrictModeScope.IsStrictModeCode);
-                using (new StrictModeScope(program.Strict))
-                {
-                    using (new EvalCodeScope())
-                    {
-                        LexicalEnvironment strictVarEnv = null;
+                script = parser.ParseScript(StrictModeScope.IsStrictModeCode);
+            }
+            catch (ParserException e)
+            {
+                return e.Description == Messages.InvalidLHSInAssignment 
+                    ? ExceptionHelper.ThrowReferenceError<JsValue>(_engine)
+                    : ExceptionHelper.ThrowSyntaxError<JsValue>(_engine);
+            }
 
-                        try
-                        {
-                            if (!directCall)
-                            {
-                                Engine.EnterExecutionContext(Engine.GlobalEnvironment, Engine.GlobalEnvironment, Engine.Global);
-                            }
+            var body = script.Body;
+            if (body.Count == 0)
+            {
+                return Undefined;
+            }
 
-                            var lexicalEnvironment = _engine.ExecutionContext.LexicalEnvironment;
-                            if (StrictModeScope.IsStrictModeCode)
-                            {
-                                strictVarEnv = LexicalEnvironment.NewDeclarativeEnvironment(Engine, lexicalEnvironment);
-                                Engine.EnterExecutionContext(strictVarEnv, strictVarEnv, Engine.ExecutionContext.ThisBinding);
-                            }
+            var strictEval = script.Strict || _engine._isStrict;
+            var ctx = _engine.ExecutionContext;
 
-                            var argumentsInstance = Engine.DeclarationBindingInstantiation(
-                                DeclarationBindingType.EvalCode,
-                                program.HoistingScope,
-                                functionInstance: this,
-                                arguments);
+            using (new StrictModeScope(strictEval))
+            {
+                LexicalEnvironment lexEnv;
+                LexicalEnvironment varEnv;
+                if (direct)
+                {
+                    lexEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, ctx.LexicalEnvironment);
+                    varEnv = ctx.VariableEnvironment;
+                }
+                else
+                {
+                    lexEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, Engine.GlobalEnvironment);
+                    varEnv = Engine.GlobalEnvironment;
+                }
 
-                            var statement = JintStatement.Build(_engine, program);
-                            var result = statement.Execute();
-                            var value = result.GetValueOrDefault();
+                if (strictEval)
+                {
+                    varEnv = lexEnv;
+                }
 
-                            argumentsInstance?.FunctionWasCalled();
+                // If ctx is not already suspended, suspend ctx.
+                
+                Engine.EnterExecutionContext(lexEnv, varEnv);
+                
+                try
+                {
+                    Engine.EvalDeclarationInstantiation(script, varEnv, lexEnv, strictEval);
 
-                            if (result.Type == CompletionType.Throw)
-                            {
-                                var ex = new JavaScriptException(value).SetCallstack(_engine, result.Location);
-                                throw ex;
-                            }
-                            else
-                            {
-                                return value;
-                            }
-                        }
-                        finally
-                        {
-                            if (strictVarEnv != null)
-                            {
-                                Engine.LeaveExecutionContext();
-                            }
+                    var statement = JintStatement.Build(_engine, script);
+                    var result = statement.Execute();
+                    var value = result.GetValueOrDefault();
 
-                            if (!directCall)
-                            {
-                                Engine.LeaveExecutionContext();
-                            }
-                        }
+                    if (result.Type == CompletionType.Throw)
+                    {
+                        var ex = new JavaScriptException(value).SetCallstack(_engine, result.Location);
+                        throw ex;
+                    }
+                    else
+                    {
+                        return value;
                     }
                 }
-            }
-            catch (ParserException e)
-            {
-                if (e.Description == Messages.InvalidLHSInAssignment)
+                finally
                 {
-                    ExceptionHelper.ThrowReferenceError(_engine, (string) null);
+                    Engine.LeaveExecutionContext();
                 }
-
-                return ExceptionHelper.ThrowSyntaxError<JsValue>(_engine);
             }
         }
     }

+ 5 - 6
Jint/Native/Function/FunctionConstructor.cs

@@ -16,7 +16,7 @@ namespace Jint.Native.Function
         private static readonly char[] ArgumentNameSeparator = new[] { ',' };
 
         private FunctionConstructor(Engine engine)
-            : base(engine, _functionName, strict: false)
+            : base(engine, _functionName)
         {
         }
 
@@ -83,11 +83,10 @@ namespace Jint.Native.Function
             var functionObject = new ScriptFunctionInstance(
                 Engine,
                 function,
-                LexicalEnvironment.NewDeclarativeEnvironment(Engine, Engine.ExecutionContext.LexicalEnvironment),
+                _engine.GlobalEnvironment,
                 function.Strict);
 
             return functionObject;
-
         }
 
         /// <summary>
@@ -95,13 +94,13 @@ namespace Jint.Native.Function
         /// </summary>
         /// <param name="functionDeclaration"></param>
         /// <returns></returns>
-        public FunctionInstance CreateFunctionObject(IFunctionDeclaration functionDeclaration)
+        public FunctionInstance CreateFunctionObject(FunctionDeclaration functionDeclaration, LexicalEnvironment env)
         {
             var functionObject = new ScriptFunctionInstance(
                 Engine,
                 functionDeclaration,
-                LexicalEnvironment.NewDeclarativeEnvironment(Engine, Engine.ExecutionContext.LexicalEnvironment),
-                functionDeclaration.Strict);
+                env, 
+                functionDeclaration.Strict || _engine._isStrict);
 
             return functionObject;
         }

+ 21 - 28
Jint/Native/Function/FunctionInstance.cs

@@ -4,11 +4,19 @@ using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Environments;
+using Jint.Runtime.Interpreter;
 
 namespace Jint.Native.Function
 {
     public abstract class FunctionInstance : ObjectInstance, ICallable
     {
+        internal enum FunctionThisMode
+        {
+            Lexical,
+            Strict,
+            Global
+        }
+
         protected internal PropertyDescriptor _prototypeDescriptor;
 
         protected PropertyDescriptor _length;
@@ -16,41 +24,30 @@ namespace Jint.Native.Function
         private JsValue _name;
         private PropertyDescriptor _nameDescriptor;
 
-        protected readonly LexicalEnvironment _scope;
-        protected internal readonly string[] _formalParameters;
-        protected readonly bool _strict;
-
-        protected FunctionInstance(
-            Engine engine,
-            string name,
-            string[] parameters,
-            LexicalEnvironment scope,
-            bool strict)
-            : this(engine, !string.IsNullOrWhiteSpace(name) ? new JsString(name) : null, parameters, scope, strict)
-        {
-        }
+        protected internal LexicalEnvironment _environment;
+        internal readonly JintFunctionDefinition _functionDefinition;
+        internal readonly FunctionThisMode _thisMode;
 
         internal FunctionInstance(
             Engine engine,
-            JsString name,
-            string[] parameters,
+            JintFunctionDefinition function,
             LexicalEnvironment scope,
-            bool strict)
-            : this(engine, name, strict)
+            FunctionThisMode thisMode)
+            : this(engine, !string.IsNullOrWhiteSpace(function.Name) ? new JsString(function.Name) : null,  thisMode)
         {
-            _formalParameters = parameters;
-            _scope = scope;
+            _functionDefinition = function;
+            _environment = scope;
         }
 
         internal FunctionInstance(
             Engine engine,
             JsString name,
-            bool strict,
+            FunctionThisMode thisMode = FunctionThisMode.Global,
             ObjectClass objectClass = ObjectClass.Function)
             : base(engine, objectClass)
         {
             _name = name;
-            _strict = strict;
+            _thisMode = thisMode;
         }
 
         /// <summary>
@@ -61,12 +58,8 @@ namespace Jint.Native.Function
         /// <returns></returns>
         public abstract JsValue Call(JsValue thisObject, JsValue[] arguments);
 
-        internal LexicalEnvironment Scope => _scope;
-
-        internal string[] FormalParameters => _formalParameters;
-
-        public bool Strict => _strict;
-
+        public bool Strict => _thisMode == FunctionThisMode.Strict;
+        
         public virtual bool HasInstance(JsValue v)
         {
             if (!(v is ObjectInstance o))
@@ -104,7 +97,7 @@ namespace Jint.Native.Function
             var v = base.Get(property, receiver);
 
             if (property == CommonProperties.Caller
-                && ((v.As<FunctionInstance>()?._strict).GetValueOrDefault()))
+                && v.As<FunctionInstance>()?._thisMode == FunctionThisMode.Strict)
             {
                 ExceptionHelper.ThrowTypeError(_engine);
             }

+ 1 - 1
Jint/Native/Function/FunctionPrototype.cs

@@ -15,7 +15,7 @@ namespace Jint.Native.Function
         private static readonly JsString _functionName = new JsString("Function");
 
         private FunctionPrototype(Engine engine)
-            : base(engine, _functionName, strict: false)
+            : base(engine, _functionName)
         {
         }
 

+ 0 - 17
Jint/Native/Function/FunctionShim.cs

@@ -1,17 +0,0 @@
-using Jint.Runtime.Environments;
-
-namespace Jint.Native.Function
-{
-    public sealed class FunctionShim : FunctionInstance
-    {
-        public FunctionShim(Engine engine, string[] parameters, LexicalEnvironment scope)
-            : base(engine, "function", parameters, scope, false)
-        {
-        }
-
-        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
-        {
-            return Undefined;
-        }
-    }
-}

+ 47 - 36
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -3,6 +3,7 @@ using Esprima.Ast;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Environments;
 using Jint.Runtime.Interpreter;
 
@@ -20,7 +21,7 @@ namespace Jint.Native.Function
             IFunction functionDeclaration,
             LexicalEnvironment scope,
             bool strict)
-            : this(engine, new JintFunctionDefinition(engine, functionDeclaration), scope, strict)
+            : this(engine, new JintFunctionDefinition(engine, functionDeclaration), scope, strict ? FunctionThisMode.Strict : FunctionThisMode.Global)
         {
         }
 
@@ -28,14 +29,14 @@ namespace Jint.Native.Function
             Engine engine,
             JintFunctionDefinition function,
             LexicalEnvironment scope,
-            bool strict)
-            : base(engine, function._name, function._parameterNames, scope, strict)
+            FunctionThisMode thisMode)
+            : base(engine, function, scope, thisMode)
         {
             _function = function;
 
             _prototype = _engine.Function.PrototypeObject;
 
-            _length = new PropertyDescriptor(JsNumber.Create(function._length), PropertyFlag.Configurable);
+            _length = new LazyPropertyDescriptor(() => JsNumber.Create(function.Initialize(engine, this).Length), PropertyFlag.Configurable);
 
             var proto = new ObjectInstanceWithConstructor(engine, this)
             {
@@ -44,7 +45,7 @@ namespace Jint.Native.Function
 
             _prototypeDescriptor = new PropertyDescriptor(proto, PropertyFlag.OnlyWritable);
 
-            if (strict)
+            if (thisMode == FunctionThisMode.Strict)
             {
                 DefineOwnProperty(CommonProperties.Caller, engine._getSetThrower);
                 DefineOwnProperty(CommonProperties.Arguments, engine._getSetThrower);
@@ -52,54 +53,64 @@ namespace Jint.Native.Function
         }
 
         // for example RavenDB wants to inspect this
-        public IFunction FunctionDeclaration => _function._function;
+        public IFunction FunctionDeclaration => _function.Function;
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.1
+        /// https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist
         /// </summary>
-        /// <param name="thisArg"></param>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
-        public override JsValue Call(JsValue thisArg, JsValue[] arguments)
+        public override JsValue Call(JsValue thisArgument, JsValue[] arguments)
         {
-            var strict = _strict || _engine._isStrict;
-            using (new StrictModeScope(strict, true))
+            // ** PrepareForOrdinaryCall **
+            // var callerContext = _engine.ExecutionContext;
+            // Let calleeRealm be F.[[Realm]].
+            // Set the Realm of calleeContext to calleeRealm.
+            // Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]].
+            var localEnv = LexicalEnvironment.NewFunctionEnvironment(_engine, this, Undefined);
+            // If callerContext is not already suspended, suspend callerContext.
+            // Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
+            // NOTE: Any exception objects produced after this point are associated with calleeRealm.
+            // Return calleeContext.
+
+            _engine.EnterExecutionContext(localEnv, localEnv);
+
+            // ** OrdinaryCallBindThis **
+            
+            JsValue thisValue;
+            if (_thisMode == FunctionThisMode.Strict)
             {
-                // setup new execution context http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.3
-                JsValue thisBinding;
-                if (StrictModeScope.IsStrictModeCode)
-                {
-                    thisBinding = thisArg;
-                }
-                else if (thisArg.IsNullOrUndefined())
-                {
-                    thisBinding = _engine.Global;
-                }
-                else if (!thisArg.IsObject())
+                thisValue = thisArgument;
+            }
+            else
+            {
+                if (thisArgument.IsNullOrUndefined())
                 {
-                    thisBinding = TypeConverter.ToObject(_engine, thisArg);
+                    var globalEnv = _engine.GlobalEnvironment;
+                    var globalEnvRec = (GlobalEnvironmentRecord) globalEnv._record;
+                    thisValue = globalEnvRec.GlobalThisValue;
                 }
                 else
                 {
-                    thisBinding = thisArg;
+                    thisValue = TypeConverter.ToObject(_engine, thisArgument);
                 }
+            }
 
-                var localEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _scope);
-
-                _engine.EnterExecutionContext(localEnv, localEnv, thisBinding);
+            var envRec = (FunctionEnvironmentRecord) localEnv._record;
+            envRec.BindThisValue(thisValue);
+            
+            // actual call
 
+            var strict = _thisMode == FunctionThisMode.Strict || _engine._isStrict;
+            using (new StrictModeScope(strict, true))
+            {
                 try
                 {
-                    var argumentsInstance = _engine.DeclarationBindingInstantiation(
-                        DeclarationBindingType.FunctionCode,
-                        _function._hoistingScope,
+                    var argumentsInstance = _engine.FunctionDeclarationInstantiation(
                         functionInstance: this,
-                        arguments);
-
-                    var result = _function._body.Execute();
+                        arguments,
+                        localEnv);
 
+                    var result = _function.Body.Execute();
                     var value = result.GetValueOrDefault().Clone();
-
                     argumentsInstance?.FunctionWasCalled();
 
                     if (result.Type == CompletionType.Throw)

+ 2 - 1
Jint/Native/Function/ThrowTypeError.cs

@@ -8,9 +8,10 @@ namespace Jint.Native.Function
         private static readonly JsString _functionName = new JsString("throwTypeError");
 
         public ThrowTypeError(Engine engine)
-            : base(engine, _functionName, System.Array.Empty<string>(), engine.GlobalEnvironment, false)
+            : base(engine, _functionName)
         {
             _length = PropertyDescriptor.AllForbiddenDescriptor.NumberZero;
+            _environment = engine.GlobalEnvironment;
             PreventExtensions();
         }
 

+ 8 - 8
Jint/Native/Global/GlobalObject.cs

@@ -707,14 +707,14 @@ namespace Jint.Native.Global
         
         // optimized versions with string parameter and without virtual dispatch for global environment usage
 
-        internal bool HasProperty(in Key property)
+        internal bool HasProperty(Key property)
         {
             return GetOwnProperty(property) != PropertyDescriptor.Undefined;
         }
 
-        internal PropertyDescriptor GetProperty(in Key property) => GetOwnProperty(property);
+        internal PropertyDescriptor GetProperty(Key property) => GetOwnProperty(property);
 
-        internal bool DefinePropertyOrThrow(in Key property, PropertyDescriptor desc)
+        internal bool DefinePropertyOrThrow(Key property, PropertyDescriptor desc)
         {
             if (!DefineOwnProperty(property, desc))
             {
@@ -724,7 +724,7 @@ namespace Jint.Native.Global
             return true;
         }
 
-        internal bool DefineOwnProperty(in Key property, PropertyDescriptor desc)
+        internal bool DefineOwnProperty(Key property, PropertyDescriptor desc)
         {
             var current = GetOwnProperty(property);
             if (current == desc)
@@ -743,13 +743,13 @@ namespace Jint.Native.Global
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal PropertyDescriptor GetOwnProperty(in Key property)
+        internal PropertyDescriptor GetOwnProperty(Key property)
         {
             Properties.TryGetValue(property, out var descriptor);
             return descriptor ?? PropertyDescriptor.Undefined;
         }
         
-        internal bool Set(in Key property, JsValue value)
+        internal bool Set(Key property, JsValue value)
         {
             // here we are called only from global environment record context
             // we can take some shortcuts to be faster
@@ -789,9 +789,9 @@ namespace Jint.Native.Global
         }
         
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal void SetOwnProperty(in Key property, PropertyDescriptor desc)
+        internal void SetOwnProperty(Key property, PropertyDescriptor desc)
         {
-            SetProperty(in property, desc);
+            SetProperty(property, desc);
         }
     }
 }

+ 1 - 1
Jint/Native/Iterator/IteratorConstructor.cs

@@ -12,7 +12,7 @@ namespace Jint.Native.Iterator
         private static readonly JsString _functionName = new JsString("iterator");
 
         private IteratorConstructor(Engine engine)
-            : base(engine, _functionName, strict: false)
+            : base(engine, _functionName)
         {
         }
 

+ 4 - 3
Jint/Native/Iterator/IteratorInstance.cs

@@ -312,7 +312,8 @@ namespace Jint.Native.Iterator
             {
                 result = IteratorNext();
 
-                if (result.TryGetValue(CommonProperties.Done, out var done) && TypeConverter.ToBoolean(done))
+                var done = result.Get(CommonProperties.Done);
+                if (!done.IsUndefined() && TypeConverter.ToBoolean(done))
                 {
                     return false;
                 }
@@ -322,8 +323,8 @@ namespace Jint.Native.Iterator
 
             private ObjectInstance IteratorNext()
             {
-                return _nextMethod.Call(_target, Arguments.Empty) as ObjectInstance
-                       ?? ExceptionHelper.ThrowTypeError<ObjectInstance>(_target.Engine);
+                var jsValue = _nextMethod.Call(_target, Arguments.Empty);
+                return jsValue as ObjectInstance ?? ExceptionHelper.ThrowTypeError<ObjectInstance>(_target.Engine, "Iterator result " + jsValue + " is not an object");
             }
 
             public void Close(CompletionType completion)

+ 1 - 1
Jint/Native/JsValue.cs

@@ -428,7 +428,7 @@ namespace Jint.Native
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver
+        /// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver
         /// </summary>
         public virtual bool Set(JsValue property, JsValue value, JsValue receiver)
         {

+ 4 - 4
Jint/Native/Json/JsonParser.cs

@@ -548,7 +548,7 @@ namespace Jint.Native.Json
             }
         }
 
-        private T MarkEnd<T>(T node) where T : INode
+        private T MarkEnd<T>(T node) where T : Node
         {
             if (_extra.Range != null)
             {
@@ -569,9 +569,9 @@ namespace Jint.Native.Json
             return node;
         }
 
-        public T MarkEndIf<T>(T node) where T : INode
+        public T MarkEndIf<T>(T node) where T : Node
         {
-            if (node.Range != null || node.Location != null)
+            if (node.Range != default || node.Location != default)
             {
                 if (_extra.Loc.HasValue)
                 {
@@ -590,7 +590,7 @@ namespace Jint.Native.Json
             return node;
         }
 
-        public INode PostProcess(INode node)
+        public Node PostProcess(Node node)
         {
             //if (_extra.Source != null)
             //{

+ 1 - 1
Jint/Native/Map/MapConstructor.cs

@@ -15,7 +15,7 @@ namespace Jint.Native.Map
         private static readonly JsString _functionName = new JsString("Map");
 
         private MapConstructor(Engine engine)
-            : base(engine, _functionName, strict: false)
+            : base(engine, _functionName)
         {
         }
 

+ 1 - 1
Jint/Native/Number/NumberConstructor.cs

@@ -16,7 +16,7 @@ namespace Jint.Native.Number
         internal const long MaxSafeInteger = 9007199254740991;
 
         public NumberConstructor(Engine engine)
-            : base(engine, _functionName, strict: false)
+            : base(engine, _functionName)
         {
 
         }

+ 3 - 1
Jint/Native/Object/ObjectConstructor.cs

@@ -10,8 +10,10 @@ namespace Jint.Native.Object
 {
     public sealed class ObjectConstructor : FunctionInstance, IConstructor
     {
+        private static readonly JsString _name = new JsString("delegate");
+
         private ObjectConstructor(Engine engine)
-            : base(engine, "Object", null, null, false)
+            : base(engine, _name)
         {
         }
 

+ 7 - 7
Jint/Native/Object/ObjectInstance.cs

@@ -153,11 +153,11 @@ namespace Jint.Native.Object
         internal void SetProperty(string property, PropertyDescriptor value)
         {
             Key key = property;
-            SetProperty(in key, value);
+            SetProperty(key, value);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal void SetProperty(in Key property, PropertyDescriptor value)
+        internal void SetProperty(Key property, PropertyDescriptor value)
         {
             _properties ??= new PropertyDictionary();
             _properties[property] = value;
@@ -565,7 +565,7 @@ namespace Jint.Native.Object
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
+        /// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
         /// </summary>
         public virtual bool HasProperty(JsValue property)
         {
@@ -585,7 +585,7 @@ namespace Jint.Native.Object
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-deletepropertyorthrow
+        /// https://tc39.es/ecma262/#sec-deletepropertyorthrow
         /// </summary>
         public bool DeletePropertyOrThrow(JsValue property)
         {
@@ -648,7 +648,7 @@ namespace Jint.Native.Object
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-validateandapplypropertydescriptor
+        /// https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
         /// </summary>
         protected static bool ValidateAndApplyPropertyDescriptor(ObjectInstance o, JsValue property, bool extensible, PropertyDescriptor desc, PropertyDescriptor current)
         {
@@ -1159,7 +1159,7 @@ namespace Jint.Native.Object
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-createdatapropertyorthrow
+        /// https://tc39.es/ecma262/#sec-createdatapropertyorthrow
         /// </summary>
         internal bool CreateDataProperty(JsValue p, JsValue v)
         {
@@ -1168,7 +1168,7 @@ namespace Jint.Native.Object
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-createdataproperty
+        /// https://tc39.es/ecma262/#sec-createdataproperty
         /// </summary>
         internal bool CreateDataPropertyOrThrow(JsValue p, JsValue v)
         {

+ 1 - 1
Jint/Native/Proxy/ProxyConstructor.cs

@@ -14,7 +14,7 @@ namespace Jint.Native.Proxy
         private static readonly JsString PropertyRevoke = new JsString("revoke");
 
         private ProxyConstructor(Engine engine)
-            : base(engine, _name, strict: false)
+            : base(engine, _name)
         {
         }
 

+ 1 - 1
Jint/Native/Proxy/ProxyInstance.cs

@@ -32,7 +32,7 @@ namespace Jint.Native.Proxy
             Engine engine,
             ObjectInstance target,
             ObjectInstance handler)
-            : base(engine, JsString.Empty, false, target.Class)
+            : base(engine, JsString.Empty, FunctionThisMode.Global, target.Class)
         {
             _target = target;
             _handler = handler;

+ 2 - 2
Jint/Native/RegExp/RegExpConstructor.cs

@@ -17,7 +17,7 @@ namespace Jint.Native.RegExp
         private static readonly JsString _functionName = new JsString("RegExp");
 
         public RegExpConstructor(Engine engine)
-            : base(engine, _functionName, strict: false)
+            : base(engine, _functionName, FunctionThisMode.Global)
         {
         }
 
@@ -59,7 +59,7 @@ namespace Jint.Native.RegExp
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-regexp-pattern-flags
+        /// https://tc39.es/ecma262/#sec-regexp-pattern-flags
         /// </summary>
         public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {

+ 2 - 2
Jint/Native/RegExp/RegExpPrototype.cs

@@ -115,7 +115,7 @@ namespace Jint.Native.RegExp
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-regexp.prototype-@@replace
+        /// https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
         /// </summary>
         private JsValue Replace(JsValue thisObj, JsValue[] arguments)
         {
@@ -380,7 +380,7 @@ namespace Jint.Native.RegExp
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-regexp.prototype-@@split
+        /// https://tc39.es/ecma262/#sec-regexp.prototype-@@split
         /// </summary>
         private JsValue Split(JsValue thisObj, JsValue[] arguments)
         {

+ 1 - 1
Jint/Native/Set/SetConstructor.cs

@@ -14,7 +14,7 @@ namespace Jint.Native.Set
         private static readonly JsString _functionName = new JsString("Set");
 
         private SetConstructor(Engine engine)
-            : base(engine, _functionName, false)
+            : base(engine, _functionName, FunctionThisMode.Global)
         {
         }
 

+ 3 - 3
Jint/Native/String/StringConstructor.cs

@@ -16,7 +16,7 @@ namespace Jint.Native.String
         private static readonly JsString _functionName = new JsString("String");
 
         public StringConstructor(Engine engine)
-            : base(engine, _functionName, strict: false)
+            : base(engine, _functionName, FunctionThisMode.Global)
         {
         }
 
@@ -60,7 +60,7 @@ namespace Jint.Native.String
             return JsString.Create(new string(chars));
         }
 
-        private static JsValue FromCodePoint(JsValue thisObj, JsValue[] arguments)
+        private JsValue FromCodePoint(JsValue thisObj, JsValue[] arguments)
         {
             var codeUnits = new List<JsValue>();
             string result = "";
@@ -73,7 +73,7 @@ namespace Jint.Native.String
                     || double.IsNaN(codePoint)
                     || TypeConverter.ToInt32(codePoint) != codePoint)
                 {
-                    return ExceptionHelper.ThrowRangeErrorNoEngine<JsValue>("Invalid code point " + codePoint);
+                    return ExceptionHelper.ThrowRangeError<JsValue>(_engine, "Invalid code point " + codePoint);
                 }
 
                 var point = (uint) codePoint;

+ 1 - 1
Jint/Native/Symbol/SymbolConstructor.cs

@@ -16,7 +16,7 @@ namespace Jint.Native.Symbol
         private static readonly JsString _functionName = new JsString("Symbol");
 
         public SymbolConstructor(Engine engine)
-            : base(engine, _functionName, strict: false)
+            : base(engine, _functionName, FunctionThisMode.Global)
         {
         }
 

+ 8 - 6
Jint/Pooling/ArgumentsInstancePool.cs

@@ -29,15 +29,17 @@ namespace Jint.Pooling
             };
         }
 
+        public ArgumentsInstance Rent(JsValue[] argumentsList) => Rent(null, null, argumentsList, null, false);
+
         public ArgumentsInstance Rent(
-            FunctionInstance func, 
-            string[] names, 
-            JsValue[] args, 
-            EnvironmentRecord env, 
-            bool strict)
+            FunctionInstance func,
+            Key[] formals,
+            JsValue[] argumentsList,
+            DeclarativeEnvironmentRecord env, 
+            bool hasRestParameter)
         {
             var obj = _pool.Allocate();
-            obj.Prepare(func, names, args, env, strict);
+            obj.Prepare(func, formals, argumentsList, env, hasRestParameter);
             return obj;
         }
 

+ 4 - 4
Jint/Runtime/Debugger/DebugHandler.cs

@@ -101,8 +101,8 @@ namespace Jint.Runtime.Debugger
             }
             else if (old == StepMode.Into && _stepMode == StepMode.Over)
             {
-                var expressionStatement = statement as ExpressionStatement;
-                if (expressionStatement != null && expressionStatement.Expression is CallExpression)
+                if (statement is ExpressionStatement expressionStatement 
+                    && expressionStatement.Expression is CallExpression)
                 {
                     _callBackStepOverDepth = _debugCallStack.Count;
                 }
@@ -152,9 +152,9 @@ namespace Jint.Runtime.Debugger
                 CurrentMemoryUsage = _engine.CurrentMemoryUsage
             };
 
-            if (_engine.ExecutionContext.LexicalEnvironment != null)
+            if (_engine.ExecutionContext.VariableEnvironment != null)
             {
-                var lexicalEnvironment = _engine.ExecutionContext.LexicalEnvironment;
+                var lexicalEnvironment = _engine.ExecutionContext.VariableEnvironment;
                 info.Locals = GetLocalVariables(lexicalEnvironment);
                 info.Globals = GetGlobalVariables(lexicalEnvironment);
             }

+ 5 - 5
Jint/Runtime/Descriptors/Specialized/ClrAccessDescriptor.cs

@@ -6,22 +6,22 @@ namespace Jint.Runtime.Descriptors.Specialized
 {
     internal sealed class ClrAccessDescriptor : PropertyDescriptor
     {
-        private readonly EnvironmentRecord _env;
+        private readonly DeclarativeEnvironmentRecord _env;
         private readonly Engine _engine;
-        private readonly Key _name;
+        private readonly EnvironmentRecord.BindingName _name;
 
         private GetterFunctionInstance _get;
         private SetterFunctionInstance _set;
 
         public ClrAccessDescriptor(
-            EnvironmentRecord env,
+            DeclarativeEnvironmentRecord env,
             Engine engine,
             string name)
             : base(value: null, PropertyFlag.Configurable)
         {
             _env = env;
             _engine = engine;
-            _name = name;
+            _name = new EnvironmentRecord.BindingName(name);
         }
 
         public override JsValue Get => _get ??= new GetterFunctionInstance(_engine, DoGet);
@@ -36,7 +36,7 @@ namespace Jint.Runtime.Descriptors.Specialized
 
         private void DoSet(JsValue n, JsValue o)
         {
-            _env.SetMutableBinding(_name, o, true);
+            _env.SetMutableBinding(_name.Key.Name, o, true);
         }
     }
 }

+ 8 - 2
Jint/Runtime/Environments/Binding.cs

@@ -4,20 +4,26 @@ namespace Jint.Runtime.Environments
 {
     public readonly struct Binding
     {
-        public Binding(JsValue value, bool canBeDeleted, bool mutable)
+        public Binding(
+            JsValue value,
+            bool canBeDeleted,
+            bool mutable,
+            bool strict)
         {
             Value = value;
             CanBeDeleted = canBeDeleted;
             Mutable = mutable;
+            Strict = strict;
         }
 
         public readonly JsValue Value;
         public readonly bool CanBeDeleted;
         public readonly bool Mutable;
+        public readonly bool Strict;
 
         public Binding ChangeValue(JsValue argument)
         {
-            return new Binding(argument, CanBeDeleted, Mutable);
+            return new Binding(argument, CanBeDeleted, Mutable, Strict);
         }
 
         public bool IsInitialized() => !(Value is null);

+ 80 - 362
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -1,13 +1,6 @@
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using Esprima.Ast;
+using System.Runtime.CompilerServices;
 using Jint.Collections;
 using Jint.Native;
-using Jint.Native.Argument;
-using Jint.Native.Array;
-using Jint.Native.Function;
-using Jint.Native.Iterator;
-using Jint.Runtime.Interpreter.Expressions;
 
 namespace Jint.Runtime.Environments
 {
@@ -15,65 +8,107 @@ namespace Jint.Runtime.Environments
     /// Represents a declarative environment record
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1.1
     /// </summary>
-    internal sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
+    internal class DeclarativeEnvironmentRecord : EnvironmentRecord
     {
-        private readonly HybridDictionary<Binding> _dictionary = new HybridDictionary<Binding>();
+        internal readonly HybridDictionary<Binding> _dictionary = new HybridDictionary<Binding>();
+        internal bool _hasBindings;
 
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         {
         }
 
-        private bool ContainsKey(in Key key)
+        public sealed override bool HasBinding(string name)
         {
-            return _dictionary.ContainsKey(key);
+            return _dictionary.ContainsKey(name);
         }
 
-        private bool TryGetValue(in Key key, out Binding value)
+        internal sealed override bool TryGetBinding(
+            BindingName name,
+            bool strict,
+            out Binding binding,
+            out JsValue value)
         {
-            value = default;
-            return _dictionary.TryGetValue(key, out value);
+            binding = default;
+            var success = _dictionary.TryGetValue(name.Key, out binding);
+            value = success && binding.IsInitialized() ? UnwrapBindingValue(strict, binding) : default;
+            return success;
         }
 
-        public override bool HasBinding(string name)
+        internal void CreateMutableBindingAndInitialize(Key name, bool canBeDeleted, JsValue value)
         {
-            return ContainsKey(name);
+            _hasBindings = true;
+            _dictionary[name] = new Binding(value, canBeDeleted, mutable: true, strict: false);
         }
 
-        internal override bool TryGetBinding(
-            in Key name,
-            bool strict,
-            out Binding binding,
-            out JsValue value)
+        internal void CreateImmutableBindingAndInitialize(Key name, bool strict, JsValue value)
         {
-            binding = default;
-            var success = _dictionary.TryGetValue(name, out binding);
-            value = success ? UnwrapBindingValue(strict, binding) : default;
-            return success;
+            _hasBindings = true;
+            _dictionary[name] = new Binding(value, canBeDeleted: false, mutable: false, strict);
         }
 
-        public override void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false)
+        public sealed override void CreateMutableBinding(string name, bool canBeDeleted = false)
         {
-            _dictionary[name] = new Binding(value, canBeDeleted, mutable: true);
+            _hasBindings = true;
+            _dictionary[name] = new Binding(null, canBeDeleted, mutable: true, strict: false);
+        }
+        
+        public sealed override void CreateImmutableBinding(string name, bool strict = true)
+        {
+            _hasBindings = true;
+            _dictionary[name] = new Binding(null, canBeDeleted: false, mutable: false, strict);
+        }
+
+        public sealed override void InitializeBinding(string name, JsValue value)
+        {
+            _hasBindings = true;
+            _dictionary.TryGetValue(name, out var binding);
+            _dictionary[name] = binding.ChangeValue(value);
         }
 
-        public override void SetMutableBinding(string name, JsValue value, bool strict)
+        internal override void SetMutableBinding(BindingName name, JsValue value, bool strict)
         {
-            var key = name;
-            _dictionary.TryGetValue(key, out var binding);
+            SetMutableBinding(name.Key.Name, value, strict);
+        }
+
+        public sealed override void SetMutableBinding(string name, JsValue value, bool strict)
+        {
+            var key = (Key) name;
+            if (!_dictionary.TryGetValue(key, out var binding))
+            {
+                if (strict)
+                {
+                    ExceptionHelper.ThrowReferenceError(_engine, key);
+                }
+                CreateMutableBindingAndInitialize(key, canBeDeleted: true, value);
+                return;
+            }
+
+            if (binding.Strict)
+            {
+                strict = true;
+            }
+
+            // Is it an uninitialized binding?
+            if (!binding.IsInitialized())
+            {
+                ExceptionHelper.ThrowReferenceError<object>(_engine, message: "Cannot access '" +  key + "' before initialization");
+            }
+            
             if (binding.Mutable)
             {
+                _hasBindings = true;
                 _dictionary[key] = binding.ChangeValue(value);
             }
             else
             {
                 if (strict)
                 {
-                    ExceptionHelper.ThrowTypeError(_engine, "Can't update the value of an immutable binding.");
+                    ExceptionHelper.ThrowTypeError(_engine, "Assignment to constant variable.");
                 }
             }
         }
 
-        public override JsValue GetBindingValue(string name, bool strict)
+        public sealed override JsValue GetBindingValue(string name, bool strict)
         {
             _dictionary.TryGetValue(name, out var binding);
             return UnwrapBindingValue(strict, binding);
@@ -100,7 +135,7 @@ namespace Jint.Runtime.Environments
             throw new JavaScriptException(_engine.ReferenceError, "Can't access an uninitialized immutable binding.");
         }
 
-        public override bool DeleteBinding(string name)
+        public sealed override bool DeleteBinding(string name)
         {
             if (!_dictionary.TryGetValue(name, out var binding))
             {
@@ -113,17 +148,24 @@ namespace Jint.Runtime.Environments
             }
 
             _dictionary.Remove(name);
+            _hasBindings = _dictionary.Count > 0;
 
             return true;
         }
+        
+        public override bool HasThisBinding() => false;
 
-        public override JsValue ImplicitThisValue()
+        public override bool HasSuperBinding() => false;
+
+        public override JsValue WithBaseObject() => Undefined;
+
+        public sealed override JsValue ImplicitThisValue()
         {
             return Undefined;
         }
 
         /// <inheritdoc />
-        internal override string[] GetAllBindingNames()
+        internal sealed override string[] GetAllBindingNames()
         {
             if (_dictionary is null)
             {
@@ -140,333 +182,9 @@ namespace Jint.Runtime.Environments
             return keys;
         }
 
-        internal void AddFunctionParameters(
-            JsValue[] arguments,
-            ArgumentsInstance argumentsInstance,
-            IFunction functionDeclaration)
-        {
-            bool empty = _dictionary.Count == 0;
-            if (!(argumentsInstance is null))
-            {
-                _dictionary[KnownKeys.Arguments] = new Binding(argumentsInstance, canBeDeleted: false, mutable: true);
-            }
-
-            ref readonly var parameters = ref functionDeclaration.Params;
-            var count = parameters.Count;
-            for (var i = 0; i < count; i++)
-            {
-                SetFunctionParameter(parameters[i], arguments, i, empty);
-            }
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private void SetFunctionParameter(
-            INode parameter,
-            JsValue[] arguments,
-            int index,
-            bool initiallyEmpty)
-        {
-            if (parameter is Identifier identifier)
-            {
-                var argument = (uint) index < (uint) arguments.Length ? arguments[index] : Undefined;
-                SetItemSafely(identifier.Name, argument, initiallyEmpty);
-            }
-            else
-            {
-                SetFunctionParameterUnlikely(parameter, arguments, index, initiallyEmpty);
-            }
-        }
-
-        private void SetFunctionParameterUnlikely(
-            INode parameter,
-            JsValue[] arguments,
-            int index,
-            bool initiallyEmpty)
-        {
-            var argument = arguments.Length > index ? arguments[index] : Undefined;
-
-            if (parameter is RestElement restElement)
-            {
-                HandleRestElementArray(restElement, arguments, index, initiallyEmpty);
-            }
-            else if (parameter is ArrayPattern arrayPattern)
-            {
-                if (argument.IsNull())
-                {
-                    ExceptionHelper.ThrowTypeError(_engine, "Destructed parameter is null");
-                }
-
-                ArrayInstance array = null;
-                var arrayContents = System.Array.Empty<JsValue>();
-                if (argument.IsArray())
-                {
-                    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.GetLength()];
-
-                    for (uint i = 0; i < (uint) arrayContents.Length; i++)
-                    {
-                        arrayContents[i] = array.Get(i);
-                    }
-                }
-
-                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 processedProperties = objectPattern.Properties.Count > 0 && objectPattern.Properties[objectPattern.Properties.Count - 1] is RestElement
-                    ? new HashSet<JsValue>()
-                    : null;
-
-                var jsValues = _engine._jsValueArrayPool.RentArray(1);
-                foreach (var property in objectPattern.Properties)
-                {
-                    if (property is Property p)
-                    {
-                        JsString propertyName;
-                        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 = JintExpression.Build(_engine, callExpression);
-                            propertyName = (JsString) jintCallExpression.GetValue();
-                        }
-                        else
-                        {
-                            propertyName = ExceptionHelper.ThrowArgumentOutOfRangeException<JsString>("property", "unknown object pattern property type");
-                        }
-
-                        processedProperties?.Add(propertyName.AsStringWithoutTypeCheck());
-                        jsValues[0] = argumentObject.Get(propertyName);
-                        SetFunctionParameter(p.Value, jsValues, 0, initiallyEmpty);
-                    }
-                    else
-                    {
-                        if (((RestElement) property).Argument is Identifier restIdentifier)
-                        {
-                            var rest = _engine.Object.Construct(argumentObject.Properties.Count - processedProperties.Count);
-                            argumentObject.CopyDataProperties(rest, processedProperties);
-                            SetItemSafely(restIdentifier.Name, rest, initiallyEmpty);
-                        }
-                        else
-                        {
-                            ExceptionHelper.ThrowSyntaxError(_engine, "Object rest parameter can only be objects");
-                        }
-                    }
-                }
-                _engine._jsValueArrayPool.ReturnArray(jsValues);
-            }
-            else if (parameter is AssignmentPattern assignmentPattern)
-            {
-                HandleAssignmentPatternOrExpression(assignmentPattern.Left, assignmentPattern.Right, argument, initiallyEmpty);
-            }
-            else if (parameter is AssignmentExpression assignmentExpression)
-            {
-                HandleAssignmentPatternOrExpression(assignmentExpression.Left, assignmentExpression.Right, argument, initiallyEmpty);
-            }
-        }
-
-        private void HandleRestElementArray(
-            RestElement restElement,
-            JsValue[] arguments,
-            int index,
-            bool initiallyEmpty)
-        {
-            // 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);
-            }
-
-            if (restElement.Argument is Identifier restIdentifier)
-            {
-                SetItemSafely(restIdentifier.Name, rest, initiallyEmpty);
-            }
-            else if (restElement.Argument is BindingPattern bindingPattern)
-            {
-                SetFunctionParameter(bindingPattern, new JsValue[]
-                {
-                    rest
-                }, index, initiallyEmpty);
-            }
-            else
-            {
-                ExceptionHelper.ThrowSyntaxError(_engine, "Rest parameters can only be identifiers or arrays");
-            }
-        }
-
-        private void HandleAssignmentPatternOrExpression(
-            INode left,
-            INode right,
-            JsValue argument,
-            bool initiallyEmpty)
-        {
-            var idLeft = left as Identifier;
-            if (idLeft != null
-                && right is Identifier idRight
-                && idLeft.Name == idRight.Name)
-            {
-                ExceptionHelper.ThrowReferenceError(_engine, idRight.Name);
-            }
-
-            if (argument.IsUndefined())
-            {
-                JsValue RunInNewParameterEnvironment(JintExpression exp)
-                {
-                    var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
-                    var paramVarEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
-
-                    _engine.EnterExecutionContext(paramVarEnv, paramVarEnv, _engine.ExecutionContext.ThisBinding);
-                    var result = exp.GetValue();
-                    _engine.LeaveExecutionContext();
-
-                    return result;
-                }
-
-                var expression = right.As<Expression>();
-                var jintExpression = JintExpression.Build(_engine, expression);
-
-                argument = jintExpression is JintSequenceExpression
-                    ? RunInNewParameterEnvironment(jintExpression)
-                    : jintExpression.GetValue();
-
-                if (idLeft != null && right.IsFunctionWithName())
-                {
-                    ((FunctionInstance) argument).SetFunctionName(idLeft.Name);
-                }
-            }
-
-            SetFunctionParameter(left, new[]
-            {
-                argument
-            }, 0, initiallyEmpty);
-        }
-
-        private void SetItemSafely(in Key name, JsValue argument, bool initiallyEmpty)
-        {
-            if (initiallyEmpty || !TryGetValue(name, out var existing))
-            {
-                _dictionary[name] = new Binding(argument, false, true);
-            }
-            else
-            {
-                if (existing.Mutable)
-                {
-                    _dictionary[name] = existing.ChangeValue(argument);
-                }
-                else
-                {
-                    ExceptionHelper.ThrowTypeError(_engine, "Can't update the value of an immutable binding.");
-                }
-            }
-        }
-
-        internal void AddVariableDeclarations(ref NodeList<VariableDeclaration> variableDeclarations)
-        {
-            var variableDeclarationsCount = variableDeclarations.Count;
-            for (var i = 0; i < variableDeclarationsCount; i++)
-            {
-                var variableDeclaration = variableDeclarations[i];
-                var declarationsCount = variableDeclaration.Declarations.Count;
-                for (var j = 0; j < declarationsCount; j++)
-                {
-                    var d = variableDeclaration.Declarations[j];
-                    if (d.Id is Identifier id)
-                    {
-                        Key dn = id.Name;
-                        if (!ContainsKey(dn))
-                        {
-                            var binding = new Binding(Undefined, canBeDeleted: false, mutable: true);
-                            _dictionary[dn] = binding;
-                        }
-                    }
-                }
-            }
-        }
-
-        internal static JsValue HandleAssignmentPatternIfNeeded(IFunction functionDeclaration, JsValue jsValue, uint index)
-        {
-            // TODO remove this method, overwrite with above SetFunctionParameter logic
-            if (jsValue.IsUndefined()
-                && index < functionDeclaration?.Params.Count
-                && functionDeclaration.Params[(int) index] is AssignmentPattern ap
-                && ap.Right is Literal l)
-            {
-                return JintLiteralExpression.ConvertToJsValue(l);
-            }
-
-            return jsValue;
-        }
-
-        private sealed class ArrayPatternProtocol : IteratorProtocol
+        public override JsValue GetThisBinding()
         {
-            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);
-                    IteratorClose(CompletionType.Normal);
-                }
-            }
+            throw new System.NotImplementedException();
         }
     }
 }

+ 42 - 4
Jint/Runtime/Environments/EnvironmentRecord.cs

@@ -23,7 +23,7 @@ namespace Jint.Runtime.Environments
         public abstract bool HasBinding(string name);
 
         internal abstract bool TryGetBinding(
-            in Key name,
+            BindingName name,
             bool strict,
             out Binding binding,
             out JsValue value);
@@ -32,9 +32,22 @@ namespace Jint.Runtime.Environments
         /// Creates a new mutable binding in an environment record.
         /// </summary>
         /// <param name="name">The identifier of the binding.</param>
-        /// <param name="value">The value of the binding.</param>
         /// <param name="canBeDeleted"><c>true</c> if the binding may be subsequently deleted.</param>
-        public abstract void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false);
+        public abstract void CreateMutableBinding(string name, bool canBeDeleted = false);
+
+        /// <summary>
+        /// Creates a new but uninitialized immutable binding in an environment record.
+        /// </summary>
+        /// <param name="name">The identifier of the binding.</param>
+        /// <param name="strict"><c>false</c> if the binding may used before it's been initialized.</param>
+        public abstract void CreateImmutableBinding(string name, bool strict = true);
+
+        /// <summary>
+        /// Set the value of an already existing but uninitialized binding in an Environment Record.
+        /// </summary>
+        /// <param name="name">The text of the bound name</param>
+        /// <param name="value">The value for the binding.</param>
+        public abstract void InitializeBinding(string name, JsValue value);
 
         /// <summary>
         /// Sets the value of an already existing mutable binding in an environment record.
@@ -44,6 +57,8 @@ namespace Jint.Runtime.Environments
         /// <param name="strict">The identify strict mode references.</param>
         public abstract void SetMutableBinding(string name, JsValue value, bool strict);
 
+        internal abstract void SetMutableBinding(BindingName name, JsValue value, bool strict);
+
         /// <summary>
         /// Returns the value of an already existing binding from an environment record.
         /// </summary>
@@ -59,6 +74,12 @@ namespace Jint.Runtime.Environments
         /// <returns><true>true</true> if the deletion is successfull.</returns>
         public abstract bool DeleteBinding(string name);
 
+        public abstract bool HasThisBinding();
+
+        public abstract bool HasSuperBinding();
+
+        public abstract JsValue WithBaseObject();
+
         /// <summary>
         /// Returns the value to use as the <c>this</c> value on calls to function objects that are obtained as binding values from this environment record.
         /// </summary>
@@ -70,7 +91,7 @@ namespace Jint.Runtime.Environments
         /// </summary>
         /// <returns>The array of all defined bindings</returns>
         internal abstract string[] GetAllBindingNames();
-        
+
         public override object ToObject()
         {
             return ExceptionHelper.ThrowNotSupportedException<object>();
@@ -80,6 +101,23 @@ namespace Jint.Runtime.Environments
         {
             return ExceptionHelper.ThrowNotSupportedException<bool>();
         }
+
+        public abstract JsValue GetThisBinding();
+
+        /// <summary>
+        /// Helper to cache JsString/Key when environments use different lookups.
+        /// </summary>
+        internal class BindingName
+        {
+            public readonly Key Key;
+            public readonly JsString StringValue;
+
+            public BindingName(string value)
+            {
+                Key = (Key) value;
+                StringValue = JsString.Create(value);
+            }
+        }
     }
 }
 

+ 11 - 8
Jint/Runtime/Environments/ExecutionContext.cs

@@ -1,23 +1,26 @@
-using Jint.Native;
-
-namespace Jint.Runtime.Environments
+namespace Jint.Runtime.Environments
 {
     public readonly struct ExecutionContext
     {
-        public ExecutionContext(LexicalEnvironment lexicalEnvironment, LexicalEnvironment variableEnvironment, JsValue thisBinding)
+        public ExecutionContext(
+            LexicalEnvironment lexicalEnvironment,
+            LexicalEnvironment variableEnvironment)
         {
             LexicalEnvironment = lexicalEnvironment;
             VariableEnvironment = variableEnvironment;
-            ThisBinding = thisBinding;
         }
 
         public readonly LexicalEnvironment LexicalEnvironment;
         public readonly LexicalEnvironment VariableEnvironment;
-        public readonly JsValue ThisBinding;
 
-        public ExecutionContext UpdateLexicalEnvironment(LexicalEnvironment newEnv)
+        public ExecutionContext UpdateLexicalEnvironment(LexicalEnvironment lexicalEnvironment)
+        {
+            return new ExecutionContext(lexicalEnvironment, VariableEnvironment);
+        }
+
+        public ExecutionContext UpdateVariableEnvironment(LexicalEnvironment variableEnvironment)
         {
-            return new ExecutionContext(newEnv, VariableEnvironment, ThisBinding);
+            return new ExecutionContext(LexicalEnvironment, variableEnvironment);
         }
     }
 }

+ 430 - 0
Jint/Runtime/Environments/FunctionEnvironmentRecord.cs

@@ -0,0 +1,430 @@
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Native.Array;
+using Jint.Native.Function;
+using Jint.Native.Iterator;
+using Jint.Native.Object;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Runtime.Environments
+{
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-function-environment-records
+    /// </summary>
+    internal sealed class FunctionEnvironmentRecord : DeclarativeEnvironmentRecord
+    {
+        private enum ThisBindingStatus
+        {
+            Lexical,
+            Initialized,
+            Uninitialized
+        }
+
+        private JsValue _thisValue;
+        private ThisBindingStatus _thisBindingStatus;
+        private readonly FunctionInstance _functionObject;
+        private readonly JsValue _homeObject = Undefined;
+        private readonly JsValue _newTarget;
+
+        public FunctionEnvironmentRecord(
+            Engine engine, 
+            FunctionInstance functionObject,
+            JsValue newTarget) : base(engine)
+        {
+            _functionObject = functionObject;
+            _newTarget = newTarget;
+            if (functionObject is ArrowFunctionInstance)
+            {
+                _thisBindingStatus = ThisBindingStatus.Lexical;
+            }
+            else
+            {
+                _thisBindingStatus = ThisBindingStatus.Uninitialized;
+            }
+        }
+
+
+        public override bool HasThisBinding() => _thisBindingStatus != ThisBindingStatus.Lexical;
+
+        public override bool HasSuperBinding() => 
+            _thisBindingStatus != ThisBindingStatus.Lexical && !_homeObject.IsUndefined();
+
+        public override JsValue WithBaseObject()
+        {
+            return _thisBindingStatus == ThisBindingStatus.Uninitialized
+                ? ExceptionHelper.ThrowReferenceError<JsValue>(_engine)
+                : _thisValue;
+        }
+
+        public JsValue BindThisValue(JsValue value)
+        {
+            if (_thisBindingStatus == ThisBindingStatus.Initialized)
+            {
+                ExceptionHelper.ThrowReferenceError<JsValue>(_engine);
+            }
+            _thisValue = value;
+            _thisBindingStatus = ThisBindingStatus.Initialized;
+            return value;
+        }
+
+        public override JsValue GetThisBinding()
+        {
+            return _thisBindingStatus == ThisBindingStatus.Uninitialized 
+                ? ExceptionHelper.ThrowReferenceError<JsValue>(_engine)
+                : _thisValue;
+        }
+
+        public JsValue GetSuperBase()
+        {
+            return _homeObject.IsUndefined() 
+                ? Undefined
+                : ((ObjectInstance) _homeObject).Prototype;
+        }
+
+
+        // optimization to have logic near record internal structures.
+
+        internal void InitializeParameters(
+            Key[] parameterNames,
+            bool hasDuplicates, 
+            JsValue[] arguments)
+        {
+            var value = hasDuplicates ? Undefined : null;
+            var directSet = !hasDuplicates && _dictionary.Count == 0;
+            for (var i = 0; (uint) i < (uint) parameterNames.Length; i++)
+            {
+                var paramName = parameterNames[i];
+                if (directSet || !_dictionary.ContainsKey(paramName))
+                {
+                    var parameterValue = value;
+                    if (arguments != null)
+                    {
+                        parameterValue = (uint) i < (uint) arguments.Length ? arguments[i] : Undefined;
+                    }
+
+                    _dictionary[paramName] = new Binding(parameterValue, canBeDeleted: false, mutable: true, strict: false);
+                }
+            }
+        }
+        
+        internal void AddFunctionParameters(IFunction functionDeclaration, JsValue[] arguments)
+        {
+            bool empty = _dictionary.Count == 0;
+            ref readonly var parameters = ref functionDeclaration.Params;
+            var count = parameters.Count;
+            for (var i = 0; i < count; i++)
+            {
+                SetFunctionParameter(parameters[i], arguments, i, empty);
+            }
+        }
+        
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private void SetFunctionParameter(
+            Node parameter,
+            JsValue[] arguments,
+            int index,
+            bool initiallyEmpty)
+        {
+            if (parameter is Identifier identifier)
+            {
+                var argument = (uint) index < (uint) arguments.Length ? arguments[index] : Undefined;
+                SetItemSafely(identifier.Name, argument, initiallyEmpty);
+            }
+            else
+            {
+                SetFunctionParameterUnlikely(parameter, arguments, index, initiallyEmpty);
+            }
+        }
+
+        private void SetFunctionParameterUnlikely(
+            Node parameter,
+            JsValue[] arguments,
+            int index,
+            bool initiallyEmpty)
+        {
+            var argument = arguments.Length > index ? arguments[index] : Undefined;
+
+            if (parameter is RestElement restElement)
+            {
+                HandleRestElementArray(restElement, arguments, index, initiallyEmpty);
+            }
+            else if (parameter is ArrayPattern arrayPattern)
+            {
+                HandleArrayPattern(initiallyEmpty, argument, arrayPattern);
+            }
+            else if (parameter is ObjectPattern objectPattern)
+            {
+                HandleObjectPattern(initiallyEmpty, argument, objectPattern);
+            }
+            else if (parameter is AssignmentPattern assignmentPattern)
+            {
+                HandleAssignmentPatternOrExpression(assignmentPattern.Left, assignmentPattern.Right, argument, initiallyEmpty);
+            }
+            else if (parameter is AssignmentExpression assignmentExpression)
+            {
+                HandleAssignmentPatternOrExpression(assignmentExpression.Left, assignmentExpression.Right, argument, initiallyEmpty);
+            }
+        }
+
+        private void HandleObjectPattern(bool initiallyEmpty, JsValue argument, ObjectPattern objectPattern)
+        {
+            if (argument.IsNullOrUndefined())
+            {
+                ExceptionHelper.ThrowTypeError(_engine, "Destructed parameter is null or undefined");
+            }
+
+            if (!argument.IsObject())
+            {
+                return;
+            }
+
+            var argumentObject = argument.AsObject();
+
+            var processedProperties = objectPattern.Properties.Count > 0 &&
+                                      objectPattern.Properties[objectPattern.Properties.Count - 1] is RestElement
+                ? new HashSet<JsValue>()
+                : null;
+
+            var jsValues = _engine._jsValueArrayPool.RentArray(1);
+            foreach (var property in objectPattern.Properties)
+            {
+                var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
+                var paramVarEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
+                _engine.EnterExecutionContext(paramVarEnv, paramVarEnv);
+
+                try
+                {
+                    if (property is Property p)
+                    {
+                        JsString propertyName;
+                        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(_engine, callExpression);
+                            var jsValue = jintCallExpression.GetValue();
+                            propertyName = TypeConverter.ToJsString(jsValue);
+                        }
+                        else
+                        {
+                            propertyName = ExceptionHelper.ThrowArgumentOutOfRangeException<JsString>("property", "unknown object pattern property type");
+                        }
+
+                        processedProperties?.Add(propertyName.AsStringWithoutTypeCheck());
+                        jsValues[0] = argumentObject.Get(propertyName);
+                        SetFunctionParameter(p.Value, jsValues, 0, initiallyEmpty);
+                    }
+                    else
+                    {
+                        if (((RestElement) property).Argument is Identifier restIdentifier)
+                        {
+                            var rest = _engine.Object.Construct(argumentObject.Properties.Count - processedProperties.Count);
+                            argumentObject.CopyDataProperties(rest, processedProperties);
+                            SetItemSafely(restIdentifier.Name, rest, initiallyEmpty);
+                        }
+                        else
+                        {
+                            ExceptionHelper.ThrowSyntaxError(_engine, "Object rest parameter can only be objects");
+                        }
+                    }
+                }
+                finally
+                {
+                    _engine.LeaveExecutionContext();
+                }
+            }
+
+            _engine._jsValueArrayPool.ReturnArray(jsValues);
+        }
+
+        private void HandleArrayPattern(bool initiallyEmpty, JsValue argument, ArrayPattern arrayPattern)
+        {
+            if (argument.IsNull())
+            {
+                ExceptionHelper.ThrowTypeError(_engine, "Destructed parameter is null");
+            }
+
+            ArrayInstance array = null;
+            var arrayContents = System.Array.Empty<JsValue>();
+            if (argument.IsArray())
+            {
+                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.GetLength()];
+
+                for (uint i = 0; i < (uint) arrayContents.Length; i++)
+                {
+                    arrayContents[i] = array.Get(i);
+                }
+            }
+
+            for (uint arrayIndex = 0; arrayIndex < arrayPattern.Elements.Count; arrayIndex++)
+            {
+                SetFunctionParameter(arrayPattern.Elements[(int) arrayIndex], arrayContents, (int) arrayIndex, initiallyEmpty);
+            }
+        }
+
+        private void HandleRestElementArray(
+            RestElement restElement,
+            JsValue[] arguments,
+            int index,
+            bool initiallyEmpty)
+        {
+            // 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);
+            }
+
+            if (restElement.Argument is Identifier restIdentifier)
+            {
+                SetItemSafely(restIdentifier.Name, rest, initiallyEmpty);
+            }
+            else if (restElement.Argument is BindingPattern bindingPattern)
+            {
+                SetFunctionParameter(bindingPattern, new JsValue[]
+                {
+                    rest
+                }, index, initiallyEmpty);
+            }
+            else
+            {
+                ExceptionHelper.ThrowSyntaxError(_engine, "Rest parameters can only be identifiers or arrays");
+            }
+        }
+
+        private void HandleAssignmentPatternOrExpression(
+            Node left,
+            Node right,
+            JsValue argument,
+            bool initiallyEmpty)
+        {
+            var idLeft = left as Identifier;
+            if (idLeft != null
+                && right is Identifier idRight
+                && idLeft.Name == idRight.Name)
+            {
+                ExceptionHelper.ThrowReferenceError(_engine, idRight.Name);
+            }
+
+            if (argument.IsUndefined())
+            {
+                var expression = right.As<Expression>();
+                var jintExpression = JintExpression.Build(_engine, expression);
+
+                var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
+                var paramVarEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
+
+                _engine.EnterExecutionContext(paramVarEnv, paramVarEnv);
+                try
+                {
+                    argument = jintExpression.GetValue();
+                }
+                finally
+                {
+                    _engine.LeaveExecutionContext();
+                }
+
+                if (idLeft != null && right.IsFunctionWithName())
+                {
+                    ((FunctionInstance) argument).SetFunctionName(idLeft.Name);
+                }
+            }
+
+            SetFunctionParameter(left, new[]
+            {
+                argument
+            }, 0, initiallyEmpty);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private void SetItemSafely(Key name, JsValue argument, bool initiallyEmpty)
+        {
+            if (initiallyEmpty)
+            {
+                _dictionary[name] = new Binding(argument, canBeDeleted: false, mutable: true, strict: false);
+            }
+            else
+            {
+                SetItemCheckExisting(name, argument);
+            }
+        }
+        
+        private void SetItemCheckExisting(Key name, JsValue argument)
+        {
+            if (!_dictionary.TryGetValue(name, out var existing))
+            {
+                _dictionary[name] = new Binding(argument, canBeDeleted: false, mutable: true, strict: false);
+            }
+            else
+            {
+                if (existing.Mutable)
+                {
+                    _dictionary[name] = existing.ChangeValue(argument);
+                }
+                else
+                {
+                    ExceptionHelper.ThrowTypeError(_engine, "Can't update the value of an immutable binding.");
+                }
+            }
+        }
+
+        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);
+                    IteratorClose(CompletionType.Normal);
+                }
+            }
+        }
+    }
+}

+ 192 - 34
Jint/Runtime/Environments/GlobalEnvironmentRecord.cs

@@ -1,4 +1,5 @@
-using System.Linq;
+using System.Collections.Generic;
+using System.Linq;
 using Jint.Native;
 using Jint.Native.Global;
 using Jint.Native.Object;
@@ -7,74 +8,231 @@ using Jint.Runtime.Descriptors;
 namespace Jint.Runtime.Environments
 {
     /// <summary>
-    /// Optimized for GlobalObject, which we know of and can skip some virtual calls.
-    /// http://www.ecma-international.org/ecma-262/6.0/#sec-global-environment-records
+    ///     http://www.ecma-international.org/ecma-262/6.0/#sec-global-environment-records
     /// </summary>
     internal sealed class GlobalEnvironmentRecord : EnvironmentRecord
     {
-        private readonly GlobalObject _global;
+        private readonly DeclarativeEnvironmentRecord _declarativeRecord;
+        private readonly ObjectEnvironmentRecord _objectRecord;
+        private readonly HashSet<string> _varNames = new HashSet<string>();
 
         public GlobalEnvironmentRecord(Engine engine, GlobalObject global) : base(engine)
         {
-            _global = global;
+            _objectRecord = new ObjectEnvironmentRecord(engine, global, provideThis: false, withEnvironment: false);
+            _declarativeRecord = new DeclarativeEnvironmentRecord(engine);
         }
 
-        public override bool HasBinding(string name) => _global.Properties.ContainsKey(name);
+        public ObjectInstance GlobalThisValue => _objectRecord._bindingObject;
+
+        public override bool HasBinding(string name)
+        {
+            return (_declarativeRecord._hasBindings && _declarativeRecord.HasBinding(name)) || _objectRecord.HasBinding(name);
+        }
 
         internal override bool TryGetBinding(
-            in Key name,
+            BindingName name,
             bool strict,
             out Binding binding,
             out JsValue value)
         {
-            // we unwrap by name
-            binding = default;
+            return (_declarativeRecord._hasBindings && _declarativeRecord.TryGetBinding(name, strict, out binding, out value))
+                   || _objectRecord.TryGetBinding(name, strict, out binding, out value);
+        }
 
-            Key key = name;
-            if (!_global.Properties.TryGetValue(key, out var desc))
+        /// <summary>
+        ///     http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1.2.2
+        /// </summary>
+        public override void CreateMutableBinding(string name, bool canBeDeleted = false)
+        {
+            if (_declarativeRecord._hasBindings && _declarativeRecord.HasBinding(name))
             {
-                value = default;
-                return false;
+                ExceptionHelper.ThrowTypeError(_engine, name + " has already been declared");
             }
 
-            value = ObjectInstance.UnwrapJsValue(desc, _global);
-            return true;
+            _declarativeRecord.CreateMutableBinding(name, canBeDeleted);
         }
 
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1.2.2
-        /// </summary>
-        public override void CreateMutableBinding(string name, JsValue value, bool configurable = false)
+        public override void CreateImmutableBinding(string name, bool strict = true)
         {
-            var propertyDescriptor = configurable
-                ? new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding)
-                : new PropertyDescriptor(value, PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding);
+            if (_declarativeRecord._hasBindings && _declarativeRecord.HasBinding(name))
+            {
+                ExceptionHelper.ThrowTypeError(_engine, name + " has already been declared");
+            }
+
+            _declarativeRecord.CreateImmutableBinding(name, strict);
+        }
 
-            _global.DefinePropertyOrThrow(name, propertyDescriptor);
+        public override void InitializeBinding(string name, JsValue value)
+        {
+            if (_declarativeRecord._hasBindings && _declarativeRecord.HasBinding(name))
+            {
+                _declarativeRecord.InitializeBinding(name, value);
+            }
+            else
+            {
+                _objectRecord.InitializeBinding(name, value);
+            }
         }
 
         public override void SetMutableBinding(string name, JsValue value, bool strict)
         {
-            if (!_global.Set(name, value) && strict)
+            if (_declarativeRecord._hasBindings && _declarativeRecord.HasBinding(name))
+            {
+                _declarativeRecord.SetMutableBinding(name, value, strict);
+            }
+            else
             {
-                ExceptionHelper.ThrowTypeError(_engine);
+                _objectRecord.SetMutableBinding(name, value, strict);
             }
         }
 
-        public override JsValue GetBindingValue(string name, bool strict)
+        internal override void SetMutableBinding(BindingName name, JsValue value, bool strict)
         {
-            var desc = _global.GetProperty(name);
-            if (strict && desc == PropertyDescriptor.Undefined)
+            if (_declarativeRecord._hasBindings && _declarativeRecord.HasBinding(name.Key.Name))
+            {
+                _declarativeRecord.SetMutableBinding(name.Key.Name, value, strict);
+            }
+            else
             {
-                ExceptionHelper.ThrowReferenceError(_engine, name.ToString());
+                _objectRecord.SetMutableBinding(name, value, strict);
             }
+        }
 
-            return ObjectInstance.UnwrapJsValue(desc, this);
+        public override JsValue GetBindingValue(string name, bool strict)
+        {
+            return _declarativeRecord._hasBindings && _declarativeRecord.HasBinding(name)
+                ? _declarativeRecord.GetBindingValue(name, strict)
+                : _objectRecord.GetBindingValue(name, strict);
         }
 
         public override bool DeleteBinding(string name)
         {
-            return _global.Delete(name);
+            if (_declarativeRecord._hasBindings && _declarativeRecord.HasBinding(name))
+            {
+                return _declarativeRecord.DeleteBinding(name);
+            }
+
+            if (_objectRecord._bindingObject.HasOwnProperty(name))
+            {
+                var status = _objectRecord.DeleteBinding(name);
+                if (status)
+                {
+                    _varNames.Remove(name);
+                }
+
+                return status;
+            }
+
+            return true;
+        }
+
+        public override bool HasThisBinding()
+        {
+            return true;
+        }
+
+        public override bool HasSuperBinding()
+        {
+            return false;
+        }
+
+        public override JsValue WithBaseObject()
+        {
+            return Undefined;
+        }
+
+        public override JsValue GetThisBinding()
+        {
+            return _objectRecord._bindingObject;
+        }
+
+        public bool HasVarDeclaration(string name)
+        {
+            return _varNames.Contains(name);
+        }
+
+        public bool HasLexicalDeclaration(string name)
+        {
+            return _declarativeRecord.HasBinding(name);
+        }
+
+        public bool HasRestrictedGlobalProperty(string name)
+        {
+            var globalObject = _objectRecord._bindingObject;
+            var existingProp = globalObject.GetOwnProperty(name);
+            if (existingProp == PropertyDescriptor.Undefined)
+            {
+                return false;
+            }
+
+            return !existingProp.Configurable;
+        }
+
+        public bool CanDeclareGlobalVar(string name)
+        {
+            var globalObject = _objectRecord._bindingObject;
+            if (globalObject.HasOwnProperty(name))
+            {
+                return true;
+            }
+
+            return globalObject.Extensible;
+        }
+
+        public bool CanDeclareGlobalFunction(string name)
+        {
+            var globalObject = _objectRecord._bindingObject;
+            var existingProp = globalObject.GetOwnProperty(name);
+            if (existingProp == PropertyDescriptor.Undefined)
+            {
+                return globalObject.Extensible;
+            }
+
+            if (existingProp.Configurable)
+            {
+                return true;
+            }
+
+            if (existingProp.IsDataDescriptor() && existingProp.Writable && existingProp.Enumerable)
+            {
+                return true;
+            }
+
+            return false;
+        }
+
+        public void CreateGlobalVarBinding(string name, bool canBeDeleted)
+        {
+            var globalObject = _objectRecord._bindingObject;
+            var hasProperty = globalObject.HasOwnProperty(name);
+            var extensible = globalObject.Extensible;
+            if (!hasProperty && extensible)
+            {
+                _objectRecord.CreateMutableBinding(name, canBeDeleted);
+                _objectRecord.InitializeBinding(name, Undefined);
+            }
+
+            _varNames.Add(name);
+        }
+
+        public void CreateGlobalFunctionBinding(string name, JsValue value, bool canBeDeleted)
+        {
+            var globalObject = _objectRecord._bindingObject;
+            var existingProp = globalObject.GetOwnProperty(name);
+
+            PropertyDescriptor desc;
+            if (existingProp == PropertyDescriptor.Undefined || existingProp.Configurable)
+            {
+                desc = new PropertyDescriptor(value, true, true, canBeDeleted);
+            }
+            else
+            {
+                desc = new PropertyDescriptor(value, PropertyFlag.None);
+            }
+
+            globalObject.DefinePropertyOrThrow(name, desc);
+            globalObject.Set(name, value, false);
+            _varNames.Add(name);
         }
 
         public override JsValue ImplicitThisValue()
@@ -84,12 +242,12 @@ namespace Jint.Runtime.Environments
 
         internal override string[] GetAllBindingNames()
         {
-            return _global.GetOwnProperties().Select( x=> x.Key.ToString()).ToArray();
+            return _objectRecord._bindingObject.GetOwnProperties().Select(x => x.Key.ToString()).ToArray();
         }
 
         public override bool Equals(JsValue other)
         {
-            return ReferenceEquals(_global, other);
+            return ReferenceEquals(_objectRecord, other);
         }
     }
-}
+}

+ 14 - 6
Jint/Runtime/Environments/LexicalEnvironment.cs

@@ -1,4 +1,5 @@
 using Jint.Native;
+using Jint.Native.Function;
 using Jint.Native.Global;
 using Jint.Native.Object;
 
@@ -24,7 +25,7 @@ namespace Jint.Runtime.Environments
 
         internal static bool TryGetIdentifierEnvironmentWithBindingValue(
             LexicalEnvironment lex,
-            in Key name,
+            EnvironmentRecord.BindingName name,
             bool strict,
             out EnvironmentRecord record,
             out JsValue value)
@@ -52,21 +53,28 @@ namespace Jint.Runtime.Environments
 
         public static LexicalEnvironment NewDeclarativeEnvironment(Engine engine, LexicalEnvironment outer = null)
         {
-            var environment = new LexicalEnvironment(engine, null, null);
-            environment._record = new DeclarativeEnvironmentRecord(engine);
-            environment._outer = outer;
+            var environment = new LexicalEnvironment(engine, null, null)
+            {
+                _record = new DeclarativeEnvironmentRecord(engine),
+                _outer = outer
+            };
 
             return environment;
         }
 
+        public static LexicalEnvironment NewFunctionEnvironment(Engine engine, FunctionInstance f, JsValue newTarget)
+        {
+            return new LexicalEnvironment(engine, new FunctionEnvironmentRecord(engine, f, newTarget), f._environment);
+        }
+
         internal static LexicalEnvironment NewGlobalEnvironment(Engine engine, GlobalObject objectInstance)
         {
             return new LexicalEnvironment(engine, new GlobalEnvironmentRecord(engine, objectInstance), null);
         }
 
-        internal static LexicalEnvironment NewObjectEnvironment(Engine engine, ObjectInstance objectInstance, LexicalEnvironment outer, bool provideThis)
+        internal static LexicalEnvironment NewObjectEnvironment(Engine engine, ObjectInstance objectInstance, LexicalEnvironment outer, bool provideThis, bool withEnvironment = false)
         {
-            return new LexicalEnvironment(engine, new ObjectEnvironmentRecord(engine, objectInstance, provideThis), outer);
+            return new LexicalEnvironment(engine, new ObjectEnvironmentRecord(engine, objectInstance, provideThis, withEnvironment), outer);
         } 
     }
 }

+ 63 - 13
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -12,13 +12,19 @@ namespace Jint.Runtime.Environments
     /// </summary>
     internal sealed class ObjectEnvironmentRecord : EnvironmentRecord
     {
-        private readonly ObjectInstance _bindingObject;
+        internal readonly ObjectInstance _bindingObject;
         private readonly bool _provideThis;
+        private readonly bool _withEnvironment;
 
-        public ObjectEnvironmentRecord(Engine engine, ObjectInstance bindingObject, bool provideThis) : base(engine)
+        public ObjectEnvironmentRecord(
+            Engine engine,
+            ObjectInstance bindingObject, 
+            bool provideThis, 
+            bool withEnvironment) : base(engine)
         {
             _bindingObject = bindingObject;
             _provideThis = provideThis;
+            _withEnvironment = withEnvironment;
         }
 
         public override bool HasBinding(string name)
@@ -31,6 +37,11 @@ namespace Jint.Runtime.Environments
                 return false;
             }
 
+            if (!_withEnvironment)
+            {
+                return true;
+            }
+            
             return !IsBlocked(name);
         }
 
@@ -40,7 +51,7 @@ namespace Jint.Runtime.Environments
         }
 
         internal override bool TryGetBinding(
-            in Key name,
+            BindingName name,
             bool strict,
             out Binding binding,
             out JsValue value)
@@ -48,23 +59,29 @@ namespace Jint.Runtime.Environments
             // we unwrap by name
             binding = default;
 
-            if (!HasProperty(name.Name) || IsBlocked(name))
+            if (!HasProperty(name.StringValue))
+            {
+                value = default;
+                return false;
+            }
+
+            if (_withEnvironment && IsBlocked(name.StringValue))
             {
                 value = default;
                 return false;
             }
 
-            var desc = _bindingObject.GetProperty(name.Name);
+            var desc = _bindingObject.GetProperty(name.StringValue);
             value = ObjectInstance.UnwrapJsValue(desc, _bindingObject);
             return true;
         }
 
-        private bool IsBlocked(string property)
+        private bool IsBlocked(JsValue property)
         {
             var unscopables = _bindingObject.Get(GlobalSymbolRegistry.Unscopables);
             if (unscopables is ObjectInstance oi)
             {
-                var blocked = TypeConverter.ToBoolean(oi.Get(new JsString(property)));
+                var blocked = TypeConverter.ToBoolean(oi.Get(property));
                 if (blocked)
                 {
                     return true;
@@ -75,20 +92,41 @@ namespace Jint.Runtime.Environments
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1.2.2
+        /// http://www.ecma-international.org/ecma-262/6.0/#sec-object-environment-records-createmutablebinding-n-d
         /// </summary>
-        public override void CreateMutableBinding(string name, JsValue value, bool configurable = false)
+        public override void CreateMutableBinding(string name, bool canBeDeleted = false)
         {
-            var propertyDescriptor = configurable
-                ? new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding)
-                : new PropertyDescriptor(value, PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding);
+            var propertyDescriptor = canBeDeleted
+                ? new PropertyDescriptor(Undefined, PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding)
+                : new PropertyDescriptor(Undefined, PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding);
 
             _bindingObject.DefinePropertyOrThrow(name, propertyDescriptor);
         }
+        
+        /// <summary>
+        ///  http://www.ecma-international.org/ecma-262/6.0/#sec-object-environment-records-createimmutablebinding-n-s
+        /// </summary>
+        public override void CreateImmutableBinding(string name, bool strict = true)
+        {
+            ExceptionHelper.ThrowInvalidOperationException("The concrete Environment Record method CreateImmutableBinding is never used within this specification in association with Object Environment Records.");
+        }
+
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/6.0/#sec-object-environment-records-initializebinding-n-v
+        /// </summary>
+        public override void InitializeBinding(string name, JsValue value)
+        {
+            SetMutableBinding(name, value, false);
+        }
 
         public override void SetMutableBinding(string name, JsValue value, bool strict)
         {
-            if (!_bindingObject.Set(name, value) && strict)
+            SetMutableBinding(new BindingName(name), value, strict);
+        }
+
+        internal override void SetMutableBinding(BindingName name, JsValue value, bool strict)
+        {
+            if (!_bindingObject.Set(name.StringValue, value) && strict)
             {
                 ExceptionHelper.ThrowTypeError(_engine);
             }
@@ -110,6 +148,13 @@ namespace Jint.Runtime.Environments
             return _bindingObject.Delete(name);
         }
 
+        public override bool HasThisBinding() => false;
+
+        public override bool HasSuperBinding() => false;
+
+        public override JsValue WithBaseObject() => _withEnvironment ? _bindingObject : Undefined;
+
+        
         public override JsValue ImplicitThisValue()
         {
             if (_provideThis)
@@ -134,5 +179,10 @@ namespace Jint.Runtime.Environments
         {
             return ReferenceEquals(_bindingObject, other);
         }
+
+        public override JsValue GetThisBinding()
+        {
+            throw new System.NotImplementedException();
+        }
     }
 }

+ 0 - 5
Jint/Runtime/ExceptionHelper.cs

@@ -72,11 +72,6 @@ namespace Jint.Runtime
             throw new JavaScriptException(engine.RangeError, message);
         }
 
-        public static T ThrowRangeErrorNoEngine<T>(string message)
-        {
-            throw new RangeErrorException(message);
-        }
-
         public static void ThrowRangeError(Engine engine, string message = null)
         {
             throw new JavaScriptException(engine.RangeError, message);

+ 1 - 1
Jint/Runtime/Interop/ClrFunctionInstance.cs

@@ -18,7 +18,7 @@ namespace Jint.Runtime.Interop
             Func<JsValue, JsValue[], JsValue> func,
             int length = 0,
             PropertyFlag lengthFlags = PropertyFlag.AllForbidden)
-            : base(engine, !string.IsNullOrWhiteSpace(name) ? new JsString(name) : null, strict: false)
+            : base(engine, !string.IsNullOrWhiteSpace(name) ? new JsString(name) : null)
         {
             _func = func;
 

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

@@ -12,11 +12,12 @@ namespace Jint.Runtime.Interop
     /// </summary>
     public sealed class DelegateWrapper : FunctionInstance
     {
+        private static readonly JsString _name = new JsString("delegate");
         private readonly Delegate _d;
         private readonly bool _delegateContainsParamsArgument;
 
         public DelegateWrapper(Engine engine, Delegate d)
-            : base(engine, "delegate", null, null, false)
+            : base(engine, _name, FunctionThisMode.Global)
         {
             _d = d;
             _prototype = engine.Function.PrototypeObject;

+ 2 - 1
Jint/Runtime/Interop/GetterFunctionInstance.cs

@@ -9,10 +9,11 @@ namespace Jint.Runtime.Interop
     /// </summary>
     public sealed class GetterFunctionInstance: FunctionInstance
     {
+        private static readonly JsString _name = new JsString("get");
         private readonly Func<JsValue, JsValue> _getter;
 
         public GetterFunctionInstance(Engine engine, Func<JsValue, JsValue> getter)
-            : base(engine, "get", null, null, false)
+            : base(engine, _name, FunctionThisMode.Global)
         {
             _getter = getter;
         }

+ 2 - 1
Jint/Runtime/Interop/MethodInfoFunctionInstance.cs

@@ -8,10 +8,11 @@ namespace Jint.Runtime.Interop
 {
     public sealed class MethodInfoFunctionInstance : FunctionInstance
     {
+        private static readonly JsString _name = new JsString("Function");
         private readonly MethodInfo[] _methods;
 
         public MethodInfoFunctionInstance(Engine engine, MethodInfo[] methods)
-            : base(engine, "Function", null, null, false)
+            : base(engine, _name)
         {
             _methods = methods;
             _prototype = engine.Function.PrototypeObject;

+ 2 - 1
Jint/Runtime/Interop/SetterFunctionInstance.cs

@@ -9,10 +9,11 @@ namespace Jint.Runtime.Interop
     /// </summary>
     public sealed class SetterFunctionInstance : FunctionInstance
     {
+        private static readonly JsString _name = new JsString("set");
         private readonly Action<JsValue, JsValue> _setter;
 
         public SetterFunctionInstance(Engine engine, Action<JsValue, JsValue> setter)
-            : base(engine, "set", null, null, false)
+            : base(engine, _name)
         {
             _setter = setter;
         }

+ 1 - 1
Jint/Runtime/Interop/TypeReference.cs

@@ -15,7 +15,7 @@ namespace Jint.Runtime.Interop
         private static readonly JsString _name = new JsString("typereference");
 
         private TypeReference(Engine engine)
-            : base(engine, _name, strict: false, ObjectClass.TypeReference)
+            : base(engine, _name, FunctionThisMode.Global, ObjectClass.TypeReference)
         {
         }
 

+ 54 - 45
Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs

@@ -31,19 +31,24 @@ namespace Jint.Runtime.Interpreter.Expressions
         protected override object EvaluateInternal()
         {
             var rightValue = _right.GetValue();
-            ProcessPatterns(_engine, _pattern, rightValue, true);
+            ProcessPatterns(_engine, _pattern, rightValue, null);
             return rightValue;
         }
 
-        internal static void ProcessPatterns(Engine engine, BindingPattern pattern, JsValue argument, bool checkReference)
+        internal static void ProcessPatterns(
+            Engine engine,
+            BindingPattern pattern,
+            JsValue argument,
+            LexicalEnvironment environment,
+            bool checkObjectPatternPropertyReference = true)
         {
             if (pattern is ArrayPattern ap)
             {
-                HandleArrayPattern(engine, ap, argument, checkReference);
+                HandleArrayPattern(engine, ap, argument, environment);
             }
             else if (pattern is ObjectPattern op)
             {
-                HandleObjectPattern(engine, op, argument, checkReference);
+                HandleObjectPattern(engine, op, argument, environment, checkObjectPatternPropertyReference);
             }
         }
         
@@ -62,7 +67,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             return true;
         }
         
-        private static void HandleArrayPattern(Engine engine, ArrayPattern pattern, JsValue argument, bool checkReference)
+        private static void HandleArrayPattern(Engine engine, ArrayPattern pattern, JsValue argument, LexicalEnvironment environment)
         {
             var obj = TypeConverter.ToObject(engine, argument);
             ArrayOperations arrayOperations = null;
@@ -121,7 +126,8 @@ namespace Jint.Runtime.Interpreter.Expressions
                                 break;
                             }
                         }
-                        AssignToIdentifier(engine, identifier.Name, value, checkReference);
+
+                        AssignToIdentifier(engine, identifier.Name, value, environment);
                     }
                     else if (left is MemberExpression me)
                     {
@@ -137,7 +143,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                             ConsumeFromIterator(iterator, out value, out done);
                         }
 
-                        AssignToReference(engine, reference, value);
+                        AssignToReference(engine, reference, value, environment);
                     }
                     else if (left is BindingPattern bindingPattern)
                     {
@@ -151,7 +157,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                             iterator.TryIteratorStep(out var temp);
                             value = temp;
                         }
-                        ProcessPatterns(engine, bindingPattern, value, checkReference);
+                        ProcessPatterns(engine, bindingPattern, value, environment);
                     }
                     else if (left is RestElement restElement)
                     {
@@ -195,15 +201,15 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                         if (restElement.Argument is Identifier leftIdentifier)
                         {
-                            AssignToIdentifier(engine, leftIdentifier.Name, array, checkReference);
+                            AssignToIdentifier(engine, leftIdentifier.Name, array, environment);
                         }
                         else if (restElement.Argument is BindingPattern bp)
                         {
-                            ProcessPatterns(engine, bp, array, checkReference: false);
+                            ProcessPatterns(engine, bp, array, environment);
                         }                    
                         else
                         {
-                            AssignToReference(engine, reference,  array);
+                            AssignToReference(engine, reference,  array, environment);
                         }
                     }
                     else if (left is AssignmentPattern assignmentPattern)
@@ -218,11 +224,9 @@ namespace Jint.Runtime.Interpreter.Expressions
                             ConsumeFromIterator(iterator, out value, out done);
                         }
 
-                        if (value.IsUndefined()
-                            && assignmentPattern.Right is Expression expression)
+                        if (value.IsUndefined())
                         {
-                            var jintExpression = Build(engine, expression);
-
+                            var jintExpression = Build(engine, assignmentPattern.Right);
                             value = jintExpression.GetValue();
                         }
 
@@ -233,11 +237,11 @@ namespace Jint.Runtime.Interpreter.Expressions
                                 ((FunctionInstance) value).SetFunctionName(new JsString(leftIdentifier.Name));
                             }
 
-                            AssignToIdentifier(engine, leftIdentifier.Name, value, checkReference);
+                            AssignToIdentifier(engine, leftIdentifier.Name, value, environment);
                         }
                         else if (assignmentPattern.Left is BindingPattern bp)
                         {
-                            ProcessPatterns(engine, bp, value, checkReference);
+                            ProcessPatterns(engine, bp, value, environment);
                         }
                     }
                     else
@@ -264,7 +268,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
         }
 
-        private static void HandleObjectPattern(Engine engine, ObjectPattern pattern, JsValue argument, bool checkReference)
+        private static void HandleObjectPattern(Engine engine, ObjectPattern pattern, JsValue argument, LexicalEnvironment environment, bool checkReference)
         {
             var processedProperties = pattern.Properties.Count > 0 && pattern.Properties[pattern.Properties.Count - 1] is RestElement
                 ? new HashSet<JsValue>()
@@ -291,15 +295,15 @@ namespace Jint.Runtime.Interpreter.Expressions
                     if (p.Value is AssignmentPattern assignmentPattern)
                     {
                         source.TryGetValue(sourceKey, out var value);
-                        if (value.IsUndefined() && assignmentPattern.Right is Expression expression)
+                        if (value.IsUndefined())
                         {
-                            var jintExpression = Build(engine, expression);
+                            var jintExpression = Build(engine, assignmentPattern.Right);
                             value = jintExpression.GetValue();
                         }
 
                         if (assignmentPattern.Left is BindingPattern bp)
                         {
-                            ProcessPatterns(engine, bp, value, checkReference);
+                            ProcessPatterns(engine, bp, value, environment);
                             continue;
                         }
 
@@ -310,25 +314,25 @@ namespace Jint.Runtime.Interpreter.Expressions
                             ((FunctionInstance) value).SetFunctionName(target.Name);
                         }
 
-                        AssignToIdentifier(engine, target.Name, value, checkReference);
+                        AssignToIdentifier(engine, target.Name, value, environment);
                     }
                     else if (p.Value is BindingPattern bindingPattern)
                     {
                         source.TryGetValue(sourceKey, out var value);
-                        ProcessPatterns(engine, bindingPattern, value, checkReference);
+                        ProcessPatterns(engine, bindingPattern, value, environment);
                     }
                     else if (p.Value is MemberExpression memberExpression)
                     {
                         var reference = GetReferenceFromMember(engine, memberExpression);
                         source.TryGetValue(sourceKey, out var value);
-                        AssignToReference(engine, reference, value);
+                        AssignToReference(engine, reference, value, environment);
                     }
                     else
                     {
                         var identifierReference = p.Value as Identifier;
                         var target = identifierReference ?? identifier;
-                        source.TryGetValue(sourceKey, out var value);
-                        AssignToIdentifier(engine, target.Name, value, checkReference);
+                        source.TryGetValue(sourceKey, out var v);
+                        AssignToIdentifier(engine, target.Name, v, environment, checkReference);
                     }
                 }
                 else
@@ -339,18 +343,18 @@ namespace Jint.Runtime.Interpreter.Expressions
                         var count = Math.Max(0, source.Properties?.Count ?? 0) - processedProperties.Count;
                         var rest = engine.Object.Construct(count);
                         source.CopyDataProperties(rest, processedProperties);
-                        AssignToIdentifier(engine, leftIdentifier.Name, rest, checkReference);
+                        AssignToIdentifier(engine, leftIdentifier.Name, rest, environment);
                     }
                     else if (restElement.Argument is BindingPattern bp)
                     {
-                        ProcessPatterns(engine, bp, argument, checkReference);
+                        ProcessPatterns(engine, bp, argument, environment);
                     }
                     else if (restElement.Argument is MemberExpression memberExpression)
                     {
                         var left = GetReferenceFromMember(engine, memberExpression);
                         var rest = engine.Object.Construct(0);
                         source.CopyDataProperties(rest, processedProperties);
-                        AssignToReference(engine, left, rest);
+                        AssignToReference(engine, left, rest, environment);
                     }
                     else
                     {
@@ -360,10 +364,21 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
         }
 
-        private static void AssignToReference(Engine engine,  Reference lref,  JsValue rval)
+        private static void AssignToReference(
+            Engine engine,
+            Reference lhs,
+            JsValue v,
+            LexicalEnvironment environment)
         {
-            engine.PutValue(lref, rval);
-            engine._referencePool.Return(lref);
+            if (environment is null)
+            {
+                engine.PutValue(lhs, v);
+            }
+            else
+            {
+                lhs.InitializeReferencedBinding(v);
+            }
+            engine._referencePool.Return(lhs);
         }
 
         private static Reference GetReferenceFromMember(Engine engine, MemberExpression memberExpression)
@@ -378,27 +393,21 @@ namespace Jint.Runtime.Interpreter.Expressions
             Engine engine,
             string name,
             JsValue rval,
-            bool checkReference)
+            LexicalEnvironment environment,
+            bool checkReference = true)
         {
-            var env = engine.ExecutionContext.LexicalEnvironment;
-
-            var strict = StrictModeScope.IsStrictModeCode;
-            if (LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
-                env,
-                name,
-                strict,
-                out var environmentRecord,
-                out _))
+            var lhs = engine.ResolveBinding(name, environment);
+            if (environment != null)
             {
-                environmentRecord.SetMutableBinding(name, rval, strict);
+                lhs.InitializeReferencedBinding(rval);
             }
             else
             {
-                if (checkReference && strict)
+                if (checkReference && lhs.IsUnresolvableReference() && StrictModeScope.IsStrictModeCode)
                 {
                     ExceptionHelper.ThrowReferenceError<Reference>(engine);
                 }
-                env._record.CreateMutableBinding(name, rval);
+                engine.PutValue(lhs, rval);
             }
         }
     }

+ 1 - 2
Jint/Runtime/Interpreter/Expressions/JintArrowFunctionExpression.cs

@@ -11,7 +11,6 @@ namespace Jint.Runtime.Interpreter.Expressions
         public JintArrowFunctionExpression(Engine engine, IFunction function)
             : base(engine, ArrowParameterPlaceHolder.Empty)
         {
-
             _function = new JintFunctionDefinition(engine, function);
         }
 
@@ -23,7 +22,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 _engine,
                 _function,
                 funcEnv,
-                _function._strict);
+                _function.Strict);
 
             return closure;
         }

+ 20 - 33
Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs

@@ -14,7 +14,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         private JintAssignmentExpression(Engine engine, AssignmentExpression expression) : base(engine, expression)
         {
-            _left = Build(engine, (Expression) expression.Left);
+            _left = Build(engine, expression.Left);
             _right = Build(engine, expression.Right);
             _operator = expression.Operator;
         }
@@ -23,15 +23,12 @@ namespace Jint.Runtime.Interpreter.Expressions
         {
             if (expression.Operator == AssignmentOperator.Assign)
             {
-                if (expression.Left is Expression)
-                {
-                    return new SimpleAssignmentExpression(engine, expression);
-                }
-
                 if (expression.Left is BindingPattern)
                 {
                     return new BindingPatternAssignmentExpression(engine, expression);
                 }
+
+                return new SimpleAssignmentExpression(engine, expression);
             }
 
             return new JintAssignmentExpression(engine, expression);
@@ -147,27 +144,25 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         internal sealed class SimpleAssignmentExpression : JintExpression
         {
-            private readonly JintExpression _left;
-            private readonly JintExpression _right;
+            private JintExpression _left;
+            private JintExpression _right;
 
-            private readonly JintIdentifierExpression _leftIdentifier;
-            private readonly bool _evalOrArguments;
-            private readonly ArrayPattern _arrayPattern;
+            private JintIdentifierExpression _leftIdentifier;
+            private bool _evalOrArguments;
 
             public SimpleAssignmentExpression(Engine engine, AssignmentExpression expression) : base(engine, expression)
             {
-                if (expression.Left is ArrayPattern arrayPattern)
-                {
-                    _arrayPattern = arrayPattern;
-                }
-                else
-                {
-                    _left = Build(engine, (Expression) expression.Left);
-                    _leftIdentifier = _left as JintIdentifierExpression;
-                    _evalOrArguments = _leftIdentifier?.HasEvalOrArguments == true;
-                }
+                _initialized = false;
+            }
+
+            protected override void Initialize()
+            {
+                var assignmentExpression = ((AssignmentExpression) _expression);
+                _left = Build(_engine, assignmentExpression.Left);
+                _leftIdentifier = _left as JintIdentifierExpression;
+                _evalOrArguments = _leftIdentifier?.HasEvalOrArguments == true;
 
-                _right = Build(engine, expression.Right);
+                _right = Build(_engine, assignmentExpression.Right);
             }
 
             protected override object EvaluateInternal()
@@ -177,14 +172,6 @@ namespace Jint.Runtime.Interpreter.Expressions
                 {
                     rval = AssignToIdentifier(_engine, _leftIdentifier, _right, _evalOrArguments);
                 }
-                else if (_arrayPattern != null)
-                {
-                    foreach (var element in _arrayPattern.Elements)
-                    {
-                        AssignToIdentifier(_engine, _leftIdentifier, _right, false);
-                    }
-                }
-
                 return rval ?? SetValue();
             }
 
@@ -211,7 +198,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var strict = StrictModeScope.IsStrictModeCode;
                 if (LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
                     env,
-                    left.ExpressionName,
+                    left._expressionName,
                     strict,
                     out var environmentRecord,
                     out _))
@@ -225,10 +212,10 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                     if (right._expression.IsFunctionWithName())
                     {
-                        ((FunctionInstance) rval).SetFunctionName(left.ExpressionName);
+                        ((FunctionInstance) rval).SetFunctionName(left._expressionName.StringValue);
                     }
 
-                    environmentRecord.SetMutableBinding(left.ExpressionName, rval, strict);
+                    environmentRecord.SetMutableBinding(left._expressionName, rval, strict);
                     return rval;
                 }
 

+ 2 - 2
Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs

@@ -33,7 +33,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 JintArguments = new JintExpression[expression.Arguments.Count]
             };
 
-            bool CanSpread(INode e)
+            bool CanSpread(Node e)
             {
                 return e?.Type == Nodes.SpreadElement
                     || e is AssignmentExpression ae && ae.Right?.Type == Nodes.SpreadElement;
@@ -148,7 +148,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var baseValue = r.GetBase();
                 if ((baseValue._type & InternalTypes.ObjectEnvironmentRecord) == 0)
                 {
-                    thisObject = baseValue;
+                    thisObject = r.GetThisValue();
                 }
                 else
                 {

+ 1 - 1
Jint/Runtime/Interpreter/Expressions/JintConstantExpression.cs

@@ -10,7 +10,7 @@ namespace Jint.Runtime.Interpreter.Expressions
     {
         private readonly JsValue _value;
 
-        public JintConstantExpression(Engine engine, INode expression, JsValue value) : base(engine, expression)
+        public JintConstantExpression(Engine engine, Expression expression, JsValue value) : base(engine, expression)
         {
             _value = value;
         }

+ 32 - 77
Jint/Runtime/Interpreter/Expressions/JintExpression.cs

@@ -14,9 +14,9 @@ namespace Jint.Runtime.Interpreter.Expressions
         protected bool _initialized = true;
 
         protected readonly Engine _engine;
-        protected internal readonly INode _expression;
+        protected internal readonly Expression _expression;
 
-        protected JintExpression(Engine engine, INode expression)
+        protected JintExpression(Engine engine, Expression expression)
         {
             _engine = engine;
             _expression = expression;
@@ -55,80 +55,35 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         protected internal static JintExpression Build(Engine engine, Expression expression)
         {
-            switch (expression.Type)
+            return expression.Type switch
             {
-                case Nodes.AssignmentExpression:
-                    return JintAssignmentExpression.Build(engine, (AssignmentExpression) expression);
-
-                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);
-
-                case Nodes.CallExpression:
-                    return new JintCallExpression(engine, (CallExpression) expression);
-
-                case Nodes.ConditionalExpression:
-                    return new JintConditionalExpression(engine, (ConditionalExpression) expression);
-
-                case Nodes.FunctionExpression:
-                    return new JintFunctionExpression(engine, (IFunction) expression);
-
-                case Nodes.Identifier:
-                    return new JintIdentifierExpression(engine, (Identifier) expression);
-
-                case Nodes.Literal:
-                    return JintLiteralExpression.Build(engine, (Literal) expression);
-
-                case Nodes.LogicalExpression:
-                    var binaryExpression = (BinaryExpression) expression;
-                    switch (binaryExpression.Operator)
-                    {
-                        case BinaryOperator.LogicalAnd:
-                            return new JintLogicalAndExpression(engine, binaryExpression);
-                        case BinaryOperator.LogicalOr:
-                            return new JintLogicalOrExpression(engine, binaryExpression);
-                        default:
-                            return ExceptionHelper.ThrowArgumentOutOfRangeException<JintExpression>();
-                    }
-
-                case Nodes.MemberExpression:
-                    return new JintMemberExpression(engine, (MemberExpression) expression);
-
-                case Nodes.NewExpression:
-                    return new JintNewExpression(engine, (NewExpression) expression);
-
-                case Nodes.ObjectExpression:
-                    return new JintObjectExpression(engine, (ObjectExpression) expression);
-
-                case Nodes.SequenceExpression:
-                    return new JintSequenceExpression(engine, (SequenceExpression) expression);
-
-                case Nodes.ThisExpression:
-                    return new JintThisExpression(engine, (ThisExpression) expression);
-
-                case Nodes.UpdateExpression:
-                    return new JintUpdateExpression(engine, (UpdateExpression) expression);
-
-                case Nodes.UnaryExpression:
-                    return JintUnaryExpression.Build(engine, (UnaryExpression) expression);
-
-                case Nodes.SpreadElement:
-                    return new JintSpreadExpression(engine, (SpreadElement) expression);
-
-                case Nodes.TemplateLiteral:
-                    return new JintTemplateLiteralExpression(engine, (TemplateLiteral) expression);
-
-                case Nodes.TaggedTemplateExpression:
-                    return new JintTaggedTemplateExpression(engine, (TaggedTemplateExpression) expression);
-
-                default:
-                    return ExceptionHelper.ThrowArgumentOutOfRangeException<JintExpression>(nameof(expression), $"unsupported language element '{expression.Type}'");
-            }
+                Nodes.AssignmentExpression => JintAssignmentExpression.Build(engine, (AssignmentExpression) expression),
+                Nodes.ArrayExpression => new JintArrayExpression(engine, (ArrayExpression) expression),
+                Nodes.ArrowFunctionExpression => new JintArrowFunctionExpression(engine, (IFunction) expression),
+                Nodes.BinaryExpression => JintBinaryExpression.Build(engine, (BinaryExpression) expression),
+                Nodes.CallExpression => new JintCallExpression(engine, (CallExpression) expression),
+                Nodes.ConditionalExpression => new JintConditionalExpression(engine, (ConditionalExpression) expression),
+                Nodes.FunctionExpression => new JintFunctionExpression(engine, (IFunction) expression),
+                Nodes.Identifier => new JintIdentifierExpression(engine, (Identifier) expression),
+                Nodes.Literal => JintLiteralExpression.Build(engine, (Literal) expression),
+                Nodes.LogicalExpression => ((BinaryExpression) expression).Operator switch
+                {
+                    BinaryOperator.LogicalAnd => new JintLogicalAndExpression(engine, (BinaryExpression) expression),
+                    BinaryOperator.LogicalOr => new JintLogicalOrExpression(engine, (BinaryExpression) expression),
+                    _ => ExceptionHelper.ThrowArgumentOutOfRangeException<JintExpression>()
+                },
+                Nodes.MemberExpression => new JintMemberExpression(engine, (MemberExpression) expression),
+                Nodes.NewExpression => new JintNewExpression(engine, (NewExpression) expression),
+                Nodes.ObjectExpression => new JintObjectExpression(engine, (ObjectExpression) expression),
+                Nodes.SequenceExpression => new JintSequenceExpression(engine, (SequenceExpression) expression),
+                Nodes.ThisExpression => new JintThisExpression(engine, (ThisExpression) expression),
+                Nodes.UpdateExpression => new JintUpdateExpression(engine, (UpdateExpression) expression),
+                Nodes.UnaryExpression => JintUnaryExpression.Build(engine, (UnaryExpression) expression),
+                Nodes.SpreadElement => new JintSpreadExpression(engine, (SpreadElement) expression),
+                Nodes.TemplateLiteral => new JintTemplateLiteralExpression(engine, (TemplateLiteral) expression),
+                Nodes.TaggedTemplateExpression => new JintTaggedTemplateExpression(engine, (TaggedTemplateExpression) expression),
+                _ => ExceptionHelper.ThrowArgumentOutOfRangeException<JintExpression>(nameof(expression), $"unsupported expression type '{expression.Type}'")
+            };
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -414,7 +369,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         protected bool TryGetIdentifierEnvironmentWithBindingValue(
-            string expressionName,
+            EnvironmentRecord.BindingName expressionName,
             out EnvironmentRecord record,
             out JsValue value)
         {
@@ -431,7 +386,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         protected bool TryGetIdentifierEnvironmentWithBindingValue(
             bool strict,
-            in Key expressionName,
+            EnvironmentRecord.BindingName expressionName,
             out EnvironmentRecord record,
             out JsValue value)
         {

+ 8 - 4
Jint/Runtime/Interpreter/Expressions/JintFunctionExpression.cs

@@ -17,17 +17,21 @@ namespace Jint.Runtime.Interpreter.Expressions
         protected override object EvaluateInternal()
         {
             var funcEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
-            var envRec = (DeclarativeEnvironmentRecord) funcEnv._record;
+
+            var functionThisMode = _function.Strict || _engine._isStrict
+                ? FunctionInstance.FunctionThisMode.Strict 
+                : FunctionInstance.FunctionThisMode.Global;
 
             var closure = new ScriptFunctionInstance(
                 _engine,
                 _function,
                 funcEnv,
-                _function._strict);
+                functionThisMode);
 
-            if (_function._name != null)
+            if (_function.Name != null)
             {
-                envRec.CreateMutableBinding(_function._name, closure);
+                var envRec = (DeclarativeEnvironmentRecord) funcEnv._record;
+                envRec.CreateMutableBindingAndInitialize(_function.Name, canBeDeleted: false, closure);
             }
 
             return closure;

+ 8 - 12
Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs

@@ -1,3 +1,4 @@
+using Esprima.Ast;
 using Jint.Native;
 using Jint.Runtime.Environments;
 
@@ -5,23 +6,20 @@ namespace Jint.Runtime.Interpreter.Expressions
 {
     internal sealed class JintIdentifierExpression : JintExpression
     {
-        private readonly Key _expressionName;
-        private JsString _expressionNameJsValue; 
+        internal readonly EnvironmentRecord.BindingName _expressionName;
         private readonly JsValue _calculatedValue;
 
-        public JintIdentifierExpression(Engine engine, Esprima.Ast.Identifier expression) : base(engine, expression)
+        public JintIdentifierExpression(Engine engine, Identifier expression) : base(engine, expression)
         {
-            _expressionName = expression.Name;
+            _expressionName = new EnvironmentRecord.BindingName(expression.Name);
             if (expression.Name == "undefined")
             {
                 _calculatedValue = JsValue.Undefined;
             }
         }
 
-        public string ExpressionName => _expressionName.Name;
-
         public bool HasEvalOrArguments
-            => ExpressionName == CommonProperties.Eval || ExpressionName == CommonProperties.Arguments;
+            => _expressionName.StringValue._value == KnownKeys.Eval || _expressionName.Key == KnownKeys.Arguments;
 
         protected override object EvaluateInternal()
         {
@@ -31,8 +29,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 ? temp
                 : JsValue.Undefined;
 
-            var property = _expressionNameJsValue ??= new JsString(_expressionName.Name);
-            return _engine._referencePool.Rent(identifierEnvironment, property, strict);
+            return _engine._referencePool.Rent(identifierEnvironment, _expressionName.StringValue, strict);
         }
 
         public override JsValue GetValue()
@@ -46,10 +43,9 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
 
             var strict = StrictModeScope.IsStrictModeCode;
-            var property = _expressionNameJsValue ??= new JsString(_expressionName);
             return TryGetIdentifierEnvironmentWithBindingValue(strict, _expressionName, out _, out var value)
-                ? value
-                : _engine.GetValue(_engine._referencePool.Rent(JsValue.Undefined, property, strict), true);
+                ? value ?? ExceptionHelper.ThrowReferenceError<JsValue>(_engine, _expressionName.Key.Name + " has not been initialized")
+                : _engine.GetValue(_engine._referencePool.Rent(JsValue.Undefined, _expressionName.StringValue, strict), true);
         }
     }
 }

+ 2 - 2
Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs

@@ -51,11 +51,11 @@ namespace Jint.Runtime.Interpreter.Expressions
 
             if (_objectIdentifierExpression != null)
             {
-                baseReferenceName = _objectIdentifierExpression.ExpressionName;
+                baseReferenceName = _objectIdentifierExpression._expressionName.Key.Name;
                 var strict = isStrictModeCode;
                 TryGetIdentifierEnvironmentWithBindingValue(
                     strict,
-                    _objectIdentifierExpression.ExpressionName,
+                    _objectIdentifierExpression._expressionName,
                     out _,
                     out baseValue);
             }

+ 1 - 1
Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs

@@ -130,7 +130,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         private object BuildObjectNormal()
         {
             var obj = _engine.Object.Construct(_properties.Length);
-            bool isStrictModeCode = StrictModeScope.IsStrictModeCode;
+            bool isStrictModeCode = _engine._isStrict || StrictModeScope.IsStrictModeCode;
 
             for (var i = 0; i < _properties.Length; i++)
             {

+ 2 - 2
Jint/Runtime/Interpreter/Expressions/JintThisExpression.cs

@@ -11,7 +11,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         protected override object EvaluateInternal()
         {
-            return _engine.ExecutionContext.ThisBinding;
+            return _engine.ResolveThisBinding();
         }
 
         public override JsValue GetValue()
@@ -19,7 +19,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             // need to notify correct node when taking shortcut
             _engine._lastSyntaxNode = _expression;
 
-            return _engine.ExecutionContext.ThisBinding;
+            return _engine.ResolveThisBinding();
         }
     }
 }

+ 14 - 8
Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs

@@ -6,17 +6,23 @@ namespace Jint.Runtime.Interpreter.Expressions
 {
     internal sealed class JintUpdateExpression : JintExpression
     {
-        private readonly JintExpression _argument;
-        private readonly int _change;
-        private readonly bool _prefix;
+        private JintExpression _argument;
+        private int _change;
+        private bool _prefix;
 
-        private readonly JintIdentifierExpression _leftIdentifier;
-        private readonly bool _evalOrArguments;
+        private JintIdentifierExpression _leftIdentifier;
+        private bool _evalOrArguments;
 
         public JintUpdateExpression(Engine engine, UpdateExpression expression) : base(engine, expression)
         {
+            _initialized = false;
+        }
+
+        protected override void Initialize()
+        {
+            var expression = (UpdateExpression) _expression;
             _prefix = expression.Prefix;
-            _argument = Build(engine, expression.Argument);
+            _argument = Build(_engine, expression.Argument);
             if (expression.Operator == UnaryOperator.Increment)
             {
                 _change = 1;
@@ -69,7 +75,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         private JsValue UpdateIdentifier()
         {
             var strict = StrictModeScope.IsStrictModeCode;
-            var name = _leftIdentifier.ExpressionName;
+            var name = _leftIdentifier._expressionName;
             if (TryGetIdentifierEnvironmentWithBindingValue(
                 name,
                 out var environmentRecord,
@@ -85,7 +91,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     ? JsNumber.Create(value.AsInteger() + _change)
                     : JsNumber.Create(TypeConverter.ToNumber(value) + _change);
 
-                environmentRecord.SetMutableBinding(name, newValue, strict);
+                environmentRecord.SetMutableBinding(name.Key.Name, newValue, strict);
                 return _prefix
                     ? newValue
                     : (isInteger ? value : JsNumber.Create(TypeConverter.ToNumber(value)));

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

@@ -1,113 +1,351 @@
+using System;
 using System.Collections.Generic;
-using System.Linq;
-using Esprima;
 using Esprima.Ast;
+using Jint.Native;
+using Jint.Native.Function;
 using Jint.Runtime.Interpreter.Statements;
 
 namespace Jint.Runtime.Interpreter
 {
+    /// <summary>
+    /// Works as memento for function execution. Optimization to cache things that don't change.
+    /// </summary>
     internal sealed class JintFunctionDefinition
     {
-        internal readonly IFunction _function;
-        internal readonly string _name;
-        internal readonly bool _strict;
-        internal readonly string[] _parameterNames;
-        internal readonly JintStatement _body;
-        internal bool _hasRestParameter;
-        internal int _length;
+        private readonly Engine _engine;
+        
+        private JintStatement _body;
+        
+        public readonly string Name;
+        public readonly bool Strict;
+        public readonly IFunction Function;
 
-        public readonly HoistingScope _hoistingScope;
+        private State _state;
 
-        public JintFunctionDefinition(Engine engine, IFunction function)
+        public JintFunctionDefinition(
+            Engine engine,
+            IFunction function)
         {
-            _function = function;
-            _hoistingScope = function.HoistingScope;
-            _name = !string.IsNullOrEmpty(function.Id?.Name) ? function.Id.Name : null;
-            _strict = function.Strict;
-            _parameterNames = GetParameterNames(function);
-
-            Statement bodyStatement;
-            if (function.Expression)
-            {
-                bodyStatement = new ReturnStatement((Expression) function.Body);
-            }
-            else
+            _engine = engine;
+            Function = function;
+            Name = !string.IsNullOrEmpty(function.Id?.Name) ? function.Id.Name : null;
+            Strict = function.Strict;
+
+            if (!Strict && !function.Expression)
             {
                 // Esprima doesn't detect strict at the moment for
                 // language/expressions/object/method-definition/name-invoke-fn-strict.js
                 var blockStatement = (BlockStatement) function.Body;
-                for (int i = 0; i < blockStatement.Body.Count; ++i)
+                ref readonly var statements = ref blockStatement.Body;
+                for (int i = 0; i < statements.Count; ++i)
                 {
-                    if (blockStatement.Body[i] is Directive d && d.Directiv == "use strict")
+                    if (statements[i] is Directive d && d.Directiv == "use strict")
                     {
-                        _strict = true;
+                        Strict = true;
                     }
                 }
-                bodyStatement = blockStatement;
             }
+        }
+
+        public JintStatement Body
+        {
+            get
+            {
+                if (_body != null)
+                {
+                    return _body;
+                }
+
+                _body = Function.Expression
+                    ? (JintStatement) new JintReturnStatement(_engine, new ReturnStatement((Expression) Function.Body))
+                    : new JintBlockStatement(_engine, (BlockStatement) Function.Body);
+
+                return _body;
+            }
+        }
 
-            _body = JintStatement.Build(engine, bodyStatement);
+        internal State Initialize(Engine engine, FunctionInstance functionInstance)
+        {
+            return _state ??= DoInitialize(functionInstance);
         }
 
-        private IEnumerable<Identifier> GetParameterIdentifiers(INode parameter)
+        internal class State
         {
-            if (parameter is Identifier identifier)
+            public bool HasRestParameter;
+            public int Length;
+            public Key[] ParameterNames;
+            public bool HasDuplicates;
+            public bool IsSimpleParameterList;
+            public bool HasParameterExpressions;
+            public bool ArgumentsObjectNeeded;
+            public List<Key> VarNames;
+            public LinkedList<FunctionDeclaration> FunctionsToInitialize;
+            public readonly HashSet<Key> FunctionNames = new HashSet<Key>();
+            public LexicalVariableDeclaration[] LexicalDeclarations = Array.Empty<LexicalVariableDeclaration>();
+            public HashSet<Key> ParameterBindings;
+            public List<VariableValuePair> VarsToInitialize;
+
+            internal struct VariableValuePair
+            {
+                public Key Name;
+                public JsValue InitialValue;
+            }
+
+            internal struct LexicalVariableDeclaration
+            {
+                public VariableDeclarationKind Kind;
+                public List<string> BoundNames;
+            }
+        }
+
+        private State DoInitialize(FunctionInstance functionInstance)
+        {
+            var state = new State();
+            
+            ProcessParameters(Function, state, out var hasArguments);
+
+            var hoistingScope = HoistingScope.GetFunctionLevelDeclarations(Function, collectVarNames: true, collectLexicalNames: true);
+            var functionDeclarations = hoistingScope._functionDeclarations;
+            var lexicalNames = hoistingScope._lexicalNames;
+            state.VarNames = hoistingScope._varNames;
+
+            LinkedList<FunctionDeclaration> functionsToInitialize = null;
+
+            if (functionDeclarations != null)
             {
-                return new [] { identifier };
+                functionsToInitialize = new LinkedList<FunctionDeclaration>();
+                for (var i = functionDeclarations.Count - 1; i >= 0; i--)
+                {
+                    var d = functionDeclarations[i];
+                    var fn = d.Id.Name;
+                    if (state.FunctionNames.Add(fn))
+                    {
+                        functionsToInitialize.AddFirst(d);
+                    }
+                }
             }
-            if (parameter is RestElement restElement)
+
+            state.FunctionsToInitialize = functionsToInitialize;
+
+            const string ParameterNameArguments = "arguments";
+
+            state.ArgumentsObjectNeeded = true;
+            if (functionInstance._thisMode == FunctionInstance.FunctionThisMode.Lexical)
             {
-                _hasRestParameter = true;
-                return GetParameterIdentifiers(restElement.Argument);
+                state.ArgumentsObjectNeeded = false;
             }
-            if (parameter is ArrayPattern arrayPattern)
+            else if (hasArguments)
             {
-                return arrayPattern.Elements.SelectMany(GetParameterIdentifiers);
+                state.ArgumentsObjectNeeded = false;
             }
-            if (parameter is ObjectPattern objectPattern)
+            else if (!state.HasParameterExpressions)
             {
-                return objectPattern.Properties.SelectMany(property =>
-                    property is Property p
-                        ? GetParameterIdentifiers(p.Value)
-                        : GetParameterIdentifiers((RestElement) property)
-                );
+                if (state.FunctionNames.Contains(ParameterNameArguments)
+                    || lexicalNames?.Contains(ParameterNameArguments) == true)
+                {
+                    state.ArgumentsObjectNeeded = false;
+                }
             }
-            if (parameter is AssignmentPattern assignmentPattern)
+            
+            var parameterBindings = new HashSet<Key>(state.ParameterNames);
+            if (state.ArgumentsObjectNeeded)
             {
-                return GetParameterIdentifiers(assignmentPattern.Left);
+                parameterBindings.Add(KnownKeys.Arguments);
             }
 
-            return Enumerable.Empty<Identifier>();
+            state.ParameterBindings = parameterBindings;
+
+
+            var varsToInitialize = new List<State.VariableValuePair>();
+            if (!state.HasParameterExpressions)
+            {
+                var instantiatedVarNames = state.VarNames != null
+                    ? new HashSet<Key>(state.ParameterBindings)
+                    : new HashSet<Key>();
+
+                for (var i = 0; i < state.VarNames?.Count; i++)
+                {
+                    var n = state.VarNames[i];
+                    if (instantiatedVarNames.Add(n))
+                    {
+                        varsToInitialize.Add(new State.VariableValuePair
+                        {
+                            Name = n
+                        });
+                    }
+                }
+            }
+            else
+            {
+                var instantiatedVarNames = state.VarNames != null 
+                    ? new HashSet<Key>(state.ParameterBindings) 
+                    : null;
+                
+                for (var i = 0; i < state.VarNames?.Count; i++)
+                {
+                    var n = state.VarNames[i];
+                    if (instantiatedVarNames.Add(n))
+                    {
+                        JsValue initialValue = null;
+                        if (!state.ParameterBindings.Contains(n) || state.FunctionNames.Contains(n))
+                        {
+                            initialValue = JsValue.Undefined;
+                        }
+
+                        varsToInitialize.Add(new State.VariableValuePair
+                        {
+                            Name = n,
+                            InitialValue = initialValue
+                        });
+                    }
+                }
+            }
+
+            state.VarsToInitialize = varsToInitialize;
+
+            if (hoistingScope._lexicalDeclarations != null)
+            {
+                var _lexicalDeclarations = hoistingScope._lexicalDeclarations;
+                var lexicalDeclarationsCount = _lexicalDeclarations.Count;
+                var declarations = new State.LexicalVariableDeclaration[lexicalDeclarationsCount];
+                for (var i = 0; i < lexicalDeclarationsCount; i++)
+                {
+                    var d = _lexicalDeclarations[i];
+                    var boundNames = new List<string>();
+                    d.GetBoundNames(boundNames);
+                    declarations[i] = new State.LexicalVariableDeclaration
+                    {
+                        Kind = d.Kind,
+                        BoundNames = boundNames
+                    };
+                }
+                state.LexicalDeclarations = declarations;
+            }
+            
+            return state;
         }
 
-        private string[] GetParameterNames(IFunction functionDeclaration)
+        private static void GetBoundNames(
+            Expression parameter,
+            List<Key> target, 
+            bool checkDuplicates, 
+            ref bool _hasRestParameter, 
+            ref bool _hasParameterExpressions, 
+            ref bool _hasDuplicates,
+            ref bool hasArguments)
         {
-            var parameterNames = new List<string>();
-            var functionDeclarationParams = functionDeclaration.Params;
-            int count = functionDeclarationParams.Count;
-            bool onlyIdentifiers = true;
+            if (parameter is Identifier identifier)
+            {
+                _hasDuplicates |= checkDuplicates && target.Contains(identifier.Name);
+                target.Add(identifier.Name);
+                hasArguments |= identifier.Name == "arguments";
+                return;
+            }
+
+            while (true)
+            {
+                if (parameter is RestElement restElement)
+                {
+                    _hasRestParameter = true;
+                    _hasParameterExpressions = true;
+                    parameter = restElement.Argument;
+                    continue;
+                }
+
+                if (parameter is ArrayPattern arrayPattern)
+                {
+                    _hasParameterExpressions = true;
+                    ref readonly var arrayPatternElements = ref arrayPattern.Elements;
+                    for (var i = 0; i < arrayPatternElements.Count; i++)
+                    {
+                        var expression = arrayPatternElements[i];
+                        GetBoundNames(
+                            expression, 
+                            target,
+                            checkDuplicates,
+                            ref _hasRestParameter,
+                            ref _hasParameterExpressions,
+                            ref _hasDuplicates,
+                            ref hasArguments);
+                    }
+                }
+                else if (parameter is ObjectPattern objectPattern)
+                {
+                    _hasParameterExpressions = true;
+                    ref readonly var objectPatternProperties = ref objectPattern.Properties;
+                    for (var i = 0; i < objectPatternProperties.Count; i++)
+                    {
+                        var property = objectPatternProperties[i];
+                        if (property is Property p)
+                        {
+                            GetBoundNames(
+                                p.Value, 
+                                target,
+                                checkDuplicates,
+                                ref _hasRestParameter,
+                                ref _hasParameterExpressions,
+                                ref _hasDuplicates,
+                                ref hasArguments);
+                        }
+                        else
+                        {
+                            _hasRestParameter = true;
+                            _hasParameterExpressions = true;
+                            parameter = ((RestElement) property).Argument;
+                            continue;
+                        }
+                    }
+                }
+                else if (parameter is AssignmentPattern assignmentPattern)
+                {
+                    _hasParameterExpressions = true;
+                    parameter = assignmentPattern.Left;
+                    continue;
+                }
+
+                break;
+            }
+        }
+
+        private static void ProcessParameters(
+            IFunction function,
+            State state,
+            out bool hasArguments)
+        {
+            hasArguments = false;
+            state.IsSimpleParameterList  = 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)
                 {
+                    state.HasDuplicates |= parameterNames.Contains(id.Name);
+                    hasArguments = id.Name == "arguments";
                     parameterNames.Add(id.Name);
-                    if (onlyIdentifiers)
+                    if (state.IsSimpleParameterList)
                     {
-                        _length++;
+                        state.Length++;
                     }
                 }
-                else
+                else if (parameter.Type != Nodes.Literal)
                 {
-                    onlyIdentifiers = false;
-                    foreach (var identifier in GetParameterIdentifiers(parameter))
-                    {
-                        parameterNames.Add(identifier.Name);
-                    }
+                    state.IsSimpleParameterList = false;
+                    GetBoundNames(
+                        parameter, 
+                        parameterNames,
+                        checkDuplicates: true,
+                        ref state.HasRestParameter,
+                        ref state.HasParameterExpressions, 
+                        ref state.HasDuplicates,
+                        ref hasArguments);
                 }
             }
 
-            return parameterNames.ToArray();
+            state.ParameterNames = parameterNames.ToArray();
         }
     }
 }

+ 48 - 7
Jint/Runtime/Interpreter/JintStatementList.cs

@@ -1,5 +1,7 @@
-using Esprima.Ast;
+using System.Collections.Generic;
+using Esprima.Ast;
 using Jint.Native;
+using Jint.Runtime.Environments;
 using Jint.Runtime.Interpreter.Statements;
 
 namespace Jint.Runtime.Interpreter
@@ -14,12 +16,12 @@ namespace Jint.Runtime.Interpreter
 
         private readonly Engine _engine;
         private readonly Statement _statement;
-        private readonly NodeList<IStatementListItem> _statements;
+        private readonly NodeList<Statement> _statements;
 
         private Pair[] _jintStatements;
         private bool _initialized;
 
-        public JintStatementList(Engine engine, Statement statement, NodeList<IStatementListItem> statements)
+        public JintStatementList(Engine engine, Statement statement, NodeList<Statement> statements)
         {
             _engine = engine;
             _statement = statement;
@@ -31,7 +33,7 @@ namespace Jint.Runtime.Interpreter
             var jintStatements = new Pair[_statements.Count];
             for (var i = 0; i < jintStatements.Length; i++)
             {
-                var esprimaStatement = (Statement) _statements[i];
+                var esprimaStatement = _statements[i];
                 jintStatements[i] = new Pair
                 {
                     Statement = JintStatement.Build(_engine, esprimaStatement),
@@ -58,6 +60,9 @@ namespace Jint.Runtime.Interpreter
             JintStatement s = null;
             var c = new Completion(CompletionType.Normal, null, null, _engine._lastSyntaxNode?.Location ?? default);
             Completion sl = c;
+            
+            // The value of a StatementList is the value of the last value-producing item in the StatementList
+            JsValue lastValue = null;
             try
             {
                 foreach (var pair in _jintStatements)
@@ -72,8 +77,8 @@ namespace Jint.Runtime.Interpreter
                             c.Identifier,
                             c.Location);
                     }
-
                     sl = c;
+                    lastValue = c.Value ?? lastValue;
                 }
             }
             catch (JavaScriptException v)
@@ -98,7 +103,43 @@ namespace Jint.Runtime.Interpreter
                 });
                 c = new Completion(CompletionType.Throw, error, null, s.Location);
             }
-            return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier, c.Location);
-        } 
+            return new Completion(c.Type, lastValue ?? JsValue.Undefined, c.Identifier, c.Location);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-blockdeclarationinstantiation
+        /// </summary>
+        internal static void BlockDeclarationInstantiation(
+            LexicalEnvironment env,
+            List<VariableDeclaration> lexicalDeclarations)
+        {
+            var envRec = env._record;
+            for (var i = 0; i < lexicalDeclarations.Count; i++)
+            {
+                var variableDeclaration = lexicalDeclarations[i];
+                ref readonly var nodeList = ref variableDeclaration.Declarations;
+                for (var j = 0; j < nodeList.Count; j++)
+                {
+                    var declaration = nodeList[j];
+                    if (declaration.Id is Identifier identifier)
+                    {
+                        if (variableDeclaration.Kind == VariableDeclarationKind.Const)
+                        {
+                            envRec.CreateImmutableBinding(identifier.Name, strict: true);
+                        }
+                        else
+                        {
+                            envRec.CreateMutableBinding(identifier.Name, canBeDeleted: false);
+                        }
+                    }
+                    // else if 
+                    /*  If d is a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration, then
+                     * Let fn be the sole element of the BoundNames of d.
+                     * Let fo be the result of performing InstantiateFunctionObject for d with argument env.
+                     * Perform envRec.InitializeBinding(fn, fo).
+                     */
+                }
+            }
+        }
     }
 }

+ 31 - 5
Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs

@@ -1,19 +1,45 @@
+using System.Collections.Generic;
 using Esprima.Ast;
+using Jint.Runtime.Environments;
 
 namespace Jint.Runtime.Interpreter.Statements
 {
-    internal sealed class JintBlockStatement : JintStatement<Statement>
+    internal sealed class JintBlockStatement : JintStatement<BlockStatement>
     {
-        private readonly JintStatementList _statementList;
+        private JintStatementList _statementList;
+        private List<VariableDeclaration> _lexicalDeclarations;
 
-        public JintBlockStatement(Engine engine, JintStatementList statementList) : base(engine, null)
+        public JintBlockStatement(Engine engine, BlockStatement blockStatement) : base(engine, blockStatement)
         {
-            _statementList = statementList;
+            _initialized = false;
         }
 
+        protected override void Initialize()
+        {
+            _statementList = new JintStatementList(_engine, _statement, _statement.Body);
+            _lexicalDeclarations = HoistingScope.GetLexicalDeclarations(_statement);
+        }
+
+        // http://www.ecma-international.org/ecma-262/6.0/#sec-blockdeclarationinstantiation
         protected override Completion ExecuteInternal()
         {
-            return _statementList.Execute();
+            LexicalEnvironment oldEnv = null;
+            if (_lexicalDeclarations != null)
+            {
+                oldEnv = _engine.ExecutionContext.LexicalEnvironment;
+                var blockEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
+                JintStatementList.BlockDeclarationInstantiation(blockEnv, _lexicalDeclarations);
+                _engine.UpdateLexicalEnvironment(blockEnv);
+            }
+
+            var blockValue = _statementList.Execute();
+
+            if (oldEnv != null)
+            {
+                _engine.UpdateLexicalEnvironment(oldEnv);
+            }
+            
+            return blockValue;
         }
     }
 }

+ 368 - 0
Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs

@@ -0,0 +1,368 @@
+using System.Collections.Generic;
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Native.Iterator;
+using Jint.Native.Object;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Environments;
+using Jint.Runtime.Interpreter.Expressions;
+using Jint.Runtime.References;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-for-in-and-for-of-statements
+    /// </summary>
+    internal sealed class JintForInForOfStatement : JintStatement<Statement>
+    {
+        private readonly Node _leftNode;
+        private readonly Statement _forBody;
+        private readonly Expression _rightExpression;
+        private readonly IterationKind _iterationKind;
+
+        private JintStatement _body;
+        private JintExpression _expr;
+        private BindingPattern _assignmentPattern;
+        private JintExpression _right;
+        private List<string> _tdzNames;
+        private bool _destructuring;
+        private LhsKind _lhsKind;
+
+        public JintForInForOfStatement(
+            Engine engine, 
+            ForInStatement statement) : base(engine, statement)
+        {
+            _initialized = false;
+            _leftNode = statement.Left;
+            _rightExpression = statement.Right;
+            _forBody = statement.Body;
+            _iterationKind = IterationKind.Enumerate;
+        }
+
+        public JintForInForOfStatement(
+            Engine engine,
+            ForOfStatement statement) : base(engine, statement)
+        {
+            _initialized = false;
+            _leftNode = statement.Left;
+            _rightExpression = statement.Right;
+            _forBody = statement.Body;
+            _iterationKind = IterationKind.Iterate;
+        }
+
+        protected override void Initialize()
+        {
+            _lhsKind = LhsKind.Assignment;
+            if (_leftNode is VariableDeclaration variableDeclaration)
+            {
+                _lhsKind = variableDeclaration.Kind == VariableDeclarationKind.Var 
+                    ? LhsKind.VarBinding
+                    : LhsKind.LexicalBinding;
+
+                var variableDeclarationDeclaration = variableDeclaration.Declarations[0];
+                var id = variableDeclarationDeclaration.Id;
+                if (_lhsKind == LhsKind.LexicalBinding)
+                {
+                    _tdzNames = new List<string>(1);
+                    id.GetBoundNames(_tdzNames);
+                }
+
+                if (id is BindingPattern bindingPattern)
+                {
+                    _destructuring = true;
+                    _assignmentPattern = bindingPattern;
+                }
+                else
+                {
+                    var identifier = (Identifier) id;
+                    _expr = new JintIdentifierExpression(_engine, identifier);
+                }
+            }
+            else if (_leftNode is BindingPattern bindingPattern)
+            {
+                _destructuring = true;
+                _assignmentPattern = bindingPattern;
+            }
+            else if (_leftNode is MemberExpression memberExpression)
+            {
+                _expr = new JintMemberExpression(_engine, memberExpression);
+            }
+            else
+            {
+                _expr = new JintIdentifierExpression(_engine, (Identifier) _leftNode);
+            }
+
+            _body = Build(_engine, _forBody);
+            _right = JintExpression.Build(_engine, _rightExpression);
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            if (!HeadEvaluation(out var keyResult))
+            {
+                return new Completion(CompletionType.Normal, JsValue.Undefined, null, Location);
+            }
+
+            return BodyEvaluation(_expr, _body, keyResult, IterationKind.Enumerate, _lhsKind);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-forin-div-ofheadevaluation-tdznames-expr-iterationkind
+        /// </summary>
+        private bool HeadEvaluation(out IIterator result)
+        {
+            var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
+            var tdz = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
+            if (_tdzNames != null)
+            {
+                var TDZEnvRec = tdz._record;
+                foreach (var name in _tdzNames)
+                {
+                    TDZEnvRec.CreateMutableBinding(name);
+                }
+            }
+
+            _engine.UpdateLexicalEnvironment(tdz);
+            var exprRef = _right.Evaluate();
+            _engine.UpdateLexicalEnvironment(oldEnv);
+
+            var exprValue = _engine.GetValue(exprRef, true);
+            if (_iterationKind == IterationKind.Enumerate)
+            {
+                if (exprValue.IsNullOrUndefined())
+                {
+                    result = null;
+                    return false;
+                }
+
+                var obj = TypeConverter.ToObject(_engine, exprValue);
+                result = EnumeratorObjectProperties(obj);
+            }
+            else
+            {
+                result = exprValue as IIterator ?? exprValue.GetIterator(_engine);
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset
+        /// </summary>
+        private Completion BodyEvaluation(
+            JintExpression lhs,
+            JintStatement stmt, 
+            IIterator iteratorRecord,
+            IterationKind iterationKind,
+            LhsKind lhsKind,
+            IteratorKind iteratorKind = IteratorKind.Sync)
+        {
+            var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
+            var v = Undefined.Instance;
+            var destructuring = _destructuring;
+            string lhsName = null;
+
+            var completionType = CompletionType.Normal;
+            var close = false;
+
+            try
+            {
+                while (true)
+                {
+                    LexicalEnvironment iterationEnv = null;
+                    if (!iteratorRecord.TryIteratorStep(out var nextResult))
+                    {
+                        close = true;
+                        return new Completion(CompletionType.Normal, v, null, Location);
+                    }
+
+                    if (iteratorKind == IteratorKind.Async)
+                    {
+                        // nextResult = await nextResult;
+                    }
+
+                    var nextValue = nextResult.Get(CommonProperties.Value);
+                    close = true;
+
+                    Reference lhsRef = null;
+                    if (lhsKind != LhsKind.LexicalBinding)
+                    {
+                        if (!destructuring)
+                        {
+                            lhsRef = (Reference) lhs.Evaluate();
+                        }
+                    }
+                    else
+                    {
+                        iterationEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
+                        if (_tdzNames != null)
+                        {
+                            BindingInstantiation(iterationEnv);
+                        }
+                        _engine.UpdateLexicalEnvironment(iterationEnv);
+
+                        if (!destructuring)
+                        {
+                            lhsName ??= ((Identifier) ((VariableDeclaration) _leftNode).Declarations[0].Id).Name;
+                            lhsRef = _engine.ResolveBinding(lhsName);
+                        }
+                    }
+
+                    if (!destructuring)
+                    {
+                        // If lhsRef is an abrupt completion, then
+                        // Let status be lhsRef.
+
+                        if (lhsKind == LhsKind.LexicalBinding)
+                        {
+                            lhsRef.InitializeReferencedBinding(nextValue);
+                        }
+                        else
+                        {
+                            _engine.PutValue(lhsRef, nextValue);
+                        }
+                    }
+                    else
+                    {
+                        BindingPatternAssignmentExpression.ProcessPatterns(
+                            _engine,
+                            _assignmentPattern,
+                            nextValue,
+                            iterationEnv,
+                            checkObjectPatternPropertyReference: _lhsKind != LhsKind.VarBinding);
+
+                        if (lhsKind == LhsKind.Assignment)
+                        {
+                            // DestructuringAssignmentEvaluation of assignmentPattern using nextValue as the argument.
+                        }
+                        else if (lhsKind == LhsKind.VarBinding)
+                        {
+                            // BindingInitialization for lhs passing nextValue and undefined as the arguments.
+                        }
+                        else
+                        {
+                            // BindingInitialization for lhs passing nextValue and iterationEnv as arguments                            
+                        }
+                    }
+
+                    var result = stmt.Execute();
+                    _engine.UpdateLexicalEnvironment(oldEnv);
+                    
+                    if (!ReferenceEquals(result.Value, null))
+                    {
+                        v = result.Value;
+                    }
+
+                    if (result.Type == CompletionType.Break && (result.Identifier == null || result.Identifier == _statement?.LabelSet?.Name))
+                    {
+                        return new Completion(CompletionType.Normal, v, null, Location);
+                    }
+
+                    if (result.Type != CompletionType.Continue || (result.Identifier != null && result.Identifier != _statement?.LabelSet?.Name))
+                    {
+                        if (result.Type != CompletionType.Normal)
+                        {
+                            return result;
+                        }
+                    }
+                }
+            }
+            catch
+            {
+                completionType = CompletionType.Throw;
+                throw;
+            }
+            finally
+            {
+                if (close)
+                {
+                    iteratorRecord.Close(completionType);
+                }
+                _engine.UpdateLexicalEnvironment(oldEnv);
+            }
+        }
+
+        private void BindingInstantiation(LexicalEnvironment environment)
+        {
+            var envRec = (DeclarativeEnvironmentRecord) environment._record;
+            var variableDeclaration = (VariableDeclaration) _leftNode;
+            var boundNames = new List<string>();
+            variableDeclaration.GetBoundNames(boundNames);
+            for (var i = 0; i < boundNames.Count; i++)
+            {
+                var name = boundNames[i];
+                if (variableDeclaration.Kind == VariableDeclarationKind.Const)
+                {
+                    envRec.CreateImmutableBinding(name, strict: true);
+                }
+                else
+                {
+                    envRec.CreateMutableBinding(name, canBeDeleted: false);
+                }
+            }
+        }
+
+        private enum LhsKind
+        {
+            Assignment, 
+            VarBinding,
+            LexicalBinding
+        }
+
+        private enum IteratorKind
+        {
+            Sync,
+            Async
+        }
+
+        private IIterator EnumeratorObjectProperties(ObjectInstance obj)
+        {
+            return new ObjectKeyVisitor(_engine, obj);
+        }
+
+        private enum IterationKind
+        {
+            Enumerate,
+            Iterate,
+            AsyncIterate
+        }
+
+        internal class ObjectKeyVisitor : IteratorInstance
+        {
+            public ObjectKeyVisitor(Engine engine, ObjectInstance obj)
+                : base(engine, CreateEnumerator(engine, obj))
+            {
+            }
+
+            private static IEnumerable<JsValue> CreateEnumerator(Engine engine, ObjectInstance obj)
+            {
+                var visited = new HashSet<JsValue>();
+                foreach (var key in obj.GetOwnPropertyKeys(Types.String))
+                {
+                    var desc = obj.GetOwnProperty(key);
+                    if (desc != PropertyDescriptor.Undefined)
+                    {
+                        visited.Add(key);
+                        if (desc.Enumerable)
+                        {
+                            yield return key;
+                        }
+                    }
+                }
+
+                if (obj.Prototype is null)
+                {
+                    yield break;
+                }
+
+                foreach (var protoKey in CreateEnumerator(engine, obj.Prototype))
+                {
+                    if (!visited.Contains(protoKey))
+                    {
+                        yield return protoKey;
+                    }
+                }
+            }
+        }
+    }
+}

+ 0 - 110
Jint/Runtime/Interpreter/Statements/JintForInStatement.cs

@@ -1,110 +0,0 @@
-using System.Collections.Generic;
-using Esprima.Ast;
-using Jint.Native;
-using Jint.Runtime.Descriptors;
-using Jint.Runtime.Interpreter.Expressions;
-using Jint.Runtime.References;
-
-namespace Jint.Runtime.Interpreter.Statements
-{
-    /// <summary>
-    ///     http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.4
-    /// </summary>
-    internal sealed class JintForInStatement : JintStatement<ForInStatement>
-    {
-        private readonly JintStatement _body;
-        private readonly JintExpression _identifier;
-        private readonly JintExpression _right;
-
-        public JintForInStatement(Engine engine, ForInStatement statement) : base(engine, statement)
-        {
-            if (_statement.Left.Type == Nodes.VariableDeclaration)
-            {
-                _identifier = JintExpression.Build(engine, (Identifier) ((VariableDeclaration) _statement.Left).Declarations[0].Id);
-            }
-            else if (_statement.Left.Type == Nodes.MemberExpression)
-            {
-                _identifier = JintExpression.Build(engine, ((MemberExpression) _statement.Left));
-            }
-            else
-            {
-                _identifier = JintExpression.Build(engine, (Identifier) _statement.Left);
-            }
-
-            _body = Build(engine, _statement.Body);
-            _right = JintExpression.Build(engine, statement.Right);
-        }
-
-        protected override Completion ExecuteInternal()
-        {
-            var varRef = _identifier.Evaluate() as Reference;
-            var experValue = _right.GetValue();
-            if (experValue.IsNullOrUndefined())
-            {
-                return new Completion(CompletionType.Normal, null, null, Location);
-            }
-
-            var obj = TypeConverter.ToObject(_engine, experValue);
-            var v = Undefined.Instance;
-
-            // keys are constructed using the prototype chain
-            var cursor = obj;
-            var processedKeys = new HashSet<JsString>();
-
-            while (!ReferenceEquals(cursor, null))
-            {
-                var keys = _engine.Object.GetOwnPropertyNames(Undefined.Instance, Arguments.From(cursor)).AsArray();
-
-                var length = keys.GetLength();
-                for (uint i = 0; i < length; i++)
-                {
-                    var p = (JsString) keys.GetOwnProperty(i).Value;
-
-                    if (processedKeys.Contains(p))
-                    {
-                        continue;
-                    }
-
-                    processedKeys.Add(p);
-
-                    // collection might be modified by inner statement
-                    if (cursor.GetOwnProperty(p) == PropertyDescriptor.Undefined)
-                    {
-                        continue;
-                    }
-
-                    var value = cursor.GetOwnProperty(p);
-                    if (!value.Enumerable)
-                    {
-                        continue;
-                    }
-
-                    _engine.PutValue(varRef, p);
-
-                    var stmt = _body.Execute();
-                    if (!ReferenceEquals(stmt.Value, null))
-                    {
-                        v = stmt.Value;
-                    }
-
-                    if (stmt.Type == CompletionType.Break)
-                    {
-                        return new Completion(CompletionType.Normal, v, null, Location);
-                    }
-
-                    if (stmt.Type != CompletionType.Continue)
-                    {
-                        if (stmt.Type != CompletionType.Normal)
-                        {
-                            return stmt;
-                        }
-                    }
-                }
-
-                cursor = cursor.Prototype;
-            }
-
-            return new Completion(CompletionType.Normal, v, null, Location);
-        }
-    }
-}

+ 0 - 140
Jint/Runtime/Interpreter/Statements/JintForOfStatement.cs

@@ -1,140 +0,0 @@
-using Esprima.Ast;
-using Jint.Native;
-using Jint.Native.Iterator;
-using Jint.Runtime.Interpreter.Expressions;
-using Jint.Runtime.References;
-
-namespace Jint.Runtime.Interpreter.Statements
-{
-    /// <summary>
-    ///     https://www.ecma-international.org/ecma-262/6.0/#sec-for-in-and-for-of-statements
-    /// </summary>
-    internal sealed class JintForOfStatement : JintStatement<ForOfStatement>
-    {
-        private JintStatement _body;
-        private JintExpression _identifier;
-        private BindingPattern _leftPattern;
-
-        private JintExpression _right;
-
-        public JintForOfStatement(Engine engine, ForOfStatement statement) : base(engine, statement)
-        {
-            _initialized = false;
-        }
-
-        protected override void Initialize()
-        {
-            if (_statement.Left is VariableDeclaration variableDeclaration)
-            {
-                var element = variableDeclaration.Declarations[0].Id;
-                if (element is BindingPattern bindingPattern)
-                {
-                    _leftPattern = bindingPattern;
-                }
-                else
-                {
-                    _identifier = JintExpression.Build(_engine, (Identifier) element);
-                }
-            }
-            else if (_statement.Left is BindingPattern bindingPattern)
-            {
-                _leftPattern = bindingPattern;
-            }
-            else
-            {
-                _identifier = JintExpression.Build(_engine, (Expression) _statement.Left);
-            }
-
-            _body = Build(_engine, _statement.Body);
-            _right = JintExpression.Build(_engine, _statement.Right);
-        }
-
-        protected override Completion ExecuteInternal()
-        {
-            var experValue = _right.GetValue();
-
-            if (!(experValue is IIterator iterator))
-            {
-                var obj = TypeConverter.ToObject(_engine, experValue);
-                obj.TryGetIterator(_engine, out iterator);
-            }
-
-            if (iterator is null)
-            {
-                return ExceptionHelper.ThrowTypeError<Completion>(_engine, _identifier + " is not iterable");
-            }
-
-            var completionType = CompletionType.Normal;
-            var v = JsValue.Undefined;
-            var close = false;
-            try
-            {
-                do
-                {
-                    iterator.TryIteratorStep(out var item);
-                    var done = item.Get(CommonProperties.Done);
-                    if (TypeConverter.ToBoolean(done))
-                    {
-                        // we can close after checks pass
-                        close = true;
-                        break;
-                    }
-
-                    var currentValue = item.Get(CommonProperties.Value);
-
-                    // we can close after checks pass
-                    close = true;
-
-                    if (_leftPattern != null)
-                    {
-                        BindingPatternAssignmentExpression.ProcessPatterns(
-                            _engine,
-                            _leftPattern,
-                            currentValue,
-                            checkReference: !(_statement.Left is VariableDeclaration));
-                    }
-                    else
-                    {
-                        var varRef = (Reference) _identifier.Evaluate();
-                        _engine.PutValue(varRef, currentValue);
-                    }
-
-                    var stmt = _body.Execute();
-
-                    if (!ReferenceEquals(stmt.Value, null))
-                    {
-                        v = stmt.Value;
-                    }
-
-                    if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == _statement?.LabelSet?.Name))
-                    {
-                        return new Completion(CompletionType.Normal, stmt.Value, null, Location);
-                    }
-
-                    if (stmt.Type != CompletionType.Continue || ((stmt.Identifier != null) && stmt.Identifier != _statement?.LabelSet?.Name))
-                    {
-                        if (stmt.Type != CompletionType.Normal)
-                        {
-                            return stmt;
-                        }
-                    }
-                    
-                } while (true);
-            }
-            catch
-            {
-                completionType = CompletionType.Throw;
-                throw;
-            }   
-            finally
-            {
-                if (close)
-                {
-                    iterator.Close(completionType);
-                }
-            }
-
-            return new Completion(CompletionType.Normal, v, null, Location);
-        }
-    }
-}

+ 122 - 23
Jint/Runtime/Interpreter/Statements/JintForStatement.cs

@@ -1,53 +1,124 @@
+using System.Collections.Generic;
 using Esprima.Ast;
 using Jint.Native;
+using Jint.Runtime.Environments;
 using Jint.Runtime.Interpreter.Expressions;
 
 namespace Jint.Runtime.Interpreter.Statements
 {
     /// <summary>
-    /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.3
+    /// https://tc39.es/ecma262/#sec-forbodyevaluation
     /// </summary>
     internal sealed class JintForStatement : JintStatement<ForStatement>
     {
-        private readonly JintStatement _body;
-        private readonly JintStatement _initStatement;
-        private readonly JintExpression _initExpression;
-        private readonly JintExpression _test;
-        private readonly JintExpression _update;
+        private JintVariableDeclaration _initStatement;
+        private JintExpression _initExpression;
+        
+        private JintExpression _test;
+        private JintExpression _increment;
+        
+        private JintStatement _body;
+        private List<string> _boundNames;
 
         public JintForStatement(Engine engine, ForStatement statement) : base(engine, statement)
         {
-            _body = Build(engine, _statement.Body);
+            _initialized = false;
+        }
+
+        protected override void Initialize()
+        {
+            _body = Build(_engine, _statement.Body);
 
             if (_statement.Init != null)
             {
                 if (_statement.Init.Type == Nodes.VariableDeclaration)
                 {
-                    _initStatement = Build(engine, (Statement) _statement.Init);
+                    var d = (VariableDeclaration) _statement.Init;
+                    if (d.Kind != VariableDeclarationKind.Var)
+                    {
+                        _boundNames = new List<string>();
+                        d.GetBoundNames(_boundNames);
+                    }
+                    _initStatement = new JintVariableDeclaration(_engine, d);
                 }
                 else
                 {
-                    _initExpression = JintExpression.Build(engine, (Expression) statement.Init);
+                    _initExpression = JintExpression.Build(_engine, (Expression) _statement.Init);
                 }
             }
 
             if (_statement.Test != null)
             {
-                _test = JintExpression.Build(engine, statement.Test);
+                _test = JintExpression.Build(_engine, _statement.Test);
             }
 
             if (_statement.Update != null)
             {
-                _update = JintExpression.Build(engine, statement.Update);
+                _increment = JintExpression.Build(_engine, _statement.Update);
             }
         }
 
         protected override Completion ExecuteInternal()
         {
-            _initStatement?.Execute();
-            _initExpression?.GetValue();
+            LexicalEnvironment oldEnv = null;
+            LexicalEnvironment loopEnv = null;
+            if (_boundNames != null)
+            {
+                oldEnv = _engine.ExecutionContext.LexicalEnvironment;
+                loopEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
+                var loopEnvRec = loopEnv._record;
+                var kind = _initStatement._statement.Kind;
+                for (var i = 0; i < _boundNames.Count; i++)
+                {
+                    var name = _boundNames[i];
+                    if (kind == VariableDeclarationKind.Const)
+                    {
+                        loopEnvRec.CreateImmutableBinding(name, true);
+                    }
+                    else
+                    {
+                        loopEnvRec.CreateMutableBinding(name, false);
+                    }
+                }
+
+                _engine.UpdateLexicalEnvironment(loopEnv);
+            }
+
+            try
+            {
+                if (_initExpression != null)
+                {
+                    _initExpression?.GetValue();
+                }
+                else
+                {
+                    _initStatement?.Execute();
+                }
+
+                return ForBodyEvaluation();
+            }
+            finally
+            {
+                if (oldEnv != null)
+                {
+                    _engine.UpdateLexicalEnvironment(oldEnv);
+                }
+            }
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-forbodyevaluation
+        /// </summary>
+        private Completion ForBodyEvaluation()
+        {
+            var v = Undefined.Instance;
+
+            var shouldCreatePerIterationEnvironment = _initStatement?._statement?.Kind == VariableDeclarationKind.Let;
+            if (shouldCreatePerIterationEnvironment)
+            {
+                CreatePerIterationEnvironment();
+            }
 
-            JsValue v = Undefined.Instance;
             while (true)
             {
                 if (_test != null)
@@ -58,27 +129,55 @@ namespace Jint.Runtime.Interpreter.Statements
                     }
                 }
 
-                var stmt = _body.Execute();
-                if (!ReferenceEquals(stmt.Value, null))
+                var result = _body.Execute();
+                if (!ReferenceEquals(result.Value, null))
                 {
-                    v = stmt.Value;
+                    v = result.Value;
                 }
 
-                if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == _statement?.LabelSet?.Name))
+                if (result.Type == CompletionType.Break && (result.Identifier == null || result.Identifier == _statement?.LabelSet?.Name))
                 {
-                    return new Completion(CompletionType.Normal, stmt.Value, null, Location);
+                    return new Completion(CompletionType.Normal, result.Value, null, Location);
                 }
 
-                if (stmt.Type != CompletionType.Continue || ((stmt.Identifier != null) && stmt.Identifier != _statement?.LabelSet?.Name))
+                if (result.Type != CompletionType.Continue || (result.Identifier != null && result.Identifier != _statement?.LabelSet?.Name))
                 {
-                    if (stmt.Type != CompletionType.Normal)
+                    if (result.Type != CompletionType.Normal)
                     {
-                        return stmt;
+                        return result;
                     }
                 }
 
-                _update?.GetValue();
+                if (shouldCreatePerIterationEnvironment)
+                {
+                    CreatePerIterationEnvironment();
+                }
+
+                _increment?.GetValue();
             }
         }
+
+        private void CreatePerIterationEnvironment()
+        {
+            if (_boundNames == null || _boundNames.Count == 0)
+            {
+                return;
+            }
+            
+            var lastIterationEnv = _engine.ExecutionContext.LexicalEnvironment;
+            var lastIterationEnvRec = lastIterationEnv._record;
+            var outer = lastIterationEnv._outer;
+            var thisIterationEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, outer);
+            var thisIterationEnvRec = (DeclarativeEnvironmentRecord) thisIterationEnv._record;
+            
+            for (var j = 0; j < _boundNames.Count; j++)
+            {
+                var bn = _boundNames[j];
+                var lastValue = lastIterationEnvRec.GetBindingValue(bn, true);
+                thisIterationEnvRec.CreateMutableBindingAndInitialize(bn, false, lastValue);
+            }
+
+            _engine.UpdateLexicalEnvironment(thisIterationEnv);
+        }
     }
 }

+ 31 - 72
Jint/Runtime/Interpreter/Statements/JintStatement.cs

@@ -7,7 +7,7 @@ namespace Jint.Runtime.Interpreter.Statements
 {
     internal abstract class JintStatement<T> : JintStatement where T : Statement
     {
-        protected readonly T _statement;
+        internal readonly T _statement;
 
         protected JintStatement(Engine engine, T statement) : base(engine, statement)
         {
@@ -32,8 +32,11 @@ namespace Jint.Runtime.Interpreter.Statements
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public Completion Execute()
         {
-            _engine._lastSyntaxNode = _statement;
-            _engine.RunBeforeExecuteStatementChecks(_statement);
+            if (_statement.Type != Nodes.BlockStatement)
+            {
+                _engine._lastSyntaxNode = _statement;
+                _engine.RunBeforeExecuteStatementChecks(_statement);
+            }
 
             if (!_initialized)
             {
@@ -57,78 +60,34 @@ namespace Jint.Runtime.Interpreter.Statements
 
         protected internal static JintStatement Build(Engine engine, Statement statement)
         {
-            switch (statement.Type)
+            return statement.Type switch
             {
-                case Nodes.BlockStatement:
-                    var statementListItems = ((BlockStatement) statement).Body;
-                    return new JintBlockStatement(engine, new JintStatementList(engine, statement, statementListItems));
-
-                case Nodes.ReturnStatement:
-                    return new JintReturnStatement(engine, (ReturnStatement) statement);
-
-                case Nodes.VariableDeclaration:
-                    return new JintVariableDeclaration(engine, (VariableDeclaration) statement);
-
-                case Nodes.BreakStatement:
-                    return new JintBreakStatement(engine, (BreakStatement) statement);
-
-                case Nodes.ContinueStatement:
-                    return new JintContinueStatement(engine, (ContinueStatement) statement);
-
-                case Nodes.DoWhileStatement:
-                    return new JintDoWhileStatement(engine, (DoWhileStatement) statement);
-
-                case Nodes.EmptyStatement:
-                    return new JintEmptyStatement(engine, (EmptyStatement) statement);
-
-                case Nodes.ExpressionStatement:
-                    return new JintExpressionStatement(engine, (ExpressionStatement) statement);
-
-                case Nodes.ForStatement:
-                    return new JintForStatement(engine, (ForStatement) statement);
-
-                case Nodes.ForInStatement:
-                    return new JintForInStatement(engine, (ForInStatement) statement);
-
-                case Nodes.ForOfStatement:
-                    return new JintForOfStatement(engine, (ForOfStatement) statement);
-
-                case Nodes.IfStatement:
-                    return new JintIfStatement(engine, (IfStatement) statement);
-
-                case Nodes.LabeledStatement:
-                    return new JintLabeledStatement(engine, (LabeledStatement) statement);
-
-                case Nodes.SwitchStatement:
-                    return new JintSwitchStatement(engine, (SwitchStatement) statement);
-
-                case Nodes.FunctionDeclaration:
-                    return new JintFunctionDeclarationStatement(engine, (FunctionDeclaration) statement);
-
-                case Nodes.ThrowStatement:
-                    return new JintThrowStatement(engine, (ThrowStatement) statement);
-
-                case Nodes.TryStatement:
-                    return new JintTryStatement(engine, (TryStatement) statement);
-
-                case Nodes.WhileStatement:
-                    return new JintWhileStatement(engine, (WhileStatement) statement);
-
-                case Nodes.WithStatement:
-                    return new JintWithStatement(engine, (WithStatement) statement);
-
-                case Nodes.DebuggerStatement:
-                    return new JintDebuggerStatement(engine, (DebuggerStatement) statement);
-
-                case Nodes.Program:
-                    return new JintScript(engine, statement as Script ?? ExceptionHelper.ThrowArgumentException<Script>("modules not supported"));
-
-                default:
-                    return ExceptionHelper.ThrowArgumentOutOfRangeException<JintStatement>();
-            }
+                Nodes.BlockStatement => new JintBlockStatement(engine, (BlockStatement) statement),
+                Nodes.ReturnStatement => new JintReturnStatement(engine, (ReturnStatement) statement),
+                Nodes.VariableDeclaration => new JintVariableDeclaration(engine, (VariableDeclaration) statement),
+                Nodes.BreakStatement => new JintBreakStatement(engine, (BreakStatement) statement),
+                Nodes.ContinueStatement => new JintContinueStatement(engine, (ContinueStatement) statement),
+                Nodes.DoWhileStatement => new JintDoWhileStatement(engine, (DoWhileStatement) statement),
+                Nodes.EmptyStatement => new JintEmptyStatement(engine, (EmptyStatement) statement),
+                Nodes.ExpressionStatement => new JintExpressionStatement(engine, (ExpressionStatement) statement),
+                Nodes.ForStatement => new JintForStatement(engine, (ForStatement) statement),
+                Nodes.ForInStatement => new JintForInForOfStatement(engine, (ForInStatement) statement),
+                Nodes.ForOfStatement => new JintForInForOfStatement(engine, (ForOfStatement) statement),
+                Nodes.IfStatement => new JintIfStatement(engine, (IfStatement) statement),
+                Nodes.LabeledStatement => new JintLabeledStatement(engine, (LabeledStatement) statement),
+                Nodes.SwitchStatement => new JintSwitchStatement(engine, (SwitchStatement) statement),
+                Nodes.FunctionDeclaration => new JintFunctionDeclarationStatement(engine, (FunctionDeclaration) statement),
+                Nodes.ThrowStatement => new JintThrowStatement(engine, (ThrowStatement) statement),
+                Nodes.TryStatement => new JintTryStatement(engine, (TryStatement) statement),
+                Nodes.WhileStatement => new JintWhileStatement(engine, (WhileStatement) statement),
+                Nodes.WithStatement => new JintWithStatement(engine, (WithStatement) statement),
+                Nodes.DebuggerStatement => new JintDebuggerStatement(engine, (DebuggerStatement) statement),
+                Nodes.Program => new JintScript(engine, statement as Script ?? ExceptionHelper.ThrowArgumentException<Script>("modules not supported")),
+                _ => ExceptionHelper.ThrowArgumentOutOfRangeException<JintStatement>(nameof(statement.Type), $"unsupported statement type '{statement.Type}'")
+            };
         }
 
-        internal static Completion? FastResolve(IStatementListItem statement)
+        internal static Completion? FastResolve(StatementListItem statement)
         {
             if (statement is ReturnStatement rs && rs.Argument is Literal l)
             {

+ 34 - 0
Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs

@@ -1,6 +1,8 @@
+using System.Collections.Generic;
 using Esprima;
 using Esprima.Ast;
 using Jint.Native;
+using Jint.Runtime.Environments;
 using Jint.Runtime.Interpreter.Expressions;
 
 namespace Jint.Runtime.Interpreter.Statements
@@ -43,6 +45,16 @@ namespace Jint.Runtime.Interpreter.Statements
             for (var i = 0; i < (uint) _jintSwitchBlock.Length; i++)
             {
                 var clause = _jintSwitchBlock[i];
+
+                LexicalEnvironment oldEnv = null;
+                if (clause.LexicalDeclarations != null)
+                {
+                    oldEnv = _engine.ExecutionContext.LexicalEnvironment;
+                    var blockEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
+                    JintStatementList.BlockDeclarationInstantiation(blockEnv, clause.LexicalDeclarations);
+                    _engine.UpdateLexicalEnvironment(blockEnv);
+                }
+
                 if (clause.Test == null)
                 {
                     defaultCase = clause;
@@ -59,6 +71,12 @@ namespace Jint.Runtime.Interpreter.Statements
                 if (hit && clause.Consequent != null)
                 {
                     var r = clause.Consequent.Execute();
+
+                    if (oldEnv != null)
+                    {
+                        _engine.UpdateLexicalEnvironment(oldEnv);
+                    }
+
                     if (r.Type != CompletionType.Normal)
                     {
                         return r;
@@ -72,7 +90,21 @@ namespace Jint.Runtime.Interpreter.Statements
             // do we need to execute the default case ?
             if (hit == false && defaultCase != null)
             {
+                LexicalEnvironment oldEnv = null;
+                if (defaultCase.LexicalDeclarations != null)
+                {
+                    oldEnv = _engine.ExecutionContext.LexicalEnvironment;
+                    var blockEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
+                    JintStatementList.BlockDeclarationInstantiation(blockEnv, defaultCase.LexicalDeclarations);
+                    _engine.UpdateLexicalEnvironment(blockEnv);
+                }
+
                 var r = defaultCase.Consequent.Execute();
+
+                if (oldEnv != null)
+                {
+                    _engine.UpdateLexicalEnvironment(oldEnv);
+                }
                 if (r.Type != CompletionType.Normal)
                 {
                     return r;
@@ -89,10 +121,12 @@ namespace Jint.Runtime.Interpreter.Statements
         {
             internal readonly JintStatementList Consequent;
             internal readonly JintExpression Test;
+            internal readonly List<VariableDeclaration> LexicalDeclarations;
 
             public JintSwitchCase(Engine engine, SwitchCase switchCase)
             {
                 Consequent = new JintStatementList(engine, null, switchCase.Consequent);
+                LexicalDeclarations = HoistingScope.GetLexicalDeclarations(switchCase);
 
                 if (switchCase.Test != null)
                 {

+ 9 - 4
Jint/Runtime/Interpreter/Statements/JintSwitchStatement.cs

@@ -8,13 +8,18 @@ namespace Jint.Runtime.Interpreter.Statements
     /// </summary>
     internal sealed class JintSwitchStatement : JintStatement<SwitchStatement>
     {
-        private readonly JintSwitchBlock _switchBlock;
-        private readonly JintExpression _discriminant;
+        private JintSwitchBlock _switchBlock;
+        private JintExpression _discriminant;
 
         public JintSwitchStatement(Engine engine, SwitchStatement statement) : base(engine, statement)
         {
-            _switchBlock = new JintSwitchBlock(engine, _statement.Cases);
-            _discriminant = JintExpression.Build(engine, _statement.Discriminant);
+            _initialized = false;
+        }
+
+        protected override void Initialize()
+        {
+            _switchBlock = new JintSwitchBlock(_engine, _statement.Cases);
+            _discriminant = JintExpression.Build(_engine, _statement.Discriminant);
         }
 
         protected override Completion ExecuteInternal()

+ 3 - 2
Jint/Runtime/Interpreter/Statements/JintTryStatement.cs

@@ -10,7 +10,7 @@ namespace Jint.Runtime.Interpreter.Statements
     {
         private readonly JintStatement _block;
         private readonly JintStatement _catch;
-        private readonly string _catchParamName;
+        private readonly Key _catchParamName;
         private readonly JintStatement _finalizer;
 
         public JintTryStatement(Engine engine, TryStatement statement) : base(engine, statement)
@@ -39,7 +39,8 @@ namespace Jint.Runtime.Interpreter.Statements
                     var c = b.Value;
                     var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
                     var catchEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
-                    catchEnv._record.CreateMutableBinding(_catchParamName, c);
+                    var catchEnvRecord = (DeclarativeEnvironmentRecord) catchEnv._record;
+                    catchEnvRecord.CreateMutableBindingAndInitialize(_catchParamName, canBeDeleted: false, c);
 
                     _engine.UpdateLexicalEnvironment(catchEnv);
                     b = _catch.Execute();

+ 49 - 21
Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs

@@ -8,7 +8,7 @@ namespace Jint.Runtime.Interpreter.Statements
 {
     internal sealed class JintVariableDeclaration : JintStatement<VariableDeclaration>
     {
-        private static readonly Completion VoidCompletion = new Completion(CompletionType.Normal, Undefined.Instance, null, default);
+        private static readonly Completion VoidCompletion = new Completion(CompletionType.Normal, null, null, default);
 
         private ResolvedDeclaration[] _declarations;
 
@@ -17,7 +17,7 @@ namespace Jint.Runtime.Interpreter.Statements
             internal JintExpression Left;
             internal BindingPattern LeftPattern;
             internal JintExpression Init;
-            internal JintIdentifierExpression LeftIdentifier;
+            internal JintIdentifierExpression LeftIdentifierExpression;
             internal bool EvalOrArguments;
         }
 
@@ -36,26 +36,27 @@ namespace Jint.Runtime.Interpreter.Statements
                 JintExpression left = null;
                 JintExpression init = null;
                 BindingPattern bindingPattern = null;
+
+                if (declaration.Id is BindingPattern bp)
+                {
+                    bindingPattern = bp;
+                }
+                else
+                {
+                    left = JintExpression.Build(_engine, declaration.Id);
+                }
+                
                 if (declaration.Init != null)
                 {
-                    if (declaration.Id is Expression expression)
-                    {
-                        left = JintExpression.Build(_engine, expression);
-                    }
-                    else if (declaration.Id is BindingPattern bp)
-                    {
-                        bindingPattern = bp;
-                    }
-                        
                     init = JintExpression.Build(_engine, declaration.Init);
                 }
-
+                
                 var leftIdentifier = left as JintIdentifierExpression;
                 _declarations[i] = new ResolvedDeclaration
                 {
                     Left = left,
                     LeftPattern = bindingPattern,
-                    LeftIdentifier = leftIdentifier,
+                    LeftIdentifierExpression = leftIdentifier,
                     EvalOrArguments = leftIdentifier?.HasEvalOrArguments == true,
                     Init = init
                 };
@@ -64,24 +65,51 @@ namespace Jint.Runtime.Interpreter.Statements
 
         protected override Completion ExecuteInternal()
         {
+            if (!_initialized)
+            {
+                _initialized = true;
+                Initialize();
+            }
+            
             foreach (var declaration in _declarations)
             {
-                if (declaration.Init != null)
+                if (_statement.Kind != VariableDeclarationKind.Var && declaration.Left != null)
+                {
+                    var lhs = (Reference) declaration.Left.Evaluate();
+                    var value = JsValue.Undefined;
+                    if (declaration.Init != null)
+                    {
+                        value = declaration.Init.GetValue().Clone();
+                        if (declaration.Init._expression.IsFunctionWithName())
+                        {
+                            ((FunctionInstance) value).SetFunctionName(lhs.GetReferencedName());
+                        }
+                    }
+
+                    lhs.InitializeReferencedBinding(value);
+                    _engine._referencePool.Return(lhs);
+                }
+                else if (declaration.Init != null)
                 {
                     if (declaration.LeftPattern != null)
                     {
+                        var environment = _statement.Kind != VariableDeclarationKind.Var
+                            ? _engine.ExecutionContext.LexicalEnvironment
+                            : null;
+
                         BindingPatternAssignmentExpression.ProcessPatterns(
                             _engine,
                             declaration.LeftPattern,
                             declaration.Init.GetValue(),
-                            checkReference: false /* we are variable assignment*/);
+                            environment,
+                            checkObjectPatternPropertyReference: _statement.Kind != VariableDeclarationKind.Var);
                     }
-                    else if (declaration.LeftIdentifier == null
-                        || JintAssignmentExpression.SimpleAssignmentExpression.AssignToIdentifier(
-                            _engine,
-                            declaration.LeftIdentifier,
-                            declaration.Init,
-                            declaration.EvalOrArguments) is null)
+                    else if (declaration.LeftIdentifierExpression == null
+                             || JintAssignmentExpression.SimpleAssignmentExpression.AssignToIdentifier(
+                                 _engine,
+                                 declaration.LeftIdentifierExpression,
+                                 declaration.Init,
+                                 declaration.EvalOrArguments) is null)
                     {
                         // slow path
                         var lhs = (Reference) declaration.Left.Evaluate();

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

@@ -23,7 +23,7 @@ namespace Jint.Runtime.Interpreter.Statements
             var jsValue = _object.GetValue();
             var obj = TypeConverter.ToObject(_engine, jsValue);
             var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
-            var newEnv = LexicalEnvironment.NewObjectEnvironment(_engine, obj, oldEnv, true);
+            var newEnv = LexicalEnvironment.NewObjectEnvironment(_engine, obj, oldEnv, provideThis: true, withEnvironment: true);
             _engine.UpdateLexicalEnvironment(newEnv);
 
             Completion c;

+ 2 - 3
Jint/Runtime/KnownKeys.cs

@@ -2,8 +2,7 @@ namespace Jint.Runtime
 {
     internal static class KnownKeys
     {
-        private static readonly Key _arguments = "arguments";
-
-        internal static ref readonly Key Arguments => ref _arguments;
+        internal static readonly Key Arguments = "arguments";
+        internal static readonly Key Eval = "eval";
     }
 }

+ 5 - 0
Jint/Runtime/RefStack.cs

@@ -88,5 +88,10 @@ namespace Jint.Runtime
         {
             _array[_size - 1] = _array[_size - 1].UpdateLexicalEnvironment(newEnv);
         }
+
+        public void ReplaceTopVariableEnvironment(LexicalEnvironment newEnv)
+        {
+            _array[_size - 1] = _array[_size - 1].UpdateVariableEnvironment(newEnv);
+        }
     }
 }

+ 16 - 1
Jint/Runtime/References/Reference.cs

@@ -51,9 +51,19 @@ namespace Jint.Runtime.References
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsPropertyReference()
         {
-            // http://www.ecma-international.org/ecma-262/#sec-ispropertyreference
+            // https://tc39.es/ecma262/#sec-ispropertyreference
             return (_baseValue._type & (InternalTypes.Primitive | InternalTypes.Object)) != 0;
         }
+        
+        public JsValue GetThisValue()
+        {
+            if (IsSuperReference())
+            {
+                return ExceptionHelper.ThrowNotImplementedException<JsValue>();
+            }
+
+            return GetBase();
+        }
 
         internal Reference Reassign(JsValue baseValue, JsValue name, bool strict)
         {
@@ -73,5 +83,10 @@ namespace Jint.Runtime.References
                 ExceptionHelper.ThrowSyntaxError(engine);
             }
         }
+
+        internal void InitializeReferencedBinding(JsValue value)
+        {
+            ((EnvironmentRecord) _baseValue).InitializeBinding(TypeConverter.ToString(_property), value);
+        }
     }
 }

+ 24 - 40
Jint/Runtime/TypeConverter.cs

@@ -77,7 +77,7 @@ namespace Jint.Runtime
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-toprimitive
+        /// https://tc39.es/ecma262/#sec-toprimitive
         /// </summary>
         public static JsValue ToPrimitive(JsValue input, Types preferredType = Types.None)
         {
@@ -115,7 +115,7 @@ namespace Jint.Runtime
         private static readonly JsString[] NumberHintCallOrder = { (JsString) "valueOf", (JsString) "toString"};
         
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-ordinarytoprimitive
+        /// https://tc39.es/ecma262/#sec-ordinarytoprimitive
         /// </summary>
         internal static JsValue OrdinaryToPrimitive(ObjectInstance input, Types hint = Types.None)
         {
@@ -290,7 +290,7 @@ namespace Jint.Runtime
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-tolength
+        /// https://tc39.es/ecma262/#sec-tolength
         /// </summary>
         public static ulong ToLength(JsValue o)
         {
@@ -304,7 +304,7 @@ namespace Jint.Runtime
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/#sec-tointeger
+        /// https://tc39.es/ecma262/#sec-tointeger
         /// </summary>
         public static double ToInteger(JsValue o)
         {
@@ -474,50 +474,34 @@ namespace Jint.Runtime
         private static string ToStringNonString(JsValue o)
         {
             var type = o._type & ~InternalTypes.InternalFlags;
-            switch (type)
+            return type switch
             {
-                case InternalTypes.Boolean:
-                    return ((JsBoolean) o)._value ? "true" : "false";
-                case InternalTypes.Integer:
-                    return ToString((int) ((JsNumber) o)._value);
-                case InternalTypes.Number:
-                    return ToString(((JsNumber) o)._value);
-                case InternalTypes.Symbol:
-                    return ExceptionHelper.ThrowTypeErrorNoEngine<string>("Cannot convert a Symbol value to a string");
-                case InternalTypes.Undefined:
-                    return Undefined.Text;
-                case InternalTypes.Null:
-                    return Null.Text;
-                case InternalTypes.Object when o is IPrimitiveInstance p:
-                    return ToString(ToPrimitive(p.PrimitiveValue, Types.String));
-                case InternalTypes.Object when o is Interop.IObjectWrapper p:
-                    return p.Target?.ToString();
-                default:
-                    return ToString(ToPrimitive(o, Types.String));
-            }
+                InternalTypes.Boolean => ((JsBoolean) o)._value ? "true" : "false",
+                InternalTypes.Integer => ToString((int) ((JsNumber) o)._value),
+                InternalTypes.Number => ToString(((JsNumber) o)._value),
+                InternalTypes.Symbol => ExceptionHelper.ThrowTypeErrorNoEngine<string>("Cannot convert a Symbol value to a string"),
+                InternalTypes.Undefined => Undefined.Text,
+                InternalTypes.Null => Null.Text,
+                InternalTypes.Object when o is IPrimitiveInstance p => ToString(ToPrimitive(p.PrimitiveValue, Types.String)),
+                InternalTypes.Object when o is Interop.IObjectWrapper p => p.Target?.ToString(),
+                _ => ToString(ToPrimitive(o, Types.String))
+            };
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static ObjectInstance ToObject(Engine engine, JsValue value)
         {
             var type = value._type & ~InternalTypes.InternalFlags;
-            switch (type)
+            return type switch
             {
-                case InternalTypes.Object:
-                    return (ObjectInstance) value;
-                case InternalTypes.Boolean:
-                    return engine.Boolean.Construct(((JsBoolean) value)._value);
-                case InternalTypes.Number:
-                case InternalTypes.Integer:
-                    return engine.Number.Construct(((JsNumber) value)._value);
-                case InternalTypes.String:
-                    return engine.String.Construct(value.AsStringWithoutTypeCheck());
-                case InternalTypes.Symbol:
-                    return engine.Symbol.Construct(((JsSymbol) value));
-                default:
-                    ExceptionHelper.ThrowTypeError(engine);
-                    return null;
-            }
+                InternalTypes.Object => (ObjectInstance) value,
+                InternalTypes.Boolean => engine.Boolean.Construct(((JsBoolean) value)._value),
+                InternalTypes.Number => engine.Number.Construct(((JsNumber) value)._value),
+                InternalTypes.Integer => engine.Number.Construct(((JsNumber) value)._value),
+                InternalTypes.String => engine.String.Construct(value.AsStringWithoutTypeCheck()),
+                InternalTypes.Symbol => engine.Symbol.Construct(((JsSymbol) value)),
+                _ => ExceptionHelper.ThrowTypeError<ObjectInstance>(engine)
+            };
         }
         
         internal static void CheckObjectCoercible(