소스 검색

ES6 Map, Set and initial Iterators (#541)

Marko Lahma 7 년 전
부모
커밋
6353bb42f7
76개의 변경된 파일2800개의 추가작업 그리고 440개의 파일을 삭제
  1. 1 1
      Jint.Benchmark/Jint.Benchmark.csproj
  2. 5 5
      Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj
  3. 5 5
      Jint.Tests.Ecma/Jint.Tests.Ecma.csproj
  4. 34 0
      Jint.Tests.Ecma/SingleTest.cs
  5. 12 12
      Jint.Tests.Ecma/TestCases/alltests.json
  6. 4 2
      Jint.Tests.Ecma/TestCases/ch15/15.1/15.1.2/15.1.2.2/S15.1.2.2_A2_T10.js
  7. 4 2
      Jint.Tests.Ecma/TestCases/ch15/15.1/15.1.2/15.1.2.3/S15.1.2.3_A2_T10.js
  8. 15 0
      Jint.Tests.Test262/ArrayTests.cs
  9. 15 0
      Jint.Tests.Test262/BooleanTests.cs
  10. 5 5
      Jint.Tests.Test262/Jint.Tests.Test262.csproj
  11. 15 0
      Jint.Tests.Test262/MapTests.cs
  12. 15 0
      Jint.Tests.Test262/ParseFloatTests.cs
  13. 15 0
      Jint.Tests.Test262/ParseIntTests.cs
  14. 15 0
      Jint.Tests.Test262/SetTests.cs
  15. 34 0
      Jint.Tests.Test262/SingleTest.cs
  16. 39 0
      Jint.Tests.Test262/UriTests.cs
  17. 96 8
      Jint.Tests.Test262/test/skipped.json
  18. 5 5
      Jint.Tests/Jint.Tests.csproj
  19. 1 1
      Jint.Tests/Runtime/NullPropagation.cs
  20. 9 1
      Jint.sln.DotSettings
  21. 31 13
      Jint/Engine.cs
  22. 32 2
      Jint/Native/Array/ArrayConstructor.cs
  23. 10 17
      Jint/Native/Array/ArrayInstance.cs
  24. 109 34
      Jint/Native/Array/ArrayPrototype.cs
  25. 7 0
      Jint/Native/Array/IArrayLike.cs
  26. 1 1
      Jint/Native/Boolean/BooleanConstructor.cs
  27. 2 2
      Jint/Native/Boolean/BooleanPrototype.cs
  28. 4 4
      Jint/Native/Date/DateConstructor.cs
  29. 45 45
      Jint/Native/Date/DatePrototype.cs
  30. 1 1
      Jint/Native/Error/ErrorConstructor.cs
  31. 1 1
      Jint/Native/Error/ErrorPrototype.cs
  32. 2 1
      Jint/Native/Function/BindFunctionInstance.cs
  33. 2 1
      Jint/Native/Function/EvalFunctionInstance.cs
  34. 1 1
      Jint/Native/Function/FunctionConstructor.cs
  35. 39 7
      Jint/Native/Function/FunctionInstance.cs
  36. 7 6
      Jint/Native/Function/FunctionPrototype.cs
  37. 2 1
      Jint/Native/Function/FunctionShim.cs
  38. 7 63
      Jint/Native/Function/ScriptFunctionInstance.cs
  39. 2 1
      Jint/Native/Function/ThrowTypeError.cs
  40. 28 22
      Jint/Native/Global/GlobalObject.cs
  41. 10 0
      Jint/Native/Iterator/IIterator.cs
  42. 118 0
      Jint/Native/Iterator/IteratorConstructor.cs
  43. 246 0
      Jint/Native/Iterator/IteratorInstance.cs
  44. 34 0
      Jint/Native/Iterator/IteratorPrototype.cs
  45. 13 1
      Jint/Native/JsString.cs
  46. 9 23
      Jint/Native/JsSymbol.cs
  47. 33 0
      Jint/Native/JsValue.cs
  48. 2 2
      Jint/Native/Json/JsonInstance.cs
  49. 152 0
      Jint/Native/Map/MapConstructor.cs
  50. 144 0
      Jint/Native/Map/MapInstance.cs
  51. 133 0
      Jint/Native/Map/MapPrototype.cs
  52. 30 29
      Jint/Native/Math/MathInstance.cs
  53. 1 1
      Jint/Native/Number/NumberConstructor.cs
  54. 8 8
      Jint/Native/Number/NumberPrototype.cs
  55. 16 15
      Jint/Native/Object/ObjectConstructor.cs
  56. 13 13
      Jint/Native/Object/ObjectInstance.cs
  57. 6 6
      Jint/Native/Object/ObjectPrototype.cs
  58. 1 1
      Jint/Native/RegExp/RegExpConstructor.cs
  59. 3 3
      Jint/Native/RegExp/RegExpPrototype.cs
  60. 145 0
      Jint/Native/Set/SetConstructor.cs
  61. 130 0
      Jint/Native/Set/SetInstance.cs
  62. 123 0
      Jint/Native/Set/SetPrototype.cs
  63. 2 2
      Jint/Native/String/StringConstructor.cs
  64. 31 31
      Jint/Native/String/StringPrototype.cs
  65. 7 6
      Jint/Native/Symbol/SymbolConstructor.cs
  66. 3 3
      Jint/Native/Symbol/SymbolPrototype.cs
  67. 13 13
      Jint/Runtime/Descriptors/PropertyDescriptor.cs
  68. 5 0
      Jint/Runtime/ExceptionHelper.cs
  69. 44 8
      Jint/Runtime/Interop/ClrFunctionInstance.cs
  70. 2 1
      Jint/Runtime/Interop/DelegateWrapper.cs
  71. 1 1
      Jint/Runtime/Interop/GetterFunctionInstance.cs
  72. 1 1
      Jint/Runtime/Interop/MethodInfoFunctionInstance.cs
  73. 1 1
      Jint/Runtime/Interop/SetterFunctionInstance.cs
  74. 1 1
      Jint/Runtime/Interop/TypeReference.cs
  75. 590 0
      Jint/Runtime/OrderedDictionary.cs
  76. 52 0
      Jint/Runtime/OrderedSet.cs

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

@@ -19,7 +19,7 @@
     <None Include="..\Jint.Tests.CommonScripts\Scripts\**" CopyToOutputDirectory="PreserveNewest" LinkBase="SunSpider" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.10.14" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.11.0" />
     <ProjectReference Include="..\Jint\Jint.csproj" />
     <PackageReference Include="Jurassic" Version="3.0.0-alpha2" />
     <PackageReference Include="NiL.JS.NetCore" Version="2.5.1200" />

+ 5 - 5
Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj

@@ -9,11 +9,11 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
-    <PackageReference Include="xunit" Version="2.3.1" />
-    <PackageReference Include="xunit.analyzers" Version="0.9.0" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
-    <PackageReference Include="xunit.runner.console" Version="2.3.1" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
+    <PackageReference Include="xunit" Version="2.4.0" />
+    <PackageReference Include="xunit.analyzers" Version="0.10.0" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
+    <PackageReference Include="xunit.runner.console" Version="2.4.0" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
   </ItemGroup>
 </Project>

+ 5 - 5
Jint.Tests.Ecma/Jint.Tests.Ecma.csproj

@@ -6,11 +6,11 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
-    <PackageReference Include="xunit" Version="2.3.1" />
-    <PackageReference Include="xunit.analyzers" Version="0.9.0" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
-    <PackageReference Include="xunit.runner.console" Version="2.3.1" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
+    <PackageReference Include="xunit" Version="2.4.0" />
+    <PackageReference Include="xunit.analyzers" Version="0.10.0" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
+    <PackageReference Include="xunit.runner.console" Version="2.4.0" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
   </ItemGroup>
 </Project>

+ 34 - 0
Jint.Tests.Ecma/SingleTest.cs

@@ -0,0 +1,34 @@
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using Xunit;
+
+namespace Jint.Tests.Ecma
+{
+    public class RunnableInDebugOnlyAttribute : FactAttribute
+    {
+        public RunnableInDebugOnlyAttribute()
+        {
+            if (!Debugger.IsAttached)
+            {
+                Skip = "Only running in interactive mode.";
+            }
+        }
+    }
+    public class SingleTest : EcmaTest
+    {
+        // helper to test single test case
+        [RunnableInDebugOnly]
+        public void TestSingle()
+        {
+            const string Target = @"ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-4-10.js";
+            var sourceFile = SourceFiles(Target, false)
+                .SelectMany(x => x)
+                .Cast<SourceFile>()
+                .Single();
+
+            var code = File.ReadAllText(Path.Combine(@"..\..\..\TestCases", sourceFile.Source));
+            RunTestCode(code, negative: false);
+        }
+    }
+}

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

@@ -17975,8 +17975,8 @@
     source: "ch15/15.1/15.1.2/15.1.2.2/S15.1.2.2_A9.1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "Function.length is configurable in ES6",
     source: "ch15/15.1/15.1.2/15.1.2.2/S15.1.2.2_A9.2.js"
   },
   {
@@ -18175,8 +18175,8 @@
     source: "ch15/15.1/15.1.2/15.1.2.3/S15.1.2.3_A7.1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "Function.length is configurable in ES6",
     source: "ch15/15.1/15.1.2/15.1.2.3/S15.1.2.3_A7.2.js"
   },
   {
@@ -18520,8 +18520,8 @@
     source: "ch15/15.1/15.1.3/15.1.3.1/S15.1.3.1_A5.1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "Function.length is configurable in ES6",
     source: "ch15/15.1/15.1.3/15.1.3.1/S15.1.3.1_A5.2.js"
   },
   {
@@ -18780,8 +18780,8 @@
     source: "ch15/15.1/15.1.3/15.1.3.2/S15.1.3.2_A5.1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "Function.length is configurable in ES6 ",
     source: "ch15/15.1/15.1.3/15.1.3.2/S15.1.3.2_A5.2.js"
   },
   {
@@ -18920,8 +18920,8 @@
     source: "ch15/15.1/15.1.3/15.1.3.3/S15.1.3.3_A5.1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "Function.length is configurable in ES6",
     source: "ch15/15.1/15.1.3/15.1.3.3/S15.1.3.3_A5.2.js"
   },
   {
@@ -19060,8 +19060,8 @@
     source: "ch15/15.1/15.1.3/15.1.3.4/S15.1.3.4_A5.1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "Function.length is configurable in ES6",
     source: "ch15/15.1/15.1.3/15.1.3.4/S15.1.3.4_A5.2.js"
   },
   {

+ 4 - 2
Jint.Tests.Ecma/TestCases/ch15/15.1/15.1.2/15.1.2.2/S15.1.2.2_A2_T10.js

@@ -9,8 +9,10 @@
 //CHECK#
 var count = 0;
 var errorCount = 0;
-var uspU = ["\u1680", "\u180E", "\u2000", "\u2001", "\u2002", "\u2003", "\u2004", "\u2005", "\u2006", "\u2007", "\u2008", "\u2009", "\u200A", "\u202F", "\u205F", "\u3000"];
-var uspS = ["1680", "180E", "2000", "2001", "2002", "2003", "2004", "2005", "2006", "2007", "2008", "2009", "200A", "202F", "205F", "3000"];
+
+// \u180E commented out per https://ecmascript-daily.github.io/2016/03/28/es2016-require-unicode-8-0-0-by-littledan-pull-request-300-tc39-ecma262
+var uspU = ["\u1680", /* "\u180E", */ "\u2000", "\u2001", "\u2002", "\u2003", "\u2004", "\u2005", "\u2006", "\u2007", "\u2008", "\u2009", "\u200A", "\u202F", "\u205F", "\u3000"];
+var uspS = ["1680", /* "180E", */ "2000", "2001", "2002", "2003", "2004", "2005", "2006", "2007", "2008", "2009", "200A", "202F", "205F", "3000"];
 
 for (var index = 0; index < uspU.length; index++) {
   var result = true;

+ 4 - 2
Jint.Tests.Ecma/TestCases/ch15/15.1/15.1.2/15.1.2.3/S15.1.2.3_A2_T10.js

@@ -9,8 +9,10 @@
 //CHECK#
 var count = 0;
 var errorCount = 0;
-var uspU = ["\u1680", "\u180E", "\u2000", "\u2001", "\u2002", "\u2003", "\u2004", "\u2005", "\u2006", "\u2007", "\u2008", "\u2009", "\u200A", "\u202F", "\u205F", "\u3000"];
-var uspS = ["1680", "180E", "2000", "2001", "2002", "2003", "2004", "2005", "2006", "2007", "2008", "2009", "200A", "202F", "205F", "3000"];
+
+// \u180E commented out per https://ecmascript-daily.github.io/2016/03/28/es2016-require-unicode-8-0-0-by-littledan-pull-request-300-tc39-ecma262
+var uspU = ["\u1680", /* "\u180E", */ "\u2000", "\u2001", "\u2002", "\u2003", "\u2004", "\u2005", "\u2006", "\u2007", "\u2008", "\u2009", "\u200A", "\u202F", "\u205F", "\u3000"];
+var uspS = ["1680", /* "180E", */ "2000", "2001", "2002", "2003", "2004", "2005", "2006", "2007", "2008", "2009", "200A", "202F", "205F", "3000"];
 for (var index = 0; index < uspU.length; index++) {
   var result = true;  
   if (parseFloat(uspU[index] + "1.1") !== parseFloat("1.1")) {

+ 15 - 0
Jint.Tests.Test262/ArrayTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class ArrayTests : Test262Test
+    {
+        [Theory(Skip = "A lot to do", DisplayName = "built-ins\\Array")]
+        [MemberData(nameof(SourceFiles), "built-ins\\Array", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\Array", true, Skip = "Skipped")]
+        protected void RunTest(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 15 - 0
Jint.Tests.Test262/BooleanTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class BooleanTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\Boolean")]
+        [MemberData(nameof(SourceFiles), "built-ins\\Boolean", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\Boolean", true, Skip = "Skipped")]
+        protected void RunTest(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 5 - 5
Jint.Tests.Test262/Jint.Tests.Test262.csproj

@@ -6,11 +6,11 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
-    <PackageReference Include="xunit" Version="2.3.1" />
-    <PackageReference Include="xunit.analyzers" Version="0.9.0" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
-    <PackageReference Include="xunit.runner.console" Version="2.3.1" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
+    <PackageReference Include="xunit" Version="2.4.0" />
+    <PackageReference Include="xunit.analyzers" Version="0.10.0" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
+    <PackageReference Include="xunit.runner.console" Version="2.4.0" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
   </ItemGroup>
 </Project>

+ 15 - 0
Jint.Tests.Test262/MapTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class MapTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\Map")]
+        [MemberData(nameof(SourceFiles), "built-ins\\Map", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\Map", true, Skip = "Skipped")]
+        protected void RunTest(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 15 - 0
Jint.Tests.Test262/ParseFloatTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class ParseFloatTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\parseFloat")]
+        [MemberData(nameof(SourceFiles), "built-ins\\parseFloat", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\parseFloat", true, Skip = "Skipped")]
+        protected void RunTest(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 15 - 0
Jint.Tests.Test262/ParseIntTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class ParseIntTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\parseInt")]
+        [MemberData(nameof(SourceFiles), "built-ins\\parseInt", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\parseInt", true, Skip = "Skipped")]
+        protected void RunTest(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 15 - 0
Jint.Tests.Test262/SetTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class SetTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\Set")]
+        [MemberData(nameof(SourceFiles), "built-ins\\Set", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\Set", true, Skip = "Skipped")]
+        protected void RunTest(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 34 - 0
Jint.Tests.Test262/SingleTest.cs

@@ -0,0 +1,34 @@
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class RunnableInDebugOnlyAttribute : FactAttribute
+    {
+        public RunnableInDebugOnlyAttribute()
+        {
+            if (!Debugger.IsAttached)
+            {
+                Skip = "Only running in interactive mode.";
+            }
+        }
+    }
+    public class SingleTest : Test262Test
+    {
+        // helper to test single test case
+        [RunnableInDebugOnly]
+        public void TestSingle()
+        {
+            const string Target = @"built-ins/Map/iterator-close-after-set-failure.js";
+            var sourceFile = SourceFiles("built-ins", false)
+                .SelectMany(x => x)
+                .Cast<SourceFile>()
+                .First(x => x.Source == Target);
+
+            var code = File.ReadAllText(sourceFile.FullPath);
+            RunTestCode(code, strict: true);
+        }
+    }
+}

+ 39 - 0
Jint.Tests.Test262/UriTests.cs

@@ -0,0 +1,39 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class UriTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\decodeURI")]
+        [MemberData(nameof(SourceFiles), "built-ins\\decodeURI", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\decodeURI", true, Skip = "Skipped")]
+        protected void DecodeUri(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+
+        [Theory(DisplayName = "built-ins\\decodeURIComponent")]
+        [MemberData(nameof(SourceFiles), "built-ins\\decodeURIComponent", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\decodeURIComponent", true, Skip = "Skipped")]
+        protected void DecodeUriComponent(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+
+        [Theory(DisplayName = "built-ins\\encodeURI")]
+        [MemberData(nameof(SourceFiles), "built-ins\\encodeURI", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\encodeURI", true, Skip = "Skipped")]
+        protected void EncodeUri(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+
+        [Theory(DisplayName = "built-ins\\encodeURIComponent")]
+        [MemberData(nameof(SourceFiles), "built-ins\\encodeURIComponent", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\encodeURIComponent", true, Skip = "Skipped")]
+        protected void EncodeUriComponent(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 96 - 8
Jint.Tests.Test262/test/skipped.json

@@ -1,18 +1,106 @@
 [
   {
-    "source": "built-ins/Math/sign/name.js",
-    "reason": "name not yet implemented"
+    "source": "built-ins/Map/iterator-is-undefined-throws.js",
+    "reason": "Esprima problem"
   },
   {
-    "source": "built-ins/Math/sign/length.js",
-    "reason": "length not yet implemented"
+    "source": "built-ins/Boolean/proto-from-ctor-realm.js",
+    "reason": "realms not implemented"
   },
   {
-    "source": "built-ins/Math/trunc/name.js",
-    "reason": "name not yet implemented"
+    "source": "built-ins/Map/proto-from-ctor-realm.js",
+    "reason": "realms not implemented"
   },
   {
-    "source": "built-ins/Math/trunc/length.js",
-    "reason": "length not yet implemented"
+    "source": "built-ins/Map/prototype/clear/context-is-weakmap-object-throws.js",
+    "reason": "WeakMap not implemented"
+  },
+  {
+    "source": "built-ins/Map/prototype/delete/context-is-weakmap-object-throws.js",
+    "reason": "WeakMap not implemented"
+  },
+  {
+    "source": "built-ins/Map/prototype/entries/does-not-have-mapdata-internal-slot-weakmap.js",
+    "reason": "WeakMap not implemented"
+  },
+  {
+    "source": "built-ins/Map/prototype/forEach/does-not-have-mapdata-internal-slot-weakmap.js",
+    "reason": "WeakMap not implemented"
+  },
+  {
+    "source": "built-ins/Map/prototype/forEach/callback-this-strict.js",
+    "reason": "global strict mode not working as expected"
+  },
+  {
+    "source": "built-ins/Map/prototype/forEach/iterates-values-deleted-then-readded.js",
+    "reason": "delete/add detection not implemented for map iterator during iteration"
+  },
+  {
+    "source": "built-ins/Map/prototype/get/does-not-have-mapdata-internal-slot-weakmap.js",
+    "reason": "WeakMap not implemented"
+  },
+  {
+    "source": "built-ins/Map/prototype/has/does-not-have-mapdata-internal-slot-weakmap.js",
+    "reason": "WeakMap not implemented"
+  },
+  {
+    "source": "built-ins/Map/prototype/keys/does-not-have-mapdata-internal-slot-weakmap.js",
+    "reason": "WeakMap not implemented"
+  },
+  {
+    "source": "built-ins/Map/prototype/set/does-not-have-mapdata-internal-slot-weakmap.js",
+    "reason": "WeakMap not implemented"
+  },
+  {
+    "source": "built-ins/Map/prototype/size/does-not-have-mapdata-internal-slot-weakmap.js",
+    "reason": "WeakMap not implemented"
+  },
+  {
+    "source": "built-ins/Map/prototype/values/does-not-have-mapdata-internal-slot-weakmap.js",
+    "reason": "WeakMap not implemented"
+  },
+  {
+    "source": "built-ins/Set/proto-from-ctor-realm.js",
+    "reason": "realms not implemented"
+  },
+  {
+    "source": "built-ins/Set/prototype/add/does-not-have-setdata-internal-slot-weakset.js",
+    "reason": "WeakSet not implemented"
+  },
+  {
+    "source": "built-ins/Set/prototype/clear/does-not-have-setdata-internal-slot-weakset.js",
+    "reason": "WeakSet not implemented"
+  },
+  {
+    "source": "built-ins/Set/prototype/delete/does-not-have-setdata-internal-slot-weakset.js",
+    "reason": "WeakSet not implemented"
+  },
+  {
+    "source": "built-ins/Set/prototype/entries/does-not-have-setdata-internal-slot-weakset.js",
+    "reason": "WeakSet not implemented"
+  },
+  {
+    "source": "built-ins/Set/prototype/foreach/does-not-have-setdata-internal-slot-weakset.js",
+    "reason": "WeakSet not implemented"
+  },
+  {
+    "source": "built-ins/Set/prototype/forEach/iterates-values-revisits-after-delete-re-add.js",
+    "reason": "delete/add detection not implemented for set iterator during iteration"
+  },
+  {
+    "source": "built-ins/Set/prototype/forEach/this-strict.js",
+    "reason": "global strict mode not working as expected"
+  },
+  {
+    "source": "built-ins/Set/prototype/has/does-not-have-setdata-internal-slot-weakset.js",
+    "reason": "WeakSet not implemented"
+  },
+  {
+    "source": "built-ins/Set/prototype/values/does-not-have-setdata-internal-slot-weakset.js",
+    "reason": "WeakSet not implemented"
+  },
+  {
+    "source": "built-ins/Set/prototype/forEach/this-arg-explicit-cannot-override-lexical-this-arrow.js",
+    "reason": "arrow functions not implemented"
   }
 ]

+ 5 - 5
Jint.Tests/Jint.Tests.csproj

@@ -9,11 +9,11 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
-    <PackageReference Include="xunit" Version="2.3.1" />
-    <PackageReference Include="xunit.analyzers" Version="0.9.0" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
-    <PackageReference Include="xunit.runner.console" Version="2.3.1" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
+    <PackageReference Include="xunit" Version="2.4.0" />
+    <PackageReference Include="xunit.analyzers" Version="0.10.0" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
+    <PackageReference Include="xunit.runner.console" Version="2.4.0" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
   </ItemGroup>
 </Project>

+ 1 - 1
Jint.Tests/Runtime/NullPropagation.cs

@@ -24,7 +24,7 @@ namespace Jint.Tests.Runtime
 
             public bool TryGetCallable(Engine engine, object reference, out JsValue value)
             {
-                value = new ClrFunctionInstance(engine, (thisObj, values) => thisObj);
+                value = new ClrFunctionInstance(engine, "anonymous", (thisObj, values) => thisObj);
                 return true;
             }
 

+ 9 - 1
Jint.sln.DotSettings

@@ -1,4 +1,12 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=2CC1F6A6_002D7DCC_002D4C7C_002DA619_002DACE1A4296446/@EntryIndexedValue"></s:String>
+	<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=2CC1F6A6_002D7DCC_002D4C7C_002DA619_002DACE1A4296446/@EntryIndexRemoved">True</s:Boolean>
 	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=2CC1F6A6_002D7DCC_002D4C7C_002DA619_002DACE1A4296446_002Fd_003ATestCases/@EntryIndexedValue">ExplicitlyExcluded</s:String>
-	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=457CBFB6_002DC8D8_002D4433_002D9E8F_002D445C71C103F1_002Fd_003Atest/@EntryIndexedValue">ExplicitlyExcluded</s:String>
+	
+	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=44CF8026_002DA797_002D420C_002DA8BC_002D409BB67D32F6_002Fd_003Atest/@EntryIndexedValue">ExplicitlyExcluded</s:String>
+	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=95ED73F7_002D3519_002D4733_002DB361_002D7790053A1A71/@EntryIndexedValue"></s:String>
+	<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=95ED73F7_002D3519_002D4733_002DB361_002D7790053A1A71/@EntryIndexRemoved">True</s:Boolean>
+	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=95ED73F7_002D3519_002D4733_002DB361_002D7790053A1A71_002Fd_003Aharness/@EntryIndexedValue">ExplicitlyExcluded</s:String>
+	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=95ED73F7_002D3519_002D4733_002DB361_002D7790053A1A71_002Fd_003Atest/@EntryIndexedValue">ExplicitlyExcluded</s:String>
+	
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ISO/@EntryIndexedValue">ISO</s:String></wpf:ResourceDictionary>

+ 31 - 13
Jint/Engine.cs

@@ -10,11 +10,14 @@ using Jint.Native.Date;
 using Jint.Native.Error;
 using Jint.Native.Function;
 using Jint.Native.Global;
+using Jint.Native.Iterator;
 using Jint.Native.Json;
+using Jint.Native.Map;
 using Jint.Native.Math;
 using Jint.Native.Number;
 using Jint.Native.Object;
 using Jint.Native.RegExp;
+using Jint.Native.Set;
 using Jint.Native.String;
 using Jint.Native.Symbol;
 using Jint.Pooling;
@@ -56,7 +59,7 @@ namespace Jint
         internal readonly ReferencePool _referencePool;
         internal readonly ArgumentsInstancePool _argumentsInstancePool;
         internal readonly JsValueArrayPool _jsValueArrayPool;
-        
+
         public ITypeConverter ClrTypeConverter;
 
         // cache of types used when resolving CLR type names
@@ -113,6 +116,9 @@ namespace Jint
 
             Symbol = SymbolConstructor.CreateSymbolConstructor(this);
             Array = ArrayConstructor.CreateArrayConstructor(this);
+            Map = MapConstructor.CreateMapConstructor(this);
+            Set = SetConstructor.CreateSetConstructor(this);
+            Iterator= IteratorConstructor.CreateIteratorConstructor(this);
             String = StringConstructor.CreateStringConstructor(this);
             RegExp = RegExpConstructor.CreateRegExpConstructor(this);
             Number = NumberConstructor.CreateNumberConstructor(this);
@@ -148,6 +154,15 @@ namespace Jint
             Array.Configure();
             Array.PrototypeObject.Configure();
 
+            Map.Configure();
+            Map.PrototypeObject.Configure();
+
+            Set.Configure();
+            Set.PrototypeObject.Configure();
+
+            Iterator.Configure();
+            Iterator.PrototypeObject.Configure();
+
             String.Configure();
             String.PrototypeObject.Configure();
 
@@ -178,16 +193,16 @@ namespace Jint
             Options = new Options();
 
             options?.Invoke(Options);
-            
+
             // gather some options as fields for faster checks
             _isDebugMode = Options.IsDebugMode;
             _isStrict = Options.IsStrict;
             _maxStatements = Options._MaxStatements;
             _referenceResolver = Options.ReferenceResolver;
             _memoryLimit = Options._MemoryLimit;
-            _runBeforeStatementChecks = (_maxStatements > 0 &&_maxStatements < int.MaxValue) 
+            _runBeforeStatementChecks = (_maxStatements > 0 &&_maxStatements < int.MaxValue)
                                         || Options._TimeoutInterval.Ticks > 0
-                                        || _memoryLimit > 0 
+                                        || _memoryLimit > 0
                                         || _isDebugMode;
 
             _referencePool = new ReferencePool();
@@ -203,10 +218,10 @@ namespace Jint
             if (Options._IsClrAllowed)
             {
                 Global.FastAddProperty("System", new NamespaceReference(this, "System"), false, false, false);
-                Global.FastAddProperty("importNamespace", new ClrFunctionInstance(this, (thisObj, arguments) =>
-                {
-                    return new NamespaceReference(this, TypeConverter.ToString(arguments.At(0)));
-                }), false, false, false);
+                Global.FastAddProperty("importNamespace", new ClrFunctionInstance(
+                    this,
+                    "importNamespace",
+                    (thisObj, arguments) => new NamespaceReference(this, TypeConverter.ToString(arguments.At(0)))), false, false, false);
             }
 
             ClrTypeConverter = new DefaultTypeConverter(this);
@@ -219,6 +234,9 @@ namespace Jint
         public ObjectConstructor Object { get; }
         public FunctionConstructor Function { get; }
         public ArrayConstructor Array { get; }
+        public MapConstructor Map { get; }
+        public SetConstructor Set { get; }
+        public IteratorConstructor Iterator { get; }
         public StringConstructor String { get; }
         public RegExpConstructor RegExp { get; }
         public BooleanConstructor Boolean { get; }
@@ -367,12 +385,12 @@ namespace Jint
         public Engine Execute(Program program)
         {
             ResetStatementsCount();
-            
+
             if (_memoryLimit > 0)
             {
                 ResetMemoryUsage();
             }
-            
+
             ResetTimeoutTicks();
             ResetLastStatement();
             ResetCallStack();
@@ -445,7 +463,7 @@ namespace Jint
 
                 case Nodes.ExpressionStatement:
                     return new Completion(
-                        CompletionType.Normal, 
+                        CompletionType.Normal,
                         GetValue(EvaluateExpression(((ExpressionStatement) statement).Expression), true),
                         null);
 
@@ -592,7 +610,7 @@ namespace Jint
         {
             return GetValue(value, false);
         }
-        
+
         internal JsValue GetValue(object value, bool returnReferenceToPool)
         {
             if (value is JsValue jsValue)
@@ -976,7 +994,7 @@ namespace Jint
         {
             _executionContexts.ReplaceTopLexicalEnvironment(newEnv);
         }
-        
+
         private static void AssertNotNullOrEmpty(string propertyname, string propertyValue)
         {
             if (string.IsNullOrEmpty(propertyValue))

+ 32 - 2
Jint/Native/Array/ArrayConstructor.cs

@@ -2,15 +2,17 @@
 using System.Runtime.CompilerServices;
 using Jint.Native.Function;
 using Jint.Native.Object;
+using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Array
 {
     public sealed class ArrayConstructor : FunctionInstance, IConstructor
     {
-        private ArrayConstructor(Engine engine) :  base(engine, null, null, false)
+        private ArrayConstructor(Engine engine) :  base(engine, "Array", null, null, false)
         {
         }
 
@@ -30,12 +32,40 @@ namespace Jint.Native.Array
             // The initial value of Array.prototype is the Array prototype object
             obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
 
+            obj.SetOwnProperty(GlobalSymbolRegistry.Species._value,
+                new GetSetPropertyDescriptor(
+                    get: new ClrFunctionInstance(engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable),
+                    set: Undefined,
+                    PropertyFlag.Configurable));
+
             return obj;
         }
 
         public void Configure()
         {
-            SetOwnProperty("isArray", new PropertyDescriptor(new ClrFunctionInstance(Engine, IsArray, 1), PropertyFlag.NonEnumerable));
+            SetOwnProperty("from",new PropertyDescriptor(new ClrFunctionInstance(Engine, "from", From, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable));
+            SetOwnProperty("isArray", new PropertyDescriptor(new ClrFunctionInstance(Engine, "isArray", IsArray, 1), PropertyFlag.NonEnumerable));
+            SetOwnProperty("of", new PropertyDescriptor(new ClrFunctionInstance(Engine, "of", Of, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable));
+        }
+
+        private JsValue From(JsValue thisObj, JsValue[] arguments)
+        {
+            var source = arguments.At(0);
+            if (source is IArrayLike arrayLike)
+            {
+                arrayLike.ToArray(_engine);
+            }
+            return Undefined;
+        }
+
+        private JsValue Of(JsValue thisObj, JsValue[] arguments)
+        {
+            return Undefined;
+        }
+
+        private static JsValue Species(JsValue thisObject, JsValue[] arguments)
+        {
+            return thisObject;
         }
 
         private static JsValue IsArray(JsValue thisObj, JsValue[] arguments)

+ 10 - 17
Jint/Native/Array/ArrayInstance.cs

@@ -1,7 +1,5 @@
-using System.Collections;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.Runtime.CompilerServices;
-
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
@@ -11,7 +9,7 @@ using TypeConverter = Jint.Runtime.TypeConverter;
 
 namespace Jint.Native.Array
 {
-    public class ArrayInstance : ObjectInstance, IEnumerable<JsValue>
+    public class ArrayInstance : ObjectInstance
     {
         private const string PropertyNameLength = "length";
         private const int PropertyNameLengthLength = 6;
@@ -48,8 +46,8 @@ namespace Jint.Native.Array
             {
                 _dense = items;
                 length = items.Length;
-            }            
-            
+            }
+
             _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
         }
 
@@ -454,7 +452,7 @@ namespace Jint.Native.Array
             {
                 return StringAsIndex(d, p);
             }
-            
+
             return (uint) d;
         }
 
@@ -527,7 +525,7 @@ namespace Jint.Native.Array
             value = Undefined;
 
             TryGetDescriptor(index, out var desc);
-            desc = desc ?? GetProperty(TypeConverter.ToString(index)) ?? PropertyDescriptor.Undefined; 
+            desc = desc ?? GetProperty(TypeConverter.ToString(index)) ?? PropertyDescriptor.Undefined;
             return desc.TryGetValue(this, out value);
         }
 
@@ -638,16 +636,11 @@ namespace Jint.Native.Array
             };
         }
 
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return GetEnumerator();
-        }
-
         internal uint Push(JsValue[] arguments)
         {
             var initialLength = GetLength();
             var newLength = initialLength + arguments.Length;
-            
+
             // if we see that we are bringing more than normal growth algorithm handles, ensure capacity eagerly
             if (_dense != null
                 && initialLength != 0
@@ -722,11 +715,11 @@ namespace Jint.Native.Array
             _engine._jsValueArrayPool.ReturnArray(args);
             return a;
         }
-        
+
         /// <inheritdoc />
         internal override bool FindWithCallback(
-            JsValue[] arguments, 
-            out uint index, 
+            JsValue[] arguments,
+            out uint index,
             out JsValue value)
         {
             var len = GetLength();

+ 109 - 34
Jint/Native/Array/ArrayPrototype.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using Jint.Native.Object;
+using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
@@ -32,29 +33,85 @@ namespace Jint.Native.Array
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance(Engine, ToString, 0), true, false, true);
-            FastAddProperty("toLocaleString", new ClrFunctionInstance(Engine, ToLocaleString), true, false, true);
-            FastAddProperty("concat", new ClrFunctionInstance(Engine, Concat, 1), true, false, true);
-            FastAddProperty("join", new ClrFunctionInstance(Engine, Join, 1), true, false, true);
-            FastAddProperty("pop", new ClrFunctionInstance(Engine, Pop), true, false, true);
-            FastAddProperty("push", new ClrFunctionInstance(Engine, Push, 1), true, false, true);
-            FastAddProperty("reverse", new ClrFunctionInstance(Engine, Reverse), true, false, true);
-            FastAddProperty("shift", new ClrFunctionInstance(Engine, Shift), true, false, true);
-            FastAddProperty("slice", new ClrFunctionInstance(Engine, Slice, 2), true, false, true);
-            FastAddProperty("sort", new ClrFunctionInstance(Engine, Sort, 1), true, false, true);
-            FastAddProperty("splice", new ClrFunctionInstance(Engine, Splice, 2), true, false, true);
-            FastAddProperty("unshift", new ClrFunctionInstance(Engine, Unshift, 1), true, false, true);
-            FastAddProperty("indexOf", new ClrFunctionInstance(Engine, IndexOf, 1), true, false, true);
-            FastAddProperty("lastIndexOf", new ClrFunctionInstance(Engine, LastIndexOf, 1), true, false, true);
-            FastAddProperty("every", new ClrFunctionInstance(Engine, Every, 1), true, false, true);
-            FastAddProperty("some", new ClrFunctionInstance(Engine, Some, 1), true, false, true);
-            FastAddProperty("forEach", new ClrFunctionInstance(Engine, ForEach, 1), true, false, true);
-            FastAddProperty("map", new ClrFunctionInstance(Engine, Map, 1), true, false, true);
-            FastAddProperty("filter", new ClrFunctionInstance(Engine, Filter, 1), true, false, true);
-            FastAddProperty("reduce", new ClrFunctionInstance(Engine, Reduce, 1), true, false, true);
-            FastAddProperty("reduceRight", new ClrFunctionInstance(Engine, ReduceRight, 1), true, false, true);
-            FastAddProperty("find", new ClrFunctionInstance(Engine, Find, 1), true, false, true);
-            FastAddProperty("findIndex", new ClrFunctionInstance(Engine, FindIndex, 1), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToString, 0), true, false, true);
+            FastAddProperty("toLocaleString", new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString), true, false, true);
+            FastAddProperty("concat", new ClrFunctionInstance(Engine, "concat", Concat, 1), true, false, true);
+            FastAddProperty("copyWithin", new ClrFunctionInstance(Engine, "copyWithin", CopyWithin, 1), true, false, true);
+            FastAddProperty("entries", new ClrFunctionInstance(Engine, "entries", Iterator, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("fill", new ClrFunctionInstance(Engine, "fill", Fill, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("join", new ClrFunctionInstance(Engine, "join", Join, 1), true, false, true);
+            FastAddProperty("pop", new ClrFunctionInstance(Engine, "pop", Pop), true, false, true);
+            FastAddProperty("push", new ClrFunctionInstance(Engine, "push", Push, 1), true, false, true);
+            FastAddProperty("reverse", new ClrFunctionInstance(Engine, "reverse", Reverse), true, false, true);
+            FastAddProperty("shift", new ClrFunctionInstance(Engine, "shift", Shift), true, false, true);
+            FastAddProperty("slice", new ClrFunctionInstance(Engine, "slice", Slice, 2), true, false, true);
+            FastAddProperty("sort", new ClrFunctionInstance(Engine, "sort", Sort, 1), true, false, true);
+            FastAddProperty("splice", new ClrFunctionInstance(Engine, "splice", Splice, 2), true, false, true);
+            FastAddProperty("unshift", new ClrFunctionInstance(Engine, "unshift", Unshift, 1), true, false, true);
+            FastAddProperty("indexOf", new ClrFunctionInstance(Engine, "indexOf", IndexOf, 1), true, false, true);
+            FastAddProperty("lastIndexOf", new ClrFunctionInstance(Engine, "lastIndexOf", LastIndexOf, 1), true, false, true);
+            FastAddProperty("every", new ClrFunctionInstance(Engine, "every", Every, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("some", new ClrFunctionInstance(Engine, "some", Some, 1), true, false, true);
+            FastAddProperty("forEach", new ClrFunctionInstance(Engine, "forEach", ForEach, 1), true, false, true);
+            FastAddProperty("map", new ClrFunctionInstance(Engine, "map", Map, 1), true, false, true);
+            FastAddProperty("filter", new ClrFunctionInstance(Engine, "filter", Filter, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("reduce", new ClrFunctionInstance(Engine, "reduce", Reduce, 1), true, false, true);
+            FastAddProperty("reduceRight", new ClrFunctionInstance(Engine, "reduceRight", ReduceRight, 1), true, false, true);
+            FastAddProperty("find", new ClrFunctionInstance(Engine, "find", Find, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("findIndex", new ClrFunctionInstance(Engine, "findIndex", FindIndex, 1), true, false, true);
+
+            FastAddProperty(GlobalSymbolRegistry.Iterator._value, new ClrFunctionInstance(Engine, "iterator", Iterator, 1), true, false, true);
+        }
+
+        private ObjectInstance Iterator(JsValue thisObj, JsValue[] arguments)
+        {
+            var array = thisObj as ArrayInstance;
+            return _engine.Iterator.Construct(array);
+        }
+
+        private JsValue Fill(JsValue thisObj, JsValue[] arguments)
+        {
+            var target = thisObj as ObjectInstance;
+            var operations = ArrayOperations.For(target);
+            var length = operations.GetLength();
+
+            var value = arguments.At(0);
+
+            var start = arguments.At(1, 0).AsNumber();
+            var relativeStart = TypeConverter.ToInteger(start);
+            uint actualStart;
+            if (relativeStart < 0)
+            {
+                actualStart = (uint) System.Math.Max(length + relativeStart, 0);
+            }
+            else
+            {
+                actualStart = (uint) System.Math.Min(relativeStart, length);
+            }
+
+            var end = arguments.At(2, length).AsNumber();
+            var relativeEnd = TypeConverter.ToInteger(end);
+            uint actualEnd;
+            if (relativeEnd < 0)
+            {
+                actualEnd = (uint) System.Math.Max(length + relativeEnd, 0);
+            }
+            else
+            {
+                actualEnd = (uint) System.Math.Min(relativeEnd, length);
+            }
+
+            for (var i = actualStart; i < actualEnd; ++i)
+            {
+                operations.Put(i, value, false);
+            }
+
+            return target;
+        }
+
+        private JsValue CopyWithin(JsValue thisObj, JsValue[] arguments)
+        {
+            return Undefined;
         }
 
         private JsValue LastIndexOf(JsValue thisObj, JsValue[] arguments)
@@ -264,12 +321,25 @@ namespace Jint.Native.Array
 
         private JsValue Every(JsValue thisObj, JsValue[] arguments)
         {
-            var callbackfn = arguments.At(0);
-            var thisArg = arguments.At(1);
-
             var o = ArrayOperations.For(Engine, thisObj);
-            var len = o.GetLength();
+            uint len;
+            if (thisObj is ArrayInstance arrayInstance)
+            {
+                len = arrayInstance.GetLength();
+            }
+            else
+            {
+                var intValue = ((ArrayOperations.ObjectInstanceOperations) o).GetIntegerLength();
+                len = intValue < 0 ? 0 : (uint) intValue;
+            }
 
+            if (len == 0)
+            {
+                return JsBoolean.True;
+            }
+
+            var callbackfn = arguments.At(0);
+            var thisArg = arguments.At(1);
             var callable = GetCallable(callbackfn);
 
             var args = _engine._jsValueArrayPool.RentArray(3);
@@ -351,14 +421,14 @@ namespace Jint.Native.Array
 
             return -1;
         }
-        
+
         private JsValue Find(JsValue thisObj, JsValue[] arguments)
         {
             var target = TypeConverter.ToObject(Engine, thisObj);
             target.FindWithCallback(arguments, out _, out var value);
             return value;
         }
-        
+
         private JsValue FindIndex(JsValue thisObj, JsValue[] arguments)
         {
             var target = TypeConverter.ToObject(Engine, thisObj);
@@ -937,12 +1007,12 @@ namespace Jint.Native.Array
             o.Target.Put("length", len, true);
             return element;
         }
-        
+
         /// <summary>
         /// Adapter to use optimized array operations when possible.
         /// Gaps the difference between ArgumensInstance and ArrayInstance.
         /// </summary>
-        private abstract class ArrayOperations
+        internal abstract class ArrayOperations
         {
             public abstract ObjectInstance Target { get; }
 
@@ -980,7 +1050,7 @@ namespace Jint.Native.Array
                 return new ObjectInstanceOperations(instance);
             }
 
-            private class ObjectInstanceOperations : ArrayOperations
+            internal class ObjectInstanceOperations : ArrayOperations
             {
                 private readonly ObjectInstance _instance;
 
@@ -991,7 +1061,7 @@ namespace Jint.Native.Array
 
                 public override ObjectInstance Target => _instance;
 
-                public override uint GetLength()
+                internal double GetIntegerLength()
                 {
                     var desc = _instance.GetProperty("length");
                     var descValue = desc.Value;
@@ -1009,7 +1079,12 @@ namespace Jint.Native.Array
                     // if getter is not undefined it must be ICallable
                     var callable = (ICallable) getter;
                     var value = callable.Call(_instance, Arguments.Empty);
-                    return TypeConverter.ToUint32(value);
+                    return (uint) TypeConverter.ToInteger(value);
+                }
+
+                public override uint GetLength()
+                {
+                    return (uint) GetIntegerLength();
                 }
 
                 public override void SetLength(uint length) => _instance.Put("length", length, true);

+ 7 - 0
Jint/Native/Array/IArrayLike.cs

@@ -0,0 +1,7 @@
+namespace Jint.Native.Array
+{
+    internal interface IArrayLike
+    {
+        ArrayInstance ToArray(Engine engine);
+    }
+}

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

@@ -7,7 +7,7 @@ namespace Jint.Native.Boolean
 {
     public sealed class BooleanConstructor : FunctionInstance, IConstructor
     {
-        private BooleanConstructor(Engine engine): base(engine, null, null, false)
+        private BooleanConstructor(Engine engine): base(engine, "Boolean", null, null, false)
         {
         }
 

+ 2 - 2
Jint/Native/Boolean/BooleanPrototype.cs

@@ -27,8 +27,8 @@ namespace Jint.Native.Boolean
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance(Engine, ToBooleanString), true, false, true);
-            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, ValueOf), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToBooleanString, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0, PropertyFlag.Configurable), true, false, true);
         }
 
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)

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

@@ -51,7 +51,7 @@ namespace Jint.Native.Date
             "THHK"
         };
 
-        public DateConstructor(Engine engine) : base(engine, null, null, false)
+        public DateConstructor(Engine engine) : base(engine, "Date", null, null, false)
         {
         }
 
@@ -74,9 +74,9 @@ namespace Jint.Native.Date
 
         public void Configure()
         {
-            FastAddProperty("parse", new ClrFunctionInstance(Engine, Parse, 1), true, false, true);
-            FastAddProperty("UTC", new ClrFunctionInstance(Engine, Utc, 7), true, false, true);
-            FastAddProperty("now", new ClrFunctionInstance(Engine, Now, 0), true, false, true);
+            FastAddProperty("parse", new ClrFunctionInstance(Engine, "parse", Parse, 1), true, false, true);
+            FastAddProperty("UTC", new ClrFunctionInstance(Engine, "utc", Utc, 7), true, false, true);
+            FastAddProperty("now", new ClrFunctionInstance(Engine, "now", Now, 0), true, false, true);
         }
 
         private JsValue Parse(JsValue thisObj, JsValue[] arguments)

+ 45 - 45
Jint/Native/Date/DatePrototype.cs

@@ -32,51 +32,51 @@ namespace Jint.Native.Date
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance(Engine, ToString, 0), true, false, true);
-            FastAddProperty("toDateString", new ClrFunctionInstance(Engine, ToDateString, 0), true, false, true);
-            FastAddProperty("toTimeString", new ClrFunctionInstance(Engine, ToTimeString, 0), true, false, true);
-            FastAddProperty("toLocaleString", new ClrFunctionInstance(Engine, ToLocaleString, 0), true, false, true);
-            FastAddProperty("toLocaleDateString", new ClrFunctionInstance(Engine, ToLocaleDateString, 0), true, false, true);
-            FastAddProperty("toLocaleTimeString", new ClrFunctionInstance(Engine, ToLocaleTimeString, 0), true, false, true);
-            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, ValueOf, 0), true, false, true);
-            FastAddProperty("getTime", new ClrFunctionInstance(Engine, GetTime, 0), true, false, true);
-            FastAddProperty("getFullYear", new ClrFunctionInstance(Engine, GetFullYear, 0), true, false, true);
-            FastAddProperty("getYear", new ClrFunctionInstance(Engine, GetYear, 0), true, false, true);
-            FastAddProperty("getUTCFullYear", new ClrFunctionInstance(Engine, GetUTCFullYear, 0), true, false, true);
-            FastAddProperty("getMonth", new ClrFunctionInstance(Engine, GetMonth, 0), true, false, true);
-            FastAddProperty("getUTCMonth", new ClrFunctionInstance(Engine, GetUTCMonth, 0), true, false, true);
-            FastAddProperty("getDate", new ClrFunctionInstance(Engine, GetDate, 0), true, false, true);
-            FastAddProperty("getUTCDate", new ClrFunctionInstance(Engine, GetUTCDate, 0), true, false, true);
-            FastAddProperty("getDay", new ClrFunctionInstance(Engine, GetDay, 0), true, false, true);
-            FastAddProperty("getUTCDay", new ClrFunctionInstance(Engine, GetUTCDay, 0), true, false, true);
-            FastAddProperty("getHours", new ClrFunctionInstance(Engine, GetHours, 0), true, false, true);
-            FastAddProperty("getUTCHours", new ClrFunctionInstance(Engine, GetUTCHours, 0), true, false, true);
-            FastAddProperty("getMinutes", new ClrFunctionInstance(Engine, GetMinutes, 0), true, false, true);
-            FastAddProperty("getUTCMinutes", new ClrFunctionInstance(Engine, GetUTCMinutes, 0), true, false, true);
-            FastAddProperty("getSeconds", new ClrFunctionInstance(Engine, GetSeconds, 0), true, false, true);
-            FastAddProperty("getUTCSeconds", new ClrFunctionInstance(Engine, GetUTCSeconds, 0), true, false, true);
-            FastAddProperty("getMilliseconds", new ClrFunctionInstance(Engine, GetMilliseconds, 0), true, false, true);
-            FastAddProperty("getUTCMilliseconds", new ClrFunctionInstance(Engine, GetUTCMilliseconds, 0), true, false, true);
-            FastAddProperty("getTimezoneOffset", new ClrFunctionInstance(Engine, GetTimezoneOffset, 0), true, false, true);
-            FastAddProperty("setTime", new ClrFunctionInstance(Engine, SetTime, 1), true, false, true);
-            FastAddProperty("setMilliseconds", new ClrFunctionInstance(Engine, SetMilliseconds, 1), true, false, true);
-            FastAddProperty("setUTCMilliseconds", new ClrFunctionInstance(Engine, SetUTCMilliseconds, 1), true, false, true);
-            FastAddProperty("setSeconds", new ClrFunctionInstance(Engine, SetSeconds, 2), true, false, true);
-            FastAddProperty("setUTCSeconds", new ClrFunctionInstance(Engine, SetUTCSeconds, 2), true, false, true);
-            FastAddProperty("setMinutes", new ClrFunctionInstance(Engine, SetMinutes, 3), true, false, true);
-            FastAddProperty("setUTCMinutes", new ClrFunctionInstance(Engine, SetUTCMinutes, 3), true, false, true);
-            FastAddProperty("setHours", new ClrFunctionInstance(Engine, SetHours, 4), true, false, true);
-            FastAddProperty("setUTCHours", new ClrFunctionInstance(Engine, SetUTCHours, 4), true, false, true);
-            FastAddProperty("setDate", new ClrFunctionInstance(Engine, SetDate, 1), true, false, true);
-            FastAddProperty("setUTCDate", new ClrFunctionInstance(Engine, SetUTCDate, 1), true, false, true);
-            FastAddProperty("setMonth", new ClrFunctionInstance(Engine, SetMonth, 2), true, false, true);
-            FastAddProperty("setUTCMonth", new ClrFunctionInstance(Engine, SetUTCMonth, 2), true, false, true);
-            FastAddProperty("setFullYear", new ClrFunctionInstance(Engine, SetFullYear, 3), true, false, true);
-            FastAddProperty("setYear", new ClrFunctionInstance(Engine, SetYear, 1), true, false, true);
-            FastAddProperty("setUTCFullYear", new ClrFunctionInstance(Engine, SetUTCFullYear, 3), true, false, true);
-            FastAddProperty("toUTCString", new ClrFunctionInstance(Engine, ToUtcString, 0), true, false, true);
-            FastAddProperty("toISOString", new ClrFunctionInstance(Engine, ToISOString, 0), true, false, true);
-            FastAddProperty("toJSON", new ClrFunctionInstance(Engine, ToJSON, 1), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToString, 0), true, false, true);
+            FastAddProperty("toDateString", new ClrFunctionInstance(Engine, "toDateString", ToDateString, 0), true, false, true);
+            FastAddProperty("toTimeString", new ClrFunctionInstance(Engine, "toTimeString", ToTimeString, 0), true, false, true);
+            FastAddProperty("toLocaleString", new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString, 0), true, false, true);
+            FastAddProperty("toLocaleDateString", new ClrFunctionInstance(Engine, "toLocaleDateString", ToLocaleDateString, 0), true, false, true);
+            FastAddProperty("toLocaleTimeString", new ClrFunctionInstance(Engine, "toLocaleTimeString", ToLocaleTimeString, 0), true, false, true);
+            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0), true, false, true);
+            FastAddProperty("getTime", new ClrFunctionInstance(Engine, "getTime", GetTime, 0), true, false, true);
+            FastAddProperty("getFullYear", new ClrFunctionInstance(Engine, "getFullYear", GetFullYear, 0), true, false, true);
+            FastAddProperty("getYear", new ClrFunctionInstance(Engine, "getYear", GetYear, 0), true, false, true);
+            FastAddProperty("getUTCFullYear", new ClrFunctionInstance(Engine, "getUTCFullYear", GetUTCFullYear, 0), true, false, true);
+            FastAddProperty("getMonth", new ClrFunctionInstance(Engine, "getMonth", GetMonth, 0), true, false, true);
+            FastAddProperty("getUTCMonth", new ClrFunctionInstance(Engine, "getUTCMonth", GetUTCMonth, 0), true, false, true);
+            FastAddProperty("getDate", new ClrFunctionInstance(Engine, "getDate", GetDate, 0), true, false, true);
+            FastAddProperty("getUTCDate", new ClrFunctionInstance(Engine, "getUTCDate", GetUTCDate, 0), true, false, true);
+            FastAddProperty("getDay", new ClrFunctionInstance(Engine, "getDay", GetDay, 0), true, false, true);
+            FastAddProperty("getUTCDay", new ClrFunctionInstance(Engine, "getUTCDay", GetUTCDay, 0), true, false, true);
+            FastAddProperty("getHours", new ClrFunctionInstance(Engine, "getHours", GetHours, 0), true, false, true);
+            FastAddProperty("getUTCHours", new ClrFunctionInstance(Engine, "getUTCHours", GetUTCHours, 0), true, false, true);
+            FastAddProperty("getMinutes", new ClrFunctionInstance(Engine, "getMinutes", GetMinutes, 0), true, false, true);
+            FastAddProperty("getUTCMinutes", new ClrFunctionInstance(Engine, "getUTCMInutes", GetUTCMinutes, 0), true, false, true);
+            FastAddProperty("getSeconds", new ClrFunctionInstance(Engine, "getSeconds", GetSeconds, 0), true, false, true);
+            FastAddProperty("getUTCSeconds", new ClrFunctionInstance(Engine, "getUTCSeconds", GetUTCSeconds, 0), true, false, true);
+            FastAddProperty("getMilliseconds", new ClrFunctionInstance(Engine, "getMilliseconds", GetMilliseconds, 0), true, false, true);
+            FastAddProperty("getUTCMilliseconds", new ClrFunctionInstance(Engine, "getUTCMilliseconds", GetUTCMilliseconds, 0), true, false, true);
+            FastAddProperty("getTimezoneOffset", new ClrFunctionInstance(Engine, "getTimezoneOffset", GetTimezoneOffset, 0), true, false, true);
+            FastAddProperty("setTime", new ClrFunctionInstance(Engine, "setTime", SetTime, 1), true, false, true);
+            FastAddProperty("setMilliseconds", new ClrFunctionInstance(Engine, "setMilliseconds", SetMilliseconds, 1), true, false, true);
+            FastAddProperty("setUTCMilliseconds", new ClrFunctionInstance(Engine, "setUTCMilliseconds", SetUTCMilliseconds, 1), true, false, true);
+            FastAddProperty("setSeconds", new ClrFunctionInstance(Engine, "setSeconds", SetSeconds, 2), true, false, true);
+            FastAddProperty("setUTCSeconds", new ClrFunctionInstance(Engine, "setUTCSeconds", SetUTCSeconds, 2), true, false, true);
+            FastAddProperty("setMinutes", new ClrFunctionInstance(Engine, "setMinutes", SetMinutes, 3), true, false, true);
+            FastAddProperty("setUTCMinutes", new ClrFunctionInstance(Engine, "setUTCMinutes", SetUTCMinutes, 3), true, false, true);
+            FastAddProperty("setHours", new ClrFunctionInstance(Engine, "setHours", SetHours, 4), true, false, true);
+            FastAddProperty("setUTCHours", new ClrFunctionInstance(Engine, "setUTCHours", SetUTCHours, 4), true, false, true);
+            FastAddProperty("setDate", new ClrFunctionInstance(Engine, "setDate", SetDate, 1), true, false, true);
+            FastAddProperty("setUTCDate", new ClrFunctionInstance(Engine, "setUTCDate", SetUTCDate, 1), true, false, true);
+            FastAddProperty("setMonth", new ClrFunctionInstance(Engine, "setMonth", SetMonth, 2), true, false, true);
+            FastAddProperty("setUTCMonth", new ClrFunctionInstance(Engine, "setUTCMonth", SetUTCMonth, 2), true, false, true);
+            FastAddProperty("setFullYear", new ClrFunctionInstance(Engine, "setFullYear", SetFullYear, 3), true, false, true);
+            FastAddProperty("setYear", new ClrFunctionInstance(Engine, "setYear", SetYear, 1), true, false, true);
+            FastAddProperty("setUTCFullYear", new ClrFunctionInstance(Engine, "setUTCFullYear     ", SetUTCFullYear, 3), true, false, true);
+            FastAddProperty("toUTCString", new ClrFunctionInstance(Engine, "toUTCString", ToUtcString, 0), true, false, true);
+            FastAddProperty("toISOString", new ClrFunctionInstance(Engine, "toISOString", ToISOString, 0), true, false, true);
+            FastAddProperty("toJSON", new ClrFunctionInstance(Engine, "toJSON", ToJSON, 1), true, false, true);
         }
 
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)

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

@@ -9,7 +9,7 @@ namespace Jint.Native.Error
     {
         private string _name;
 
-        public ErrorConstructor(Engine engine) : base(engine, null, null, false)
+        public ErrorConstructor(Engine engine) : base(engine, "Error", null, null, false)
         {
         }
 

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

@@ -36,7 +36,7 @@ namespace Jint.Native.Error
         public void Configure()
         {
             // Error prototype functions
-            FastAddProperty("toString", new ClrFunctionInstance(Engine, ToString), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToString), true, false, true);
         }
 
         public JsValue ToString(JsValue thisObject, JsValue[] arguments)

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

@@ -6,7 +6,8 @@ namespace Jint.Native.Function
 {
     public sealed class BindFunctionInstance : FunctionInstance, IConstructor
     {
-        public BindFunctionInstance(Engine engine) : base(engine, System.Array.Empty<string>(), null, false)
+        public BindFunctionInstance(Engine engine)
+            : base(engine, "bind", System.Array.Empty<string>(), null, false)
         {
         }
 

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

@@ -10,7 +10,8 @@ namespace Jint.Native.Function
     {
         private static readonly ParserOptions ParserOptions = new ParserOptions { AdaptRegexp = true, Tolerant = false };
 
-        public EvalFunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict) : base(engine, parameters, scope, strict)
+        public EvalFunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict) 
+            : base(engine, "eval", parameters, scope, strict)
         {
             Prototype = Engine.Function.PrototypeObject;
             SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));

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

@@ -11,7 +11,7 @@ namespace Jint.Native.Function
     {
         private static readonly ParserOptions ParserOptions = new ParserOptions { AdaptRegexp = true, Tolerant = false };
 
-        private FunctionConstructor(Engine engine):base(engine, null, null, false)
+        private FunctionConstructor(Engine engine):base(engine, "Function", null, null, false)
         {
         }
 

+ 39 - 7
Jint/Native/Function/FunctionInstance.cs

@@ -15,23 +15,35 @@ namespace Jint.Native.Function
         private const string PropertyNameLength = "length";
         private const int PropertyNameLengthLength = 6;
         protected PropertyDescriptor _length;
+
+        private const string PropertyNameName = "name";
+        private const int PropertyNameNameLength = 4;
+        private PropertyDescriptor _name;
+
         protected readonly LexicalEnvironment _scope;
         protected internal readonly string[] _formalParameters;
         private readonly bool _strict;
 
-        protected FunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict)
-            : this(engine, parameters, scope, strict, objectClass: "Function")
+        protected FunctionInstance(
+            Engine engine,
+            string name,
+            string[] parameters,
+            LexicalEnvironment scope,
+            bool strict)
+            : this(engine, name, parameters, scope, strict, objectClass: "Function")
         {
         }
 
         protected FunctionInstance(
-            Engine engine, 
-            string[] parameters, 
-            LexicalEnvironment scope, 
-            bool strict, 
+            Engine engine,
+            string name,
+            string[] parameters,
+            LexicalEnvironment scope,
+            bool strict,
             in string objectClass)
             : base(engine, objectClass)
         {
+            _name = new PropertyDescriptor(name, PropertyFlag.Configurable);
             _formalParameters = parameters;
             _scope = scope;
             _strict = strict;
@@ -117,6 +129,10 @@ namespace Jint.Native.Function
             {
                 yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameLength, _length);
             }
+            if (_name != null)
+            {
+                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameName, _name);
+            }
 
             foreach (var entry in base.GetOwnProperties())
             {
@@ -134,6 +150,10 @@ namespace Jint.Native.Function
             {
                 return _length ?? PropertyDescriptor.Undefined;
             }
+            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
+            {
+                return _name ?? PropertyDescriptor.Undefined;
+            }
 
             return base.GetOwnProperty(propertyName);
         }
@@ -148,6 +168,10 @@ namespace Jint.Native.Function
             {
                 _length = desc;
             }
+            else if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
+            {
+                _name = desc;
+            }
             else
             {
                 base.SetOwnProperty(propertyName, desc);
@@ -164,6 +188,10 @@ namespace Jint.Native.Function
             {
                 return _length != null;
             }
+            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
+            {
+                return _name != null;
+            }
 
             return base.HasOwnProperty(propertyName);
         }
@@ -176,7 +204,11 @@ namespace Jint.Native.Function
             }
             if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
-                _prototype = null;
+                _length = null;
+            }
+            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
+            {
+                _name = null;
             }
 
             base.RemoveOwnProperty(propertyName);

+ 7 - 6
Jint/Native/Function/FunctionPrototype.cs

@@ -11,7 +11,8 @@ namespace Jint.Native.Function
     /// </summary>
     public sealed class FunctionPrototype : FunctionInstance
     {
-        private FunctionPrototype(Engine engine) : base(engine, null, null, false)
+        private FunctionPrototype(Engine engine)
+            : base(engine, "Function", null, null, false)
         {
         }
 
@@ -31,10 +32,10 @@ namespace Jint.Native.Function
         public void Configure()
         {
             SetOwnProperty("constructor", new PropertyDescriptor(Engine.Function, PropertyFlag.NonEnumerable));
-            FastAddProperty("toString", new ClrFunctionInstance(Engine, ToString), true, false, true);
-            FastAddProperty("apply", new ClrFunctionInstance(Engine, Apply, 2), true, false, true);
-            FastAddProperty("call", new ClrFunctionInstance(Engine, CallImpl, 1), true, false, true);
-            FastAddProperty("bind", new ClrFunctionInstance(Engine, Bind, 1), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToString), true, false, true);
+            FastAddProperty("apply", new ClrFunctionInstance(Engine, "apply", Apply, 2), true, false, true);
+            FastAddProperty("call", new ClrFunctionInstance(Engine, "call", CallImpl, 1), true, false, true);
+            FastAddProperty("bind", new ClrFunctionInstance(Engine, "bind", Bind, 1), true, false, true);
         }
 
         private JsValue Bind(JsValue thisObj, JsValue[] arguments)
@@ -118,7 +119,7 @@ namespace Jint.Native.Function
 
             var result = func.Call(thisArg, argList);
             _engine._jsValueArrayPool.ReturnArray(argList);
-            
+
             return result;
         }
 

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

@@ -4,7 +4,8 @@ namespace Jint.Native.Function
 {
     public sealed class FunctionShim : FunctionInstance
     {
-        public FunctionShim(Engine engine, string[] parameters, LexicalEnvironment scope) : base(engine, parameters, scope, false)
+        public FunctionShim(Engine engine, string[] parameters, LexicalEnvironment scope)
+            : base(engine, "function", parameters, scope, false)
         {
         }
 

+ 7 - 63
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -14,11 +14,6 @@ namespace Jint.Native.Function
     /// </summary>
     public sealed class ScriptFunctionInstance : FunctionInstance, IConstructor
     {
-        private const string PropertyNameName = "name";
-        private const int PropertyNameNameLength = 4;
-
-        private PropertyDescriptor _name;
-
         private readonly IFunction _functionDeclaration;
 
         /// <summary>
@@ -28,8 +23,12 @@ namespace Jint.Native.Function
         /// <param name="functionDeclaration"></param>
         /// <param name="scope"></param>
         /// <param name="strict"></param>
-        public ScriptFunctionInstance(Engine engine, IFunction functionDeclaration, LexicalEnvironment scope, bool strict)
-            : base(engine, GetParameterNames(functionDeclaration), scope, strict)
+        public ScriptFunctionInstance(
+            Engine engine, 
+            IFunction functionDeclaration, 
+            LexicalEnvironment scope, 
+            bool strict)
+            : base(engine, functionDeclaration.Id?.Name ?? "", GetParameterNames(functionDeclaration), scope, strict)
         {
             _functionDeclaration = functionDeclaration;
 
@@ -48,7 +47,7 @@ namespace Jint.Native.Function
 
             if (_functionDeclaration.Id != null)
             {
-                _name = new PropertyDescriptor(_functionDeclaration.Id.Name, PropertyFlag.None);
+                DefineOwnProperty("name", new PropertyDescriptor(_functionDeclaration.Id.Name, PropertyFlag.None), false);
             }
 
             if (strict)
@@ -60,61 +59,6 @@ namespace Jint.Native.Function
             }
         }
 
-        public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
-        {
-            if (_name != null)
-            {
-                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameName, _name);
-            }
-
-            foreach (var entry in base.GetOwnProperties())
-            {
-                yield return entry;
-            }
-        }
-
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
-        {
-            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
-            {
-                return _name ?? PropertyDescriptor.Undefined;
-            }
-
-            return base.GetOwnProperty(propertyName);
-        }
-
-        protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
-        {
-            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
-            {
-                _name = desc;
-            }
-            else
-            {
-                base.SetOwnProperty(propertyName, desc);
-            }
-        }
-
-        public override bool HasOwnProperty(string propertyName)
-        {
-            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
-            {
-                return _name != null;
-            }
-
-            return base.HasOwnProperty(propertyName);
-        }
-
-        public override void RemoveOwnProperty(string propertyName)
-        {
-            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
-            {
-                _name = null;
-            }
-
-            base.RemoveOwnProperty(propertyName);
-        }
-
         private static string[] GetParameterNames(IFunction functionDeclaration)
         {
             var list = functionDeclaration.Params;

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

@@ -5,7 +5,8 @@ namespace Jint.Native.Function
 {
     public sealed class ThrowTypeError : FunctionInstance
     {
-        public ThrowTypeError(Engine engine): base(engine, System.Array.Empty<string>(), engine.GlobalEnvironment, false)
+        public ThrowTypeError(Engine engine)
+            : base(engine, "throwTypeError", System.Array.Empty<string>(), engine.GlobalEnvironment, false)
         {
             DefineOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden), false);
             Extensible = false;

+ 28 - 22
Jint/Native/Global/GlobalObject.cs

@@ -1,10 +1,12 @@
 using System;
+using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
 using System.Text;
 using Jint.Native.Object;
 using Jint.Native.String;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Global
@@ -34,6 +36,8 @@ namespace Jint.Native.Global
             FastAddProperty("Function", Engine.Function, true, false, true);
             FastAddProperty("Symbol", Engine.Symbol, true, false, true);
             FastAddProperty("Array", Engine.Array, true, false, true);
+            FastAddProperty("Map", Engine.Map, true, false, true);
+            FastAddProperty("Set", Engine.Set, true, false, true);
             FastAddProperty("String", Engine.String, true, false, true);
             FastAddProperty("RegExp", Engine.RegExp, true, false, true);
             FastAddProperty("Number", Engine.Number, true, false, true);
@@ -55,16 +59,16 @@ namespace Jint.Native.Global
             FastAddProperty("undefined", Undefined, false, false, false);
 
             // Global object functions
-            FastAddProperty("parseInt", new ClrFunctionInstance(Engine, ParseInt, 2), true, false, true);
-            FastAddProperty("parseFloat", new ClrFunctionInstance(Engine, ParseFloat, 1), true, false, true);
-            FastAddProperty("isNaN", new ClrFunctionInstance(Engine, IsNaN, 1), true, false, true);
-            FastAddProperty("isFinite", new ClrFunctionInstance(Engine, IsFinite, 1), true, false, true);
-            FastAddProperty("decodeURI", new ClrFunctionInstance(Engine, DecodeUri, 1), true, false, true);
-            FastAddProperty("decodeURIComponent", new ClrFunctionInstance(Engine, DecodeUriComponent, 1), true, false, true);
-            FastAddProperty("encodeURI", new ClrFunctionInstance(Engine, EncodeUri, 1), true, false, true);
-            FastAddProperty("encodeURIComponent", new ClrFunctionInstance(Engine, EncodeUriComponent, 1), true, false, true);
-            FastAddProperty("escape", new ClrFunctionInstance(Engine, Escape, 1), true, false, true);
-            FastAddProperty("unescape", new ClrFunctionInstance(Engine, Unescape, 1), true, false, true);
+            FastAddProperty("parseInt", new ClrFunctionInstance(Engine, "parseInt", ParseInt, 2, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("parseFloat", new ClrFunctionInstance(Engine, "parseFloat", ParseFloat, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("isNaN", new ClrFunctionInstance(Engine, "isNaN", IsNaN, 1), true, false, true);
+            FastAddProperty("isFinite", new ClrFunctionInstance(Engine, "isFinite", IsFinite, 1), true, false, true);
+            FastAddProperty("decodeURI", new ClrFunctionInstance(Engine, "decodeURI", DecodeUri, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("decodeURIComponent", new ClrFunctionInstance(Engine, "decodeURIComponent", DecodeUriComponent, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("encodeURI", new ClrFunctionInstance(Engine, "encodeURI", EncodeUri, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("encodeURIComponent", new ClrFunctionInstance(Engine, "encodeURIComponent", EncodeUriComponent, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("escape", new ClrFunctionInstance(Engine, "escape", Escape, 1), true, false, true);
+            FastAddProperty("unescape", new ClrFunctionInstance(Engine, "unescape", Unescape, 1), true, false, true);
         }
 
         /// <summary>
@@ -73,7 +77,7 @@ namespace Jint.Native.Global
         public static JsValue ParseInt(JsValue thisObject, JsValue[] arguments)
         {
             string inputString = TypeConverter.ToString(arguments.At(0));
-            var s = StringPrototype.TrimEx(inputString);
+            var s = StringPrototype.TrimEx(inputString, acceptMongolianVowelSeparator: false);
 
             var sign = 1;
             if (!System.String.IsNullOrEmpty(s))
@@ -174,7 +178,7 @@ namespace Jint.Native.Global
         public static JsValue ParseFloat(JsValue thisObject, JsValue[] arguments)
         {
             var inputString = TypeConverter.ToString(arguments.At(0));
-            var trimmedString = StringPrototype.TrimStartEx(inputString);
+            var trimmedString = StringPrototype.TrimStartEx(inputString, acceptMongolianVowelSeparator: false);
 
             var sign = 1;
             if (trimmedString.Length > 0)
@@ -347,9 +351,12 @@ namespace Jint.Native.Global
             return true;
         }
 
-        private static readonly char[] UriReserved = { ';', '/', '?', ':', '@', '&', '=', '+', '$', ',' };
+        private static readonly HashSet<char> UriReserved = new HashSet<char>
+        {
+            ';', '/', '?', ':', '@', '&', '=', '+', '$', ','
+        };
 
-        private static readonly char[] UriUnescaped =
+        private static readonly HashSet<char> UriUnescaped = new HashSet<char>
         {
             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
             'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
@@ -357,8 +364,8 @@ namespace Jint.Native.Global
             '~', '*', '\'', '(', ')'
         };
 
-        private static readonly char[] UnescapedUriSet = UriReserved.Concat(UriUnescaped).Concat(new[] { '#' }).ToArray();
-        private static readonly char[] ReservedUriSet = UriReserved.Concat(new[] { '#' }).ToArray();
+        private static readonly HashSet<char> UnescapedUriSet = new HashSet<char>(UriReserved.Concat(UriUnescaped).Concat(new[] { '#' }));
+        private static readonly HashSet<char> ReservedUriSet = new HashSet<char>(UriReserved.Concat(new[] { '#' }));
 
         private const string HexaMap = "0123456789ABCDEF";
 
@@ -396,7 +403,7 @@ namespace Jint.Native.Global
             return Encode(uriString, UriUnescaped);
         }
 
-        private string Encode(string uriString, char[] unescapedUriSet)
+        private string Encode(string uriString, HashSet<char> unescapedUriSet)
         {
             var strLen = uriString.Length;
 
@@ -406,7 +413,7 @@ namespace Jint.Native.Global
             for (var k = 0; k < strLen; k++)
             {
                 var c = uriString[k];
-                if (System.Array.IndexOf(unescapedUriSet, c) != -1)
+                if (unescapedUriSet != null && unescapedUriSet.Contains(c))
                 {
                     _stringBuilder.Append(c);
                 }
@@ -512,12 +519,11 @@ namespace Jint.Native.Global
         public JsValue DecodeUriComponent(JsValue thisObject, JsValue[] arguments)
         {
             var componentString = TypeConverter.ToString(arguments.At(0));
-            var reservedUriComponentSet = new char[0];
 
-            return Decode(componentString, reservedUriComponentSet);
+            return Decode(componentString, null);
         }
 
-        public string Decode(string uriString, char[] reservedSet)
+        private string Decode(string uriString, HashSet<char> reservedSet)
         {
             var strLen = uriString.Length;
 
@@ -550,7 +556,7 @@ namespace Jint.Native.Global
                     if ((B & 0x80) == 0)
                     {
                         C = (char)B;
-                        if (System.Array.IndexOf(reservedSet, C) == -1)
+                        if (reservedSet == null || !reservedSet.Contains(C))
                         {
                             _stringBuilder.Append(C);
                         }

+ 10 - 0
Jint/Native/Iterator/IIterator.cs

@@ -0,0 +1,10 @@
+using Jint.Native.Object;
+
+namespace Jint.Native.Iterator
+{
+    public interface IIterator
+    {
+        ObjectInstance Next();
+        void Return();
+    }
+}

+ 118 - 0
Jint/Native/Iterator/IteratorConstructor.cs

@@ -0,0 +1,118 @@
+using System.Collections.Generic;
+using System.Linq;
+using Jint.Native.Array;
+using Jint.Native.Function;
+using Jint.Native.Map;
+using Jint.Native.Object;
+using Jint.Native.Set;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.Iterator
+{
+    public sealed class IteratorConstructor : FunctionInstance, IConstructor
+    {
+        private IteratorConstructor(Engine engine)
+            : base(engine, "iterator", null, null, false)
+        {
+        }
+
+        internal IteratorPrototype PrototypeObject { get; private set; }
+
+        public static IteratorConstructor CreateIteratorConstructor(Engine engine)
+        {
+            var obj = new IteratorConstructor(engine);
+            obj.Extensible = true;
+
+            // The value of the [[Prototype]] internal property of the Map constructor is the Function prototype object
+            obj.Prototype = engine.Function.PrototypeObject;
+            obj.PrototypeObject = IteratorPrototype.CreatePrototypeObject(engine, obj);
+
+            obj.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.Configurable));
+
+            // The initial value of Map.prototype is the Map prototype object
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
+
+            return obj;
+        }
+
+        public void Configure()
+        {
+        }
+
+        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
+        {
+            return Construct(arguments);
+        }
+
+        public ObjectInstance Construct(JsValue[] arguments)
+        {
+            return Construct(Enumerable.Empty<JsValue>());
+        }
+
+        internal ObjectInstance Construct(IEnumerable<JsValue> enumerable)
+        {
+            var instance = new IteratorInstance(Engine, enumerable)
+            {
+                Prototype = PrototypeObject,
+                Extensible = true
+            };
+
+            return instance;
+        }
+
+        internal ObjectInstance Construct(List<JsValue> enumerable)
+        {
+            var instance = new IteratorInstance.ListIterator(Engine, enumerable)
+            {
+                Prototype = PrototypeObject,
+                Extensible = true
+            };
+
+            return instance;
+        }
+
+        internal ObjectInstance Construct(ArrayInstance array)
+        {
+            var instance = new IteratorInstance.ArrayIterator(Engine, array)
+            {
+                Prototype = PrototypeObject,
+                Extensible = true
+            };
+
+            return instance;
+        }
+
+        internal ObjectInstance Construct(MapInstance map)
+        {
+            var instance = new IteratorInstance.MapIterator(Engine, map)
+            {
+                Prototype = PrototypeObject,
+                Extensible = true
+            };
+
+            return instance;
+        }
+
+        internal ObjectInstance Construct(SetInstance set)
+        {
+            var instance = new IteratorInstance.SetIterator(Engine, set)
+            {
+                Prototype = PrototypeObject,
+                Extensible = true
+            };
+
+            return instance;
+        }
+
+        internal ObjectInstance ConstructEntryIterator(SetInstance set)
+        {
+            var instance = new IteratorInstance.SetEntryIterator(Engine, set)
+            {
+                Prototype = PrototypeObject,
+                Extensible = true
+            };
+
+            return instance;
+        }
+    }
+}

+ 246 - 0
Jint/Native/Iterator/IteratorInstance.cs

@@ -0,0 +1,246 @@
+using System.Collections.Generic;
+using System.Linq;
+using Jint.Native.Array;
+using Jint.Native.Map;
+using Jint.Native.Object;
+using Jint.Native.Set;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.Iterator
+{
+    internal class IteratorInstance : ObjectInstance, IIterator
+    {
+        private readonly IEnumerator<JsValue> _enumerable;
+
+        public IteratorInstance(Engine engine)
+            : this(engine, Enumerable.Empty<JsValue>())
+        {
+        }
+
+        public IteratorInstance(
+            Engine engine,
+            IEnumerable<JsValue> enumerable) : base(engine, "Iterator")
+        {
+            _enumerable = enumerable.GetEnumerator();
+        }
+
+        public override object ToObject()
+        {
+            throw new System.NotImplementedException();
+        }
+
+        public override bool Equals(JsValue other)
+        {
+            return false;
+        }
+
+        public virtual ObjectInstance Next()
+        {
+            if (_enumerable.MoveNext())
+            {
+                return new ValueIteratorPosition(_engine, _enumerable.Current);
+            }
+
+            return ValueIteratorPosition.Done;
+        }
+
+        public void Return()
+        {
+        }
+
+        private class KeyValueIteratorPosition : ObjectInstance
+        {
+            internal static readonly ObjectInstance Done = new KeyValueIteratorPosition(null, null, null);
+
+            public KeyValueIteratorPosition(Engine engine, JsValue key, JsValue value) : base(engine)
+            {
+                var done = ReferenceEquals(null, key) && ReferenceEquals(null, value);
+                if (!done)
+                {
+                    var arrayInstance = engine.Array.ConstructFast(2);
+                    arrayInstance.SetIndexValue(0, key, false);
+                    arrayInstance.SetIndexValue(1, value, false);
+                    SetOwnProperty("value", new PropertyDescriptor(arrayInstance, PropertyFlag.AllForbidden));
+                }
+                SetOwnProperty("done", new PropertyDescriptor(done, PropertyFlag.AllForbidden));
+            }
+        }
+
+        private class ValueIteratorPosition : ObjectInstance
+        {
+            internal static readonly ObjectInstance Done = new KeyValueIteratorPosition(null, null, null);
+
+            public ValueIteratorPosition(Engine engine, JsValue value) : base(engine)
+            {
+                var done = ReferenceEquals(null, value);
+                if (!done)
+                {
+                    SetOwnProperty("value", new PropertyDescriptor(value, PropertyFlag.AllForbidden));
+                }
+                SetOwnProperty("done", new PropertyDescriptor(done, PropertyFlag.AllForbidden));
+            }
+        }
+
+        public class MapIterator : IteratorInstance
+        {
+            private readonly MapInstance _map;
+
+            private int _position;
+
+            public MapIterator(Engine engine, MapInstance map) : base(engine)
+            {
+                _map = map;
+                _position = 0;
+            }
+
+            public override ObjectInstance Next()
+            {
+                if (_position < _map.GetSize())
+                {
+                    var key  = _map._map.GetKey(_position);
+                    var value = _map._map[key];
+
+                    _position++;
+                    return new KeyValueIteratorPosition(_engine, key, value);
+                }
+
+                return KeyValueIteratorPosition.Done;
+            }
+        }
+
+        public class ArrayIterator : IteratorInstance
+        {
+            private readonly ArrayPrototype.ArrayOperations _array;
+            private uint? _end;
+            private uint _position;
+
+            public ArrayIterator(Engine engine, JsValue target) : base(engine)
+            {
+                if (!(target is ObjectInstance objectInstance))
+                {
+                    ExceptionHelper.ThrowTypeError(engine, "Target must be an object");
+                    return;
+                }
+
+                _array = ArrayPrototype.ArrayOperations.For(objectInstance);
+                _position = 0;
+            }
+
+            public override ObjectInstance Next()
+            {
+                if (_end == null)
+                {
+                    _end = _array.GetLength();
+                }
+
+                if (_position < _end.Value)
+                {
+                    _array.TryGetValue(_position, out var value);
+                    return new KeyValueIteratorPosition(_engine, _position++, value);
+                }
+
+                return KeyValueIteratorPosition.Done;
+            }
+        }
+
+        public class SetIterator : IteratorInstance
+        {
+            private readonly SetInstance _set;
+            private int _position;
+
+            public SetIterator(Engine engine, SetInstance set) : base(engine)
+            {
+                _set = set;
+                _position = 0;
+            }
+
+            public override ObjectInstance Next()
+            {
+                if (_position < _set._set._list.Count)
+                {
+                    var value = _set._set[_position];
+                    _position++;
+                    return new  ValueIteratorPosition(_engine, value);
+                }
+
+                return KeyValueIteratorPosition.Done;
+            }
+        }
+
+        public class SetEntryIterator : IteratorInstance
+        {
+            private readonly SetInstance _set;
+            private int _position;
+
+            public SetEntryIterator(Engine engine, SetInstance set) : base(engine)
+            {
+                _set = set;
+                _position = 0;
+            }
+
+            public override ObjectInstance Next()
+            {
+                if (_position < _set._set._list.Count)
+                {
+                    var value = _set._set[_position];
+                    _position++;
+                    return new  KeyValueIteratorPosition(_engine, value, value);
+                }
+
+                return KeyValueIteratorPosition.Done;
+            }
+        }
+
+        public class ListIterator : IteratorInstance
+        {
+            private readonly List<JsValue> _values;
+            private int _position;
+            private bool _closed;
+
+            public ListIterator(Engine engine, List<JsValue> values) : base(engine)
+            {
+                _values = values;
+                _position = 0;
+            }
+
+            public override ObjectInstance Next()
+            {
+                if (!_closed && _position < _values.Count)
+                {
+                    var value = _values[_position];
+                    _position++;
+                    return new  ValueIteratorPosition(_engine, value);
+                }
+
+                _closed = true;
+                return ValueIteratorPosition.Done;
+            }
+        }
+
+        internal class ObjectWrapper : IIterator
+        {
+            private readonly ObjectInstance _target;
+            private readonly ICallable _callable;
+
+            public ObjectWrapper(ObjectInstance target)
+            {
+                _target = target;
+                _callable = (ICallable) target.Get("next");
+            }
+
+            public ObjectInstance Next()
+            {
+                return (ObjectInstance) _callable.Call(_target, System.Array.Empty<JsValue>());
+            }
+
+            public void Return()
+            {
+                if (_target.TryGetValue("return", out var func))
+                {
+                    ((ICallable) func).Call(_target, System.Array.Empty<JsValue>());
+                }
+            }
+        }
+    }
+}

+ 34 - 0
Jint/Native/Iterator/IteratorPrototype.cs

@@ -0,0 +1,34 @@
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.Iterator
+{
+    internal sealed class IteratorPrototype : IteratorInstance
+    {
+        private IteratorPrototype(Engine engine) : base(engine)
+        {
+        }
+
+        public static IteratorPrototype CreatePrototypeObject(Engine engine, IteratorConstructor iteratorConstructor)
+        {
+            var obj = new IteratorPrototype(engine)
+            {
+                Extensible = true, Prototype = engine.Object.PrototypeObject
+            };
+
+            obj.SetOwnProperty("name", new PropertyDescriptor("Map", PropertyFlag.Configurable));
+
+            return obj;
+        }
+
+        public void Configure()
+        {
+            FastAddProperty("next", new ClrFunctionInstance(Engine, "next", Next, 0), true, false, true);
+        }
+
+        private JsValue Next(JsValue thisObj, JsValue[] arguments)
+        {
+            return ((IteratorInstance) thisObj).Next();
+        }
+    }
+}

+ 13 - 1
Jint/Native/JsString.cs

@@ -1,10 +1,11 @@
 using System;
 using System.Text;
+using Jint.Native.Array;
 using Jint.Runtime;
 
 namespace Jint.Native
 {
-    public class JsString : JsValue, IEquatable<JsString>
+    public class JsString : JsValue, IEquatable<JsString>, IArrayLike
     {
         private const int AsciiMax = 126;
         private static readonly JsString[] _charToJsValue;
@@ -96,6 +97,17 @@ namespace Jint.Native
             return _value;
         }
 
+        public ArrayInstance ToArray(Engine engine)
+        {
+            var array = engine.Array.ConstructFast((uint) this._value.Length);
+            for (uint i = 0; i < _value.Length; ++i)
+            {
+                array.SetIndexValue(i, this._value[0], updateLength: false);
+            }
+
+            return array;
+        }
+
         public override bool Equals(JsValue obj)
         {
             if (ReferenceEquals(null, obj))

+ 9 - 23
Jint/Native/JsSymbol.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Runtime.CompilerServices;
 using Jint.Runtime;
 
 namespace Jint.Native
@@ -20,39 +21,24 @@ namespace Jint.Native
             return _value;
         }
 
-        public override bool Equals(JsValue obj)
+        public override string ToString()
         {
-            if (ReferenceEquals(null, obj))
-            {
-                return false;
-            }
-
-            if (!(obj is JsBoolean number))
-            {
-                return false;
-            }
+            return "Symbol(" + _value + ")";
+        }
 
-            return Equals(number);
+        public override bool Equals(JsValue obj)
+        {
+            return ReferenceEquals(this, obj);
         }
 
         public bool Equals(JsSymbol other)
         {
-            if (ReferenceEquals(null, other))
-            {
-                return false;
-            }
-
-            if (ReferenceEquals(this, other))
-            {
-                return true;
-            }
-
-            return _value == other._value;
+            return ReferenceEquals(this, other);
         }
 
         public override int GetHashCode()
         {
-            return _value.GetHashCode();
+            return RuntimeHelpers.GetHashCode(this);
         }
     }
 }

+ 33 - 0
Jint/Native/JsValue.cs

@@ -6,8 +6,10 @@ using System.Runtime.CompilerServices;
 using System.Threading;
 using Jint.Native.Array;
 using Jint.Native.Date;
+using Jint.Native.Iterator;
 using Jint.Native.Object;
 using Jint.Native.RegExp;
+using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
@@ -142,6 +144,37 @@ namespace Jint.Native
             }
             return this as ArrayInstance;
         }
+        
+        [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal IIterator GetIterator()
+        {
+            if (!(this is ObjectInstance oi))
+            {
+                ExceptionHelper.ThrowArgumentException("The value is not iterable");
+                return null;
+            }
+
+            // TODO
+            if (!oi.TryGetValue(GlobalSymbolRegistry.Iterator._value, out var value))
+            {
+                ExceptionHelper.ThrowArgumentException("The value is not iterable");
+                return null;
+            }
+
+            if (!(value is ICallable callable))
+            {
+                ExceptionHelper.ThrowArgumentException("The value is not iterable");
+                return null;
+            }
+
+            var obj = (ObjectInstance) callable.Call(this, Arguments.Empty);
+            if (obj is IIterator iterator)
+            {
+                return iterator;
+            }
+            return new IteratorInstance.ObjectWrapper(obj);
+        }
 
         [Pure]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]

+ 2 - 2
Jint/Native/Json/JsonInstance.cs

@@ -24,8 +24,8 @@ namespace Jint.Native.Json
 
         public void Configure()
         {
-            FastAddProperty("parse", new ClrFunctionInstance(Engine, Parse, 2), true, false, true);
-            FastAddProperty("stringify", new ClrFunctionInstance(Engine, Stringify, 3), true, false, true);
+            FastAddProperty("parse", new ClrFunctionInstance(Engine, "parse", Parse, 2), true, false, true);
+            FastAddProperty("stringify", new ClrFunctionInstance(Engine, "stringify", Stringify, 3), true, false, true);
         }
 
         private JsValue AbstractWalkOperation(ObjectInstance thisObject, string prop)

+ 152 - 0
Jint/Native/Map/MapConstructor.cs

@@ -0,0 +1,152 @@
+using Jint.Native.Function;
+using Jint.Native.Object;
+using Jint.Native.Symbol;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.Map
+{
+    public sealed class MapConstructor : FunctionInstance, IConstructor
+    {
+        private MapConstructor(Engine engine, string name) :  base(engine, name, null, null, false)
+        {
+        }
+
+        public MapPrototype PrototypeObject { get; private set; }
+
+        public static MapConstructor CreateMapConstructor(Engine engine)
+        {
+            MapConstructor CreateMapConstructorTemplate(string name)
+            {
+                var mapConstructor = new MapConstructor(engine, name);
+                mapConstructor.Extensible = true;
+
+                // The value of the [[Prototype]] internal property of the Map constructor is the Function prototype object
+                mapConstructor.Prototype = engine.Function.PrototypeObject;
+                mapConstructor.PrototypeObject = MapPrototype.CreatePrototypeObject(engine, mapConstructor);
+
+                mapConstructor.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.Configurable));
+                return mapConstructor;
+            }
+
+            var obj = CreateMapConstructorTemplate("Map");
+
+            // The initial value of Map.prototype is the Map prototype object
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
+
+            obj.SetOwnProperty(GlobalSymbolRegistry.Species._value,
+                new GetSetPropertyDescriptor(
+                    get: new ClrFunctionInstance(engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable),
+                    set: Undefined,
+                    PropertyFlag.Configurable));
+
+            return obj;
+        }
+
+        public void Configure()
+        {
+        }
+
+        private static JsValue Species(JsValue thisObject, JsValue[] arguments)
+        {
+            return thisObject;
+        }
+
+        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
+        {
+            if (thisObject.IsUndefined())
+            {
+                ExceptionHelper.ThrowTypeError(_engine, "Constructor Map requires 'new'");
+            }
+
+            return Construct(arguments);
+        }
+
+        public ObjectInstance Construct(JsValue[] arguments)
+        {
+            var instance = new MapInstance(Engine)
+            {
+                Prototype = PrototypeObject,
+                Extensible = true
+            };
+
+            if (arguments.Length > 0
+                && !arguments[0].IsUndefined()
+                && !arguments[0].IsNull())
+            {
+                var iterator = arguments.At(0).GetIterator();
+                if (iterator != null)
+                {
+                    var setterProperty = instance.GetProperty("set");
+
+                    ICallable setter = null;
+                    if (setterProperty == null
+                        || !setterProperty.TryGetValue(instance, out var setterValue)
+                        || (setter = setterValue as ICallable) == null)
+                    {
+                        ExceptionHelper.ThrowTypeError(_engine, "set must be callable");
+                        return null;
+                    }
+                    
+                    var args = _engine._jsValueArrayPool.RentArray(2);
+                    try
+                    {
+                        do
+                        {
+                            var item = iterator.Next();
+                            if (item.TryGetValue("done", out var done) && done.AsBoolean())
+                            {
+                                break;
+                            }
+
+                            if (!item.TryGetValue("value", out var currentValue))
+                            {
+                                break;
+                            }
+
+                            if (!(currentValue is ObjectInstance oi))
+                            {
+                                ExceptionHelper.ThrowTypeError(_engine, "iterator's value must be an object");
+                                break;
+                            }
+
+                            JsValue key = Undefined;
+                            JsValue value = Undefined;
+                            if (oi.TryGetValue("0", out var arrayIndex)
+                                && oi.TryGetValue("1", out var source))
+                            {
+                                if (source is ObjectInstance oi2)
+                                {
+                                    key = oi2.Get("0");
+                                    value = oi2.Get("1");
+                                }
+                                else
+                                {
+                                    ExceptionHelper.ThrowTypeError(_engine, "iterator's value must be an object");
+                                    break;
+                                }
+                            }
+
+                            args[0] = key;
+                            args[1] = value;
+                            setter.Call(instance, args);
+                        } while (true);
+                    }
+                    catch
+                    {
+                        iterator.Return();
+                        throw;
+                    }
+                    finally
+                    {
+                        _engine._jsValueArrayPool.ReturnArray(args);
+                    }
+                }
+            }
+
+            return instance;
+        }
+    }
+}

+ 144 - 0
Jint/Native/Map/MapInstance.cs

@@ -0,0 +1,144 @@
+using System.Runtime.CompilerServices;
+using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.Map
+{
+    public class MapInstance : ObjectInstance
+    {
+        internal readonly OrderedDictionary<JsValue, JsValue> _map;
+
+        public MapInstance(Engine engine)
+            : base(engine, objectClass: "Map")
+        {
+            _map = new OrderedDictionary<JsValue, JsValue>();
+        }
+
+        /// Implementation from ObjectInstance official specs as the one
+        /// in ObjectInstance is optimized for the general case and wouldn't work
+        /// for arrays
+        public override void Put(string propertyName, JsValue value, bool throwOnError)
+        {
+            if (!CanPut(propertyName))
+            {
+                if (throwOnError)
+                {
+                    ExceptionHelper.ThrowTypeError(Engine);
+                }
+
+                return;
+            }
+
+            var ownDesc = GetOwnProperty(propertyName);
+
+            if (ownDesc.IsDataDescriptor())
+            {
+                var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
+                DefineOwnProperty(propertyName, valueDesc, throwOnError);
+                return;
+            }
+
+            // property is an accessor or inherited
+            var desc = GetProperty(propertyName);
+
+            if (desc.IsAccessorDescriptor())
+            {
+                var setter = desc.Set.TryCast<ICallable>();
+                setter.Call(this, new[] {value});
+            }
+            else
+            {
+                var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
+                DefineOwnProperty(propertyName, newDesc, throwOnError);
+            }
+        }
+
+        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        {
+            if (propertyName.Length == 4 && propertyName == "size")
+            {
+                return new PropertyDescriptor(_map.Count, PropertyFlag.None);
+            }
+
+            return base.GetOwnProperty(propertyName);
+        }
+
+        protected override bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
+        {
+            if (propertyName.Length == 4 && propertyName == "size")
+            {
+                descriptor = new PropertyDescriptor(_map.Count, PropertyFlag.None);
+                return true;
+            }
+
+            return base.TryGetProperty(propertyName, out descriptor);
+        }
+
+        public void Clear()
+        {
+            _map.Clear();
+        }
+
+        public bool Has(JsValue key)
+        {
+            return _map.ContainsKey(key);
+        }
+
+        public bool Delete(JsValue key)
+        {
+            return _map.Remove(key);
+        }
+
+        public void Set(JsValue key, JsValue value)
+        {
+            _map[key] = value;
+        }
+
+        public void ForEach(ICallable callable, JsValue thisArg)
+        {
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = this;
+
+            for (var i = 0; i < _map.Count; i++)
+            {
+                args[0] = _map[i];
+                args[1] = _map.GetKey(i);
+                callable.Call(thisArg, args);
+            }
+
+            _engine._jsValueArrayPool.ReturnArray(args);
+        }
+
+        public JsValue Get(JsValue key)
+        {
+            if (!_map.TryGetValue(key, out var value))
+            {
+                return Undefined;
+            }
+
+            return value;
+        }
+
+        public ObjectInstance Iterator()
+        {
+            return _engine.Iterator.Construct(this);
+        }
+
+        public ObjectInstance Keys()
+        {
+            return _engine.Iterator.Construct(_map.Keys);
+        }
+
+        public ObjectInstance Values()
+        {
+            return _engine.Iterator.Construct(_map.Values);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal uint GetSize()
+        {
+            return (uint) _map.Count;
+        }
+    }
+}

+ 133 - 0
Jint/Native/Map/MapPrototype.cs

@@ -0,0 +1,133 @@
+using Jint.Native.Object;
+using Jint.Native.Symbol;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.Map
+{
+    /// <summary>
+    /// https://www.ecma-international.org/ecma-262/6.0/#sec-map-objects
+    /// </summary>
+    public sealed class MapPrototype : ObjectInstance
+    {
+        private MapPrototype(Engine engine) : base(engine)
+        {
+        }
+
+        public static MapPrototype CreatePrototypeObject(Engine engine, MapConstructor mapConstructor)
+        {
+            var obj = new MapPrototype(engine)
+            {
+                Extensible = true,
+                Prototype = engine.Object.PrototypeObject
+            };
+
+            obj.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.Configurable));
+            obj.SetOwnProperty("constructor", new PropertyDescriptor(mapConstructor, PropertyFlag.NonEnumerable));
+
+            return obj;
+        }
+
+        public void Configure()
+        {
+            FastAddProperty(GlobalSymbolRegistry.Iterator._value, new ClrFunctionInstance(Engine, "iterator", Iterator, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty(GlobalSymbolRegistry.ToStringTag._value, "Map", false, false, true);
+
+            FastAddProperty("clear", new ClrFunctionInstance(Engine, "clear", Clear, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("delete", new ClrFunctionInstance(Engine, "delete", Delete, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("entries", new ClrFunctionInstance(Engine, "entries", Iterator, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("forEach", new ClrFunctionInstance(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("get", new ClrFunctionInstance(Engine, "get", Get, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("has", new ClrFunctionInstance(Engine, "has", Has, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("keys", new ClrFunctionInstance(Engine, "keys", Keys, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("set", new ClrFunctionInstance(Engine, "set", Set, 2, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("values", new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), true, false, true);
+
+            AddProperty(
+                "size",
+                new GetSetPropertyDescriptor(
+                    get: new ClrFunctionInstance(Engine, "get size", Size, 0, PropertyFlag.Configurable),
+                    set: null,
+                    PropertyFlag.Configurable));
+        }
+
+        private JsValue Size(JsValue thisObj, JsValue[] arguments)
+        {
+            if (!(thisObj is MapInstance))
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+            return JsNumber.Create(0);
+        }
+
+        private JsValue Get(JsValue thisObj, JsValue[] arguments)
+        {
+            return ((MapInstance) thisObj).Get(arguments.At(0));
+        }
+
+        private JsValue Clear(JsValue thisObj, JsValue[] arguments)
+        {
+            var map = thisObj as MapInstance
+                      ?? ExceptionHelper.ThrowTypeError<MapInstance>(_engine, "object must be a Map");
+
+            map.Clear();
+            return Undefined;
+        }
+
+        private JsValue Delete(JsValue thisObj, JsValue[] arguments)
+        {
+            var map = thisObj as MapInstance
+                      ?? ExceptionHelper.ThrowTypeError<MapInstance>(_engine, "object must be a Map");
+
+            return map.Delete(arguments[0])
+                ? JsBoolean.True
+                : JsBoolean.False;
+        }
+
+        private JsValue Set(JsValue thisObj, JsValue[] arguments)
+        {
+            ((MapInstance) thisObj).Set(arguments[0], arguments[1]);
+            return thisObj;
+        }
+
+        private JsValue Has(JsValue thisObj, JsValue[] arguments)
+        {
+            return ((MapInstance) thisObj).Has(arguments[0])
+                ? JsBoolean.True
+                : JsBoolean.False;
+        }
+
+        private JsValue ForEach(JsValue thisObj, JsValue[] arguments)
+        {
+            var callbackfn = arguments.At(0);
+            var thisArg = arguments.At(1);
+
+            var map = (MapInstance) thisObj;
+
+            var callable = GetCallable(callbackfn);
+
+            map.ForEach(callable, thisArg);
+
+            return Undefined;
+        }
+
+        private ObjectInstance Iterator(JsValue thisObj, JsValue[] arguments)
+        {
+            return ((MapInstance) thisObj).Iterator();
+        }
+
+        private ObjectInstance Keys(JsValue thisObj, JsValue[] arguments)
+        {
+            return ((MapInstance) thisObj).Keys();
+        }
+
+        private ObjectInstance Values(JsValue thisObj, JsValue[] arguments)
+        {
+            return ((MapInstance) thisObj).Values();
+        }
+
+
+    }
+}

+ 30 - 29
Jint/Native/Math/MathInstance.cs

@@ -2,6 +2,7 @@
 using Jint.Native.Number;
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Math
@@ -25,28 +26,28 @@ namespace Jint.Native.Math
 
         public void Configure()
         {
-            FastAddProperty("abs", new ClrFunctionInstance(Engine, Abs), true, false, true);
-            FastAddProperty("acos", new ClrFunctionInstance(Engine, Acos), true, false, true);
-            FastAddProperty("asin", new ClrFunctionInstance(Engine, Asin), true, false, true);
-            FastAddProperty("atan", new ClrFunctionInstance(Engine, Atan), true, false, true);
-            FastAddProperty("atan2", new ClrFunctionInstance(Engine, Atan2), true, false, true);
-            FastAddProperty("ceil", new ClrFunctionInstance(Engine, Ceil), true, false, true);
-            FastAddProperty("cos", new ClrFunctionInstance(Engine, Cos), true, false, true);
-            FastAddProperty("exp", new ClrFunctionInstance(Engine, Exp), true, false, true);
-            FastAddProperty("floor", new ClrFunctionInstance(Engine, Floor), true, false, true);
-            FastAddProperty("log", new ClrFunctionInstance(Engine, Log), true, false, true);
-            FastAddProperty("max", new ClrFunctionInstance(Engine, Max, 2), true, false, true);
-            FastAddProperty("min", new ClrFunctionInstance(Engine, Min, 2), true, false, true);
-            FastAddProperty("pow", new ClrFunctionInstance(Engine, Pow, 2), true, false, true);
-            FastAddProperty("random", new ClrFunctionInstance(Engine, Random), true, false, true);
-            FastAddProperty("round", new ClrFunctionInstance(Engine, Round), true, false, true);
-            FastAddProperty("sin", new ClrFunctionInstance(Engine, Sin), true, false, true);
-            FastAddProperty("sqrt", new ClrFunctionInstance(Engine, Sqrt), true, false, true);
-            FastAddProperty("tan", new ClrFunctionInstance(Engine, Tan), true, false, true);
-
-            FastAddProperty("trunc", new ClrFunctionInstance(Engine, Truncate), true, false, true);
-            FastAddProperty("sign", new ClrFunctionInstance(Engine, Sign), true, false, true);
-            FastAddProperty("cbrt", new ClrFunctionInstance(Engine, Cbrt), true, false, true);
+            FastAddProperty("abs", new ClrFunctionInstance(Engine, "abs", Abs), true, false, true);
+            FastAddProperty("acos", new ClrFunctionInstance(Engine, "acos", Acos), true, false, true);
+            FastAddProperty("asin", new ClrFunctionInstance(Engine, "asin", Asin), true, false, true);
+            FastAddProperty("atan", new ClrFunctionInstance(Engine, "atan", Atan), true, false, true);
+            FastAddProperty("atan2", new ClrFunctionInstance(Engine, "atan2", Atan2), true, false, true);
+            FastAddProperty("ceil", new ClrFunctionInstance(Engine, "ceil", Ceil), true, false, true);
+            FastAddProperty("cos", new ClrFunctionInstance(Engine, "cos", Cos), true, false, true);
+            FastAddProperty("exp", new ClrFunctionInstance(Engine, "exp", Exp), true, false, true);
+            FastAddProperty("floor", new ClrFunctionInstance(Engine, "floor", Floor), true, false, true);
+            FastAddProperty("log", new ClrFunctionInstance(Engine, "log", Log), true, false, true);
+            FastAddProperty("max", new ClrFunctionInstance(Engine, "max", Max, 2), true, false, true);
+            FastAddProperty("min", new ClrFunctionInstance(Engine, "min", Min, 2), true, false, true);
+            FastAddProperty("pow", new ClrFunctionInstance(Engine, "pow", Pow, 2), true, false, true);
+            FastAddProperty("random", new ClrFunctionInstance(Engine, "random", Random), true, false, true);
+            FastAddProperty("round", new ClrFunctionInstance(Engine, "round", Round), true, false, true);
+            FastAddProperty("sin", new ClrFunctionInstance(Engine, "sin", Sin), true, false, true);
+            FastAddProperty("sqrt", new ClrFunctionInstance(Engine, "sqrt", Sqrt), true, false, true);
+            FastAddProperty("tan", new ClrFunctionInstance(Engine, "tan", Tan), true, false, true);
+
+            FastAddProperty("trunc", new ClrFunctionInstance(Engine, "trunc", Truncate, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("sign", new ClrFunctionInstance(Engine, "sign", Sign, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("cbrt", new ClrFunctionInstance(Engine, "cbrt", Cbrt, 1, PropertyFlag.Configurable), true, false, true);
 
             FastAddProperty("E", System.Math.E, false, false, false);
             FastAddProperty("LN10", System.Math.Log(10), false, false, false);
@@ -150,7 +151,7 @@ namespace Jint.Native.Math
             {
                 return System.Math.PI/2;
             }
-            
+
             if (NumberInstance.IsPositiveZero(y))
             {
                 // If y is +0 and x>0, the result is +0.
@@ -170,7 +171,7 @@ namespace Jint.Native.Math
                 {
                     return System.Math.PI;
                 }
-                
+
                 // If y is +0 and x<0, the result is an implementation-dependent approximation to +π.
                 if (x < 0)
                 {
@@ -243,7 +244,7 @@ namespace Jint.Native.Math
                     return -System.Math.PI;
                 }
             }
-            
+
             // If y is +∞ and x is finite, the result is an implementation-dependent approximation to +π/2.
             if (double.IsPositiveInfinity(y) && !double.IsInfinity(x))
             {
@@ -261,25 +262,25 @@ namespace Jint.Native.Math
             {
                 return System.Math.PI/4;
             }
-            
+
             // If y is +∞ and x is −∞, the result is an implementation-dependent approximation to +3π/4.
             if (double.IsPositiveInfinity(y) && double.IsNegativeInfinity(x))
             {
                 return 3 * System.Math.PI / 4;
             }
-            
+
             // If y is −∞ and x is +∞, the result is an implementation-dependent approximation to −π/4.
             if (double.IsNegativeInfinity(y) && double.IsPositiveInfinity(x))
             {
                 return -System.Math.PI / 4;
             }
-            
+
             // If y is −∞ and x is −∞, the result is an implementation-dependent approximation to −3π/4.
             if (double.IsNegativeInfinity(y) && double.IsNegativeInfinity(x))
             {
                 return - 3 * System.Math.PI / 4;
             }
-            
+
             return System.Math.Atan2(y, x);
         }
 

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

@@ -8,7 +8,7 @@ namespace Jint.Native.Number
     public sealed class NumberConstructor : FunctionInstance, IConstructor
     {
         public NumberConstructor(Engine engine)
-            : base(engine, null, null, false)
+            : base(engine, "Number", null, null, false)
         {
 
         }

+ 8 - 8
Jint/Native/Number/NumberPrototype.cs

@@ -35,12 +35,12 @@ namespace Jint.Native.Number
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance(Engine, ToNumberString), true, false, true);
-            FastAddProperty("toLocaleString", new ClrFunctionInstance(Engine, ToLocaleString), true, false, true);
-            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, ValueOf), true, false, true);
-            FastAddProperty("toFixed", new ClrFunctionInstance(Engine, ToFixed, 1), true, false, true);
-            FastAddProperty("toExponential", new ClrFunctionInstance(Engine, ToExponential), true, false, true);
-            FastAddProperty("toPrecision", new ClrFunctionInstance(Engine, ToPrecision), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToNumberString), true, false, true);
+            FastAddProperty("toLocaleString", new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString), true, false, true);
+            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOf", ValueOf), true, false, true);
+            FastAddProperty("toFixed", new ClrFunctionInstance(Engine, "toFixed", ToFixed, 1), true, false, true);
+            FastAddProperty("toExponential", new ClrFunctionInstance(Engine, "toExponential", ToExponential), true, false, true);
+            FastAddProperty("toPrecision", new ClrFunctionInstance(Engine, "toPrecision", ToPrecision), true, false, true);
         }
 
         private JsValue ToLocaleString(JsValue thisObject, JsValue[] arguments)
@@ -174,8 +174,8 @@ namespace Jint.Native.Number
                 ExceptionHelper.ThrowTypeError(_engine);
             }
 
-            var radix = arguments.At(0).IsUndefined() 
-                ? 10 
+            var radix = arguments.At(0).IsUndefined()
+                ? 10
                 : (int) TypeConverter.ToInteger(arguments.At(0));
 
             if (radix < 2 || radix > 36)

+ 16 - 15
Jint/Native/Object/ObjectConstructor.cs

@@ -11,7 +11,8 @@ namespace Jint.Native.Object
 {
     public sealed class ObjectConstructor : FunctionInstance, IConstructor
     {
-        private ObjectConstructor(Engine engine) : base(engine, null, null, false)
+        private ObjectConstructor(Engine engine)
+            : base(engine, "Object", null, null, false)
         {
         }
 
@@ -32,19 +33,19 @@ namespace Jint.Native.Object
         {
             Prototype = Engine.Function.PrototypeObject;
 
-            FastAddProperty("getPrototypeOf", new ClrFunctionInstance(Engine, GetPrototypeOf, 1), true, false, true);
-            FastAddProperty("getOwnPropertyDescriptor", new ClrFunctionInstance(Engine, GetOwnPropertyDescriptor, 2), true, false, true);
-            FastAddProperty("getOwnPropertyNames", new ClrFunctionInstance(Engine, GetOwnPropertyNames, 1), true, false, true);
-            FastAddProperty("create", new ClrFunctionInstance(Engine, Create, 2), true, false, true);
-            FastAddProperty("defineProperty", new ClrFunctionInstance(Engine, DefineProperty, 3), true, false, true);
-            FastAddProperty("defineProperties", new ClrFunctionInstance(Engine, DefineProperties, 2), true, false, true);
-            FastAddProperty("seal", new ClrFunctionInstance(Engine, Seal, 1), true, false, true);
-            FastAddProperty("freeze", new ClrFunctionInstance(Engine, Freeze, 1), true, false, true);
-            FastAddProperty("preventExtensions", new ClrFunctionInstance(Engine, PreventExtensions, 1), true, false, true);
-            FastAddProperty("isSealed", new ClrFunctionInstance(Engine, IsSealed, 1), true, false, true);
-            FastAddProperty("isFrozen", new ClrFunctionInstance(Engine, IsFrozen, 1), true, false, true);
-            FastAddProperty("isExtensible", new ClrFunctionInstance(Engine, IsExtensible, 1), true, false, true);
-            FastAddProperty("keys", new ClrFunctionInstance(Engine, Keys, 1), true, false, true);
+            FastAddProperty("getPrototypeOf", new ClrFunctionInstance(Engine, "getPrototypeOf", GetPrototypeOf, 1), true, false, true);
+            FastAddProperty("getOwnPropertyDescriptor", new ClrFunctionInstance(Engine, "getOwnPropertyDescriptor", GetOwnPropertyDescriptor, 2), true, false, true);
+            FastAddProperty("getOwnPropertyNames", new ClrFunctionInstance(Engine, "getOwnPropertyNames", GetOwnPropertyNames, 1), true, false, true);
+            FastAddProperty("create", new ClrFunctionInstance(Engine, "create", Create, 2), true, false, true);
+            FastAddProperty("defineProperty", new ClrFunctionInstance(Engine, "defineProperty", DefineProperty, 3), true, false, true);
+            FastAddProperty("defineProperties", new ClrFunctionInstance(Engine, "defineProperties", DefineProperties, 2), true, false, true);
+            FastAddProperty("seal", new ClrFunctionInstance(Engine, "seal", Seal, 1), true, false, true);
+            FastAddProperty("freeze", new ClrFunctionInstance(Engine, "freeze", Freeze, 1), true, false, true);
+            FastAddProperty("preventExtensions", new ClrFunctionInstance(Engine, "preventExtensions", PreventExtensions, 1), true, false, true);
+            FastAddProperty("isSealed", new ClrFunctionInstance(Engine, "isSealed", IsSealed, 1), true, false, true);
+            FastAddProperty("isFrozen", new ClrFunctionInstance(Engine, "isFrozen", IsFrozen, 1), true, false, true);
+            FastAddProperty("isExtensible", new ClrFunctionInstance(Engine, "isExtensible", IsExtensible, 1), true, false, true);
+            FastAddProperty("keys", new ClrFunctionInstance(Engine, "keys", Keys, 1), true, false, true);
         }
 
         public ObjectPrototype PrototypeObject { get; private set; }
@@ -100,7 +101,7 @@ namespace Jint.Native.Object
 
             return obj;
         }
-        
+
         internal ObjectInstance Construct(int propertyCount)
         {
             var obj = new ObjectInstance(_engine)

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

@@ -20,14 +20,14 @@ namespace Jint.Native.Object
     {
         protected Dictionary<string, PropertyDescriptor> _intrinsicProperties;
         protected internal Dictionary<string, PropertyDescriptor> _properties;
-        
+
         private readonly string _class;
         protected readonly Engine _engine;
-        
+
         public ObjectInstance(Engine engine) : this(engine, "Object")
         {
         }
-        
+
         protected ObjectInstance(Engine engine, string objectClass) : base(Types.Object)
         {
             _engine = engine;
@@ -543,7 +543,7 @@ namespace Jint.Native.Object
             var currentGet = current.Get;
             var currentSet = current.Set;
             var currentValue = current.Value;
-            
+
             if ((current._flags & PropertyFlag.ConfigurableSet | PropertyFlag.EnumerableSet | PropertyFlag.WritableSet) == 0 &&
                 ReferenceEquals(currentGet, null) &&
                 ReferenceEquals(currentSet, null) &&
@@ -703,7 +703,7 @@ namespace Jint.Native.Object
             }
 
             if (mutable != null)
-            { 
+            {
                 // replace old with new type that supports get and set
                 FastSetProperty(propertyName, mutable);
             }
@@ -851,13 +851,13 @@ namespace Jint.Native.Object
 
             return this;
         }
-        
+
         /// <summary>
         /// Handles the generic find of (callback[, thisArg])
         /// </summary>
         internal virtual bool FindWithCallback(
-            JsValue[] arguments, 
-            out uint index, 
+            JsValue[] arguments,
+            out uint index,
             out JsValue value)
         {
             uint GetLength()
@@ -878,7 +878,7 @@ namespace Jint.Native.Object
                 // if getter is not undefined it must be ICallable
                 return TypeConverter.ToUint32(((ICallable) getter).Call(this, Arguments.Empty));
             }
-           
+
             bool TryGetValue(uint idx, out JsValue jsValue)
             {
                 var property = TypeConverter.ToString(idx);
@@ -887,21 +887,21 @@ namespace Jint.Native.Object
                 return kPresent;
             }
 
-            var len = GetLength();
-            if (len == 0)
+            if (GetLength() == 0)
             {
                 index = 0;
                 value = Undefined;
                 return false;
             }
-
+            
             var callbackfn = arguments.At(0);
             var thisArg = arguments.At(1);
             var callable = GetCallable(callbackfn);
 
             var args = _engine._jsValueArrayPool.RentArray(3);
             args[2] = this;
-            for (uint k = 0; k < len; k++)
+            var length = GetLength();
+            for (uint k = 0; k < length; k++)
             {
                 if (TryGetValue(k, out var kvalue))
                 {

+ 6 - 6
Jint/Native/Object/ObjectPrototype.cs

@@ -21,12 +21,12 @@ namespace Jint.Native.Object
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance(Engine, ToObjectString), true, false, true);
-            FastAddProperty("toLocaleString", new ClrFunctionInstance(Engine, ToLocaleString), true, false, true);
-            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, ValueOf), true, false, true);
-            FastAddProperty("hasOwnProperty", new ClrFunctionInstance(Engine, HasOwnProperty, 1), true, false, true);
-            FastAddProperty("isPrototypeOf", new ClrFunctionInstance(Engine, IsPrototypeOf, 1), true, false, true);
-            FastAddProperty("propertyIsEnumerable", new ClrFunctionInstance(Engine, PropertyIsEnumerable, 1), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToObjectString), true, false, true);
+            FastAddProperty("toLocaleString", new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString), true, false, true);
+            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOF", ValueOf), true, false, true);
+            FastAddProperty("hasOwnProperty", new ClrFunctionInstance(Engine, "hasOwnProperty", HasOwnProperty, 1), true, false, true);
+            FastAddProperty("isPrototypeOf", new ClrFunctionInstance(Engine, "isPrototypeOf", IsPrototypeOf, 1), true, false, true);
+            FastAddProperty("propertyIsEnumerable", new ClrFunctionInstance(Engine, "propertyIsEnumerable", PropertyIsEnumerable, 1), true, false, true);
         }
 
         private JsValue PropertyIsEnumerable(JsValue thisObject, JsValue[] arguments)

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

@@ -11,7 +11,7 @@ namespace Jint.Native.RegExp
     public sealed class RegExpConstructor : FunctionInstance, IConstructor
     {
         public RegExpConstructor(Engine engine)
-            : base(engine, null, null, false)
+            : base(engine, "RegExp", null, null, false)
         {
         }
 

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

@@ -25,9 +25,9 @@ namespace Jint.Native.RegExp
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance(Engine, ToRegExpString), true, false, true);
-            FastAddProperty("exec", new ClrFunctionInstance(Engine, Exec, 1), true, false, true);
-            FastAddProperty("test", new ClrFunctionInstance(Engine, Test, 1), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToRegExpString), true, false, true);
+            FastAddProperty("exec", new ClrFunctionInstance(Engine, "exec", Exec, 1), true, false, true);
+            FastAddProperty("test", new ClrFunctionInstance(Engine, "test", Test, 1), true, false, true);
 
             FastAddProperty("global", false, false, false, false);
             FastAddProperty("ignoreCase", false, false, false, false);

+ 145 - 0
Jint/Native/Set/SetConstructor.cs

@@ -0,0 +1,145 @@
+using Jint.Native.Array;
+using Jint.Native.Function;
+using Jint.Native.Object;
+using Jint.Native.Symbol;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.Set
+{
+    public sealed class SetConstructor : FunctionInstance, IConstructor
+    {
+        private SetConstructor(Engine engine, string name) :  base(engine, name, null, null, false)
+        {
+        }
+
+        public SetPrototype PrototypeObject { get; private set; }
+
+        public static SetConstructor CreateSetConstructor(Engine engine)
+        {
+            SetConstructor CreateSetConstructorTemplate(string name)
+            {
+                var ctr = new SetConstructor(engine, name);
+                ctr.Extensible = true;
+
+                // The value of the [[Prototype]] internal property of the Set constructor is the Function prototype object
+                ctr.Prototype = engine.Function.PrototypeObject;
+                ctr.PrototypeObject = SetPrototype.CreatePrototypeObject(engine, ctr);
+
+                ctr.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.Configurable));
+                return ctr;
+            }
+
+            var obj = CreateSetConstructorTemplate("Set");
+
+            // The initial value of Set.prototype is the Set prototype object
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
+
+            obj.SetOwnProperty(GlobalSymbolRegistry.Species._value,
+                new GetSetPropertyDescriptor(
+                    get: new ClrFunctionInstance(engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable),
+                    set: Undefined,
+                    PropertyFlag.Configurable));
+
+            return obj;
+        }
+
+        public void Configure()
+        {
+        }
+
+        private static JsValue Species(JsValue thisObject, JsValue[] arguments)
+        {
+            return thisObject;
+        }
+
+        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
+        {
+            if (thisObject.IsUndefined())
+            {
+                ExceptionHelper.ThrowTypeError(_engine, "Constructor Set requires 'new'");
+            }
+
+            return Construct(arguments);
+        }
+
+        public ObjectInstance Construct(JsValue[] arguments)
+        {
+            var instance = new SetInstance(Engine)
+            {
+                Prototype = PrototypeObject,
+                Extensible = true
+            };
+            if (arguments.Length > 0
+                && !arguments[0].IsUndefined()
+                && !arguments[0].IsNull())
+            {
+                var iterator = arguments.At(0).GetIterator();
+                if (iterator != null)
+                {
+                    var setterProperty = instance.GetProperty("add");
+
+                    ICallable adder = null;
+                    if (setterProperty == null
+                        || !setterProperty.TryGetValue(instance, out var setterValue)
+                        || (adder = setterValue as ICallable) == null)
+                    {
+                        ExceptionHelper.ThrowTypeError(_engine, "add must be callable");
+                        return null;
+                    }
+
+                    var args = _engine._jsValueArrayPool.RentArray(1);
+                    try
+                    {
+                        do
+                        {
+                            var item = iterator.Next();
+                            if (item.TryGetValue("done", out var done) && done.AsBoolean())
+                            {
+                                break;
+                            }
+
+                            if (!item.TryGetValue("value", out var currentValue))
+                            {
+                                break;
+                            }
+
+                            args[0] = ExtractValueFromIteratorInstance(currentValue);
+
+                            adder.Call(instance, args);
+                        } while (true);
+                    }
+                    catch
+                    {
+                        iterator.Return();
+                        throw;
+                    }
+                    finally
+                    {
+                        _engine._jsValueArrayPool.ReturnArray(args);
+                    }
+                }
+            }
+
+            return instance;
+        }
+
+        private static JsValue ExtractValueFromIteratorInstance(JsValue jsValue)
+        {
+            if (jsValue is ArrayInstance ai)
+            {
+                uint index = 0;
+                if (ai.GetLength() > 1)
+                {
+                    index = 1;
+                }
+                ai.TryGetValue(index, out var value);
+                return value;
+            }
+
+            return jsValue;
+        }
+    }
+}

+ 130 - 0
Jint/Native/Set/SetInstance.cs

@@ -0,0 +1,130 @@
+using System.Runtime.CompilerServices;
+using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.Set
+{
+    public class SetInstance : ObjectInstance
+    {
+        internal readonly OrderedSet<JsValue> _set;
+
+        public SetInstance(Engine engine)
+            : base(engine, objectClass: "Map")
+        {
+            _set = new OrderedSet<JsValue>();
+        }
+
+        /// Implementation from ObjectInstance official specs as the one
+        /// in ObjectInstance is optimized for the general case and wouldn't work
+        /// for arrays
+        public override void Put(string propertyName, JsValue value, bool throwOnError)
+        {
+            if (!CanPut(propertyName))
+            {
+                if (throwOnError)
+                {
+                    ExceptionHelper.ThrowTypeError(Engine);
+                }
+
+                return;
+            }
+
+            var ownDesc = GetOwnProperty(propertyName);
+
+            if (ownDesc.IsDataDescriptor())
+            {
+                var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
+                DefineOwnProperty(propertyName, valueDesc, throwOnError);
+                return;
+            }
+
+            // property is an accessor or inherited
+            var desc = GetProperty(propertyName);
+
+            if (desc.IsAccessorDescriptor())
+            {
+                var setter = desc.Set.TryCast<ICallable>();
+                setter.Call(this, new[] {value});
+            }
+            else
+            {
+                var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
+                DefineOwnProperty(propertyName, newDesc, throwOnError);
+            }
+        }
+
+        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        {
+            if (propertyName.Length == 4 && propertyName == "size")
+            {
+                return new PropertyDescriptor(_set.Count, PropertyFlag.None);
+            }
+
+            return base.GetOwnProperty(propertyName);
+        }
+
+        protected override bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
+        {
+            if (propertyName.Length == 4 && propertyName == "size")
+            {
+                descriptor = new PropertyDescriptor(_set.Count, PropertyFlag.None);
+                return true;
+            }
+
+            return base.TryGetProperty(propertyName, out descriptor);
+        }
+
+        public void Add(JsValue value)
+        {
+            _set.Add(value);
+        }
+
+        public void Clear()
+        {
+            _set.Clear();
+        }
+
+        public bool Has(JsValue key)
+        {
+            return _set.Contains(key);
+        }
+
+        public bool Delete(JsValue key)
+        {
+            return _set.Remove(key);
+        }
+
+        public void ForEach(ICallable callable, JsValue thisArg)
+        {
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = this;
+
+            for (var i = 0; i < _set._list.Count; i++)
+            {
+                var value = _set._list[i];
+                args[0] = value;
+                args[1] = value;
+                callable.Call(thisArg, args);
+            }
+
+            _engine._jsValueArrayPool.ReturnArray(args);
+        }
+
+        public ObjectInstance Entries()
+        {
+            return _engine.Iterator.ConstructEntryIterator(this);
+        }
+
+        public ObjectInstance Values()
+        {
+            return _engine.Iterator.Construct(_set._list);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal uint GetSize()
+        {
+            return (uint) _set.Count;
+        }
+    }
+}

+ 123 - 0
Jint/Native/Set/SetPrototype.cs

@@ -0,0 +1,123 @@
+using Jint.Native.Object;
+using Jint.Native.Symbol;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.Set
+{
+    /// <summary>
+    /// https://www.ecma-international.org/ecma-262/6.0/#sec-set-objects
+    /// </summary>
+    public sealed class SetPrototype : ObjectInstance
+    {
+        private SetPrototype(Engine engine) : base(engine)
+        {
+        }
+
+        public static SetPrototype CreatePrototypeObject(Engine engine, SetConstructor mapConstructor)
+        {
+            var obj = new SetPrototype(engine)
+            {
+                Extensible = true, 
+                Prototype = engine.Object.PrototypeObject
+            };
+
+            obj.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.Configurable));
+            obj.SetOwnProperty("constructor", new PropertyDescriptor(mapConstructor, PropertyFlag.NonEnumerable));
+
+            return obj;
+        }
+
+        public void Configure()
+        {
+            FastAddProperty(GlobalSymbolRegistry.Iterator._value, new ClrFunctionInstance(Engine, "iterator", Values, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty(GlobalSymbolRegistry.ToStringTag._value, "Set", false, false, true);
+
+            FastAddProperty("add", new ClrFunctionInstance(Engine, "add", Add, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("clear", new ClrFunctionInstance(Engine, "clear", Clear, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("delete", new ClrFunctionInstance(Engine, "delete", Delete, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("entries", new ClrFunctionInstance(Engine, "entries", Entries, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("forEach", new ClrFunctionInstance(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("has", new ClrFunctionInstance(Engine, "has", Has, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("keys", new ClrFunctionInstance(Engine, "keys", Values, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("values", new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), true, false, true);
+
+            AddProperty(
+                "size", 
+                new GetSetPropertyDescriptor(
+                    get: new ClrFunctionInstance(Engine, "get size", Size, 0, PropertyFlag.Configurable), 
+                    set: null,
+                    PropertyFlag.Configurable));
+        }
+        
+        private JsValue Size(JsValue thisObj, JsValue[] arguments)
+        {
+            if (!thisObj.IsObject())
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+            return JsNumber.Create(0);
+        }
+
+        private JsValue Add(JsValue thisObj, JsValue[] arguments)
+        {
+            ((SetInstance) thisObj).Add(arguments[0]);
+            return thisObj;
+        }
+
+        private JsValue Clear(JsValue thisObj, JsValue[] arguments)
+        {
+            var map = thisObj as SetInstance
+                      ?? ExceptionHelper.ThrowTypeError<SetInstance>(_engine, "object must be a Set");
+
+            map.Clear();
+            return Undefined;
+        }
+
+        private JsValue Delete(JsValue thisObj, JsValue[] arguments)
+        {
+            var map = thisObj as SetInstance
+                      ?? ExceptionHelper.ThrowTypeError<SetInstance>(_engine, "object must be a Set");
+
+            return map.Delete(arguments[0])
+                ? JsBoolean.True
+                : JsBoolean.False;
+        }
+
+        private JsValue Has(JsValue thisObj, JsValue[] arguments)
+        {
+            return ((SetInstance) thisObj).Has(arguments[0])
+                ? JsBoolean.True
+                : JsBoolean.False;
+        }
+
+        private JsValue Entries(JsValue thisObj, JsValue[] arguments)
+        {
+            var map = thisObj as SetInstance
+                      ?? ExceptionHelper.ThrowTypeError<SetInstance>(_engine, "object must be a Set");
+
+            return map.Entries();
+        }
+
+        private JsValue ForEach(JsValue thisObj, JsValue[] arguments)
+        {
+            var callbackfn = arguments.At(0);
+            var thisArg = arguments.At(1);
+
+            var map = (SetInstance) thisObj;
+
+            var callable = GetCallable(callbackfn);
+
+            map.ForEach(callable, thisArg);
+
+            return Undefined;
+        }
+
+        private ObjectInstance Values(JsValue thisObj, JsValue[] arguments)
+        {
+            return ((SetInstance) thisObj).Values();
+        }
+    }
+}

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

@@ -9,7 +9,7 @@ namespace Jint.Native.String
     public sealed class StringConstructor : FunctionInstance, IConstructor
     {
         public StringConstructor(Engine engine)
-            : base(engine, null, null, false)
+            : base(engine, "String", null, null, false)
         {
         }
 
@@ -32,7 +32,7 @@ namespace Jint.Native.String
 
         public void Configure()
         {
-            SetOwnProperty("fromCharCode", new PropertyDescriptor(new ClrFunctionInstance(Engine, FromCharCode, 1), PropertyFlag.NonEnumerable));
+            SetOwnProperty("fromCharCode", new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromCharCode", FromCharCode, 1), PropertyFlag.NonEnumerable));
         }
 
         private static JsValue FromCharCode(JsValue thisObj, JsValue[] arguments)

+ 31 - 31
Jint/Native/String/StringPrototype.cs

@@ -35,29 +35,29 @@ namespace Jint.Native.String
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance(Engine, ToStringString), true, false, true);
-            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, ValueOf), true, false, true);
-            FastAddProperty("charAt", new ClrFunctionInstance(Engine, CharAt, 1), true, false, true);
-            FastAddProperty("charCodeAt", new ClrFunctionInstance(Engine, CharCodeAt, 1), true, false, true);
-            FastAddProperty("concat", new ClrFunctionInstance(Engine, Concat, 1), true, false, true);
-            FastAddProperty("indexOf", new ClrFunctionInstance(Engine, IndexOf, 1), true, false, true);
-            FastAddProperty("startsWith", new ClrFunctionInstance(Engine, StartsWith, 1), true, false, true);
-            FastAddProperty("lastIndexOf", new ClrFunctionInstance(Engine, LastIndexOf, 1), true, false, true);
-            FastAddProperty("localeCompare", new ClrFunctionInstance(Engine, LocaleCompare, 1), true, false, true);
-            FastAddProperty("match", new ClrFunctionInstance(Engine, Match, 1), true, false, true);
-            FastAddProperty("replace", new ClrFunctionInstance(Engine, Replace, 2), true, false, true);
-            FastAddProperty("search", new ClrFunctionInstance(Engine, Search, 1), true, false, true);
-            FastAddProperty("slice", new ClrFunctionInstance(Engine, Slice, 2), true, false, true);
-            FastAddProperty("split", new ClrFunctionInstance(Engine, Split, 2), true, false, true);
-            FastAddProperty("substr", new ClrFunctionInstance(Engine, Substr, 2), true, false, true);
-            FastAddProperty("substring", new ClrFunctionInstance(Engine, Substring, 2), true, false, true);
-            FastAddProperty("toLowerCase", new ClrFunctionInstance(Engine, ToLowerCase), true, false, true);
-            FastAddProperty("toLocaleLowerCase", new ClrFunctionInstance(Engine, ToLocaleLowerCase), true, false, true);
-            FastAddProperty("toUpperCase", new ClrFunctionInstance(Engine, ToUpperCase), true, false, true);
-            FastAddProperty("toLocaleUpperCase", new ClrFunctionInstance(Engine, ToLocaleUpperCase), true, false, true);
-            FastAddProperty("trim", new ClrFunctionInstance(Engine, Trim), true, false, true);
-            FastAddProperty("padStart", new ClrFunctionInstance(Engine, PadStart), true, false, true);
-            FastAddProperty("padEnd", new ClrFunctionInstance(Engine, PadEnd), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToStringString), true, false, true);
+            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOF", ValueOf), true, false, true);
+            FastAddProperty("charAt", new ClrFunctionInstance(Engine, "charAt", CharAt, 1), true, false, true);
+            FastAddProperty("charCodeAt", new ClrFunctionInstance(Engine, "charCodeAt", CharCodeAt, 1), true, false, true);
+            FastAddProperty("concat", new ClrFunctionInstance(Engine, "concat", Concat, 1), true, false, true);
+            FastAddProperty("indexOf", new ClrFunctionInstance(Engine, "indexOf", IndexOf, 1), true, false, true);
+            FastAddProperty("startsWith", new ClrFunctionInstance(Engine, "startsWith", StartsWith, 1), true, false, true);
+            FastAddProperty("lastIndexOf", new ClrFunctionInstance(Engine, "lastIndexOf", LastIndexOf, 1), true, false, true);
+            FastAddProperty("localeCompare", new ClrFunctionInstance(Engine, "localeCompare", LocaleCompare, 1), true, false, true);
+            FastAddProperty("match", new ClrFunctionInstance(Engine, "match", Match, 1), true, false, true);
+            FastAddProperty("replace", new ClrFunctionInstance(Engine, "replace", Replace, 2), true, false, true);
+            FastAddProperty("search", new ClrFunctionInstance(Engine, "search", Search, 1), true, false, true);
+            FastAddProperty("slice", new ClrFunctionInstance(Engine, "slice", Slice, 2), true, false, true);
+            FastAddProperty("split", new ClrFunctionInstance(Engine, "split", Split, 2), true, false, true);
+            FastAddProperty("substr", new ClrFunctionInstance(Engine, "substr", Substr, 2), true, false, true);
+            FastAddProperty("substring", new ClrFunctionInstance(Engine, "substring", Substring, 2), true, false, true);
+            FastAddProperty("toLowerCase", new ClrFunctionInstance(Engine, "toLowerCase", ToLowerCase), true, false, true);
+            FastAddProperty("toLocaleLowerCase", new ClrFunctionInstance(Engine, "toLocaleLowerCase", ToLocaleLowerCase), true, false, true);
+            FastAddProperty("toUpperCase", new ClrFunctionInstance(Engine, "toUpperCase", ToUpperCase), true, false, true);
+            FastAddProperty("toLocaleUpperCase", new ClrFunctionInstance(Engine, "toLocaleUpperCase", ToLocaleUpperCase), true, false, true);
+            FastAddProperty("trim", new ClrFunctionInstance(Engine, "trim", Trim), true, false, true);
+            FastAddProperty("padStart", new ClrFunctionInstance(Engine, "padStart", PadStart), true, false, true);
+            FastAddProperty("padEnd", new ClrFunctionInstance(Engine, "padEnd", PadEnd), true, false, true);
         }
 
         private JsValue ToStringString(JsValue thisObj, JsValue[] arguments)
@@ -77,13 +77,13 @@ namespace Jint.Native.String
         const char MONGOLIAN_VOWEL_SEPARATOR = '\u180E';
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static bool IsWhiteSpaceEx(char c)
+        internal static bool IsWhiteSpaceEx(char c, bool acceptMongolianVowelSeparator = true)
         {
             return
                 char.IsWhiteSpace(c) ||
                 c == BOM_CHAR ||
                 // In .NET 4.6 this was removed from WS based on Unicode 6.3 changes
-                c == MONGOLIAN_VOWEL_SEPARATOR;
+                (acceptMongolianVowelSeparator && c == MONGOLIAN_VOWEL_SEPARATOR);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -113,12 +113,12 @@ namespace Jint.Native.String
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static string TrimStartEx(string s)
+        public static string TrimStartEx(string s, bool acceptMongolianVowelSeparator = true)
         {
             if (s.Length == 0)
                 return string.Empty;
 
-            if (!IsWhiteSpaceEx(s[0]))
+            if (!IsWhiteSpaceEx(s[0], acceptMongolianVowelSeparator))
                 return s;
 
             return TrimStart(s);
@@ -139,9 +139,9 @@ namespace Jint.Native.String
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static string TrimEx(string s)
+        public static string TrimEx(string s, bool acceptMongolianVowelSeparator = true)
         {
-            return TrimEndEx(TrimStartEx(s));
+            return TrimEndEx(TrimStartEx(s, acceptMongolianVowelSeparator));
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -470,7 +470,7 @@ namespace Jint.Native.String
             var replaceFunction = replaceValue.TryCast<FunctionInstance>();
             if (ReferenceEquals(replaceFunction, null))
             {
-                replaceFunction = new ClrFunctionInstance(Engine, (self, args) =>
+                replaceFunction = new ClrFunctionInstance(Engine, "anonymous", (self, args) =>
                 {
                     var replaceString = TypeConverter.ToString(replaceValue);
                     var matchValue = TypeConverter.ToString(args.At(0));
@@ -603,7 +603,7 @@ namespace Jint.Native.String
                 args[2] = thisString;
 
                 var replaceString = TypeConverter.ToString(replaceFunction.Call(Undefined, args));
-                
+
                 _engine._jsValueArrayPool.ReturnArray(args);
 
                 // Replace only the first match.

+ 7 - 6
Jint/Native/Symbol/SymbolConstructor.cs

@@ -13,7 +13,7 @@ namespace Jint.Native.Symbol
     public sealed class SymbolConstructor : FunctionInstance, IConstructor
     {
         public SymbolConstructor(Engine engine)
-            : base(engine, null, null, false)
+            : base(engine, "Symbol", null, null, false)
         {
         }
 
@@ -32,13 +32,15 @@ namespace Jint.Native.Symbol
             obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
 
 
+            obj.SetOwnProperty("species", new PropertyDescriptor(GlobalSymbolRegistry.Species, PropertyFlag.AllForbidden));
+
             return obj;
         }
 
         public void Configure()
         {
-            FastAddProperty("for", new ClrFunctionInstance(Engine, For, 1), true, false, true);
-            FastAddProperty("keyFor", new ClrFunctionInstance(Engine, KeyFor, 1), true, false, true);
+            FastAddProperty("for", new ClrFunctionInstance(Engine, "for", For, 1), true, false, true);
+            FastAddProperty("keyFor", new ClrFunctionInstance(Engine, "keyFor", KeyFor, 1), true, false, true);
 
             FastAddProperty("hasInstance", GlobalSymbolRegistry.HasInstance, false, false, false);
             FastAddProperty("isConcatSpreadable", GlobalSymbolRegistry.IsConcatSpreadable, false, false, false);
@@ -64,13 +66,12 @@ namespace Jint.Native.Symbol
                 ? Undefined
                 : TypeConverter.ToString(description);
 
-            var value = new JsSymbol(description.AsString());
-
             if (ReturnOnAbruptCompletion(ref descString))
             {
                 return descString;
             }
 
+            var value = new JsSymbol(TypeConverter.ToString(description));
             return value;
         }
 
@@ -123,7 +124,7 @@ namespace Jint.Native.Symbol
             var instance = new SymbolInstance(Engine)
             {
                 Prototype = PrototypeObject,
-                SymbolData = symbol, 
+                SymbolData = symbol,
                 Extensible = true
             };
 

+ 3 - 3
Jint/Native/Symbol/SymbolPrototype.cs

@@ -28,11 +28,11 @@ namespace Jint.Native.Symbol
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance(Engine, ToSymbolString), true, false, true);
-            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, ValueOf), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToSymbolString), true, false, true);
+            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOf", ValueOf), true, false, true);
             FastAddProperty("toStringTag", new JsString("Symbol"), false, false, true);
 
-            SetIntrinsicValue(GlobalSymbolRegistry.ToPrimitive, new ClrFunctionInstance(Engine, ToPrimitive), false, false, true);
+            SetIntrinsicValue(GlobalSymbolRegistry.ToPrimitive, new ClrFunctionInstance(Engine, "toPrimitive", ToPrimitive), false, false, true);
             SetIntrinsicValue(GlobalSymbolRegistry.ToStringTag, new JsString("Symbol"), false, false, true);
         }
 

+ 13 - 13
Jint/Runtime/Descriptors/PropertyDescriptor.cs

@@ -16,7 +16,7 @@ namespace Jint.Runtime.Descriptors
         {
             _flags = flags;
         }
-        
+
         protected internal PropertyDescriptor(JsValue value, PropertyFlag flags) : this(flags)
         {
             if ((_flags & PropertyFlag.CustomJsValue) != 0)
@@ -32,7 +32,7 @@ namespace Jint.Runtime.Descriptors
             {
                 CustomValue = value;
             }
-            _value = value;  
+            _value = value;
 
             if (writable != null)
             {
@@ -59,7 +59,7 @@ namespace Jint.Runtime.Descriptors
 
             Enumerable = descriptor.Enumerable;
             EnumerableSet = descriptor.EnumerableSet;
-            
+
             Configurable = descriptor.Configurable;
             ConfigurableSet = descriptor.ConfigurableSet;
 
@@ -88,7 +88,7 @@ namespace Jint.Runtime.Descriptors
                 }
             }
         }
-                
+
         public bool EnumerableSet
         {
             [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -162,7 +162,7 @@ namespace Jint.Runtime.Descriptors
                 }
             }
         }
-        
+
         public bool ConfigurableSet
         {
             [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -190,7 +190,7 @@ namespace Jint.Runtime.Descriptors
                 {
                     return CustomValue;
                 }
-                
+
                 return _value;
             }
             [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -228,7 +228,7 @@ namespace Jint.Runtime.Descriptors
             var hasGetProperty = getProperty != Undefined;
             var setProperty = obj.GetProperty("set");
             var hasSetProperty = setProperty != Undefined;
-            
+
             if ((obj.HasProperty("value") || obj.HasProperty("writable")) &&
                 (hasGetProperty || hasSetProperty))
             {
@@ -305,7 +305,7 @@ namespace Jint.Runtime.Descriptors
             {
                 return Native.Undefined.Instance;
             }
-            
+
             var obj = engine.Object.Construct(Arguments.Empty);
 
             if (desc.IsDataDescriptor())
@@ -324,7 +324,7 @@ namespace Jint.Runtime.Descriptors
 
             return obj;
         }
-        
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsAccessorDescriptor()
         {
@@ -334,7 +334,7 @@ namespace Jint.Runtime.Descriptors
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsDataDescriptor()
         {
-            return (_flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != 0 
+            return (_flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != 0
                    || (_flags & PropertyFlag.CustomJsValue) != 0 && !ReferenceEquals(CustomValue, null)
                    || !ReferenceEquals(_value, null);
         }
@@ -360,19 +360,19 @@ namespace Jint.Runtime.Descriptors
                 var val = (_flags & PropertyFlag.CustomJsValue) != 0
                     ? CustomValue
                     : _value;
-                
+
                 if (!ReferenceEquals(val, null))
                 {
                     value = val;
                     return true;
                 }
             }
-            
+
             if (this == Undefined)
             {
                 return false;
             }
-            
+
             var getter = Get;
             if (!ReferenceEquals(getter, null) && !getter.IsUndefined())
             {

+ 5 - 0
Jint/Runtime/ExceptionHelper.cs

@@ -102,6 +102,11 @@ namespace Jint.Runtime
             throw new ArgumentNullException(paramName);
         }
 
+        public static T ThrowArgumentNullException<T>(string paramName)
+        {
+            throw new ArgumentNullException(paramName);
+        }
+
         public static void ThrowError(Engine engine, string message)
         {
             throw new JavaScriptException(engine.Error, message);

+ 44 - 8
Jint/Runtime/Interop/ClrFunctionInstance.cs

@@ -8,22 +8,23 @@ namespace Jint.Runtime.Interop
     /// <summary>
     /// Wraps a Clr method into a FunctionInstance
     /// </summary>
-    public sealed class ClrFunctionInstance : FunctionInstance
+    public sealed class ClrFunctionInstance : FunctionInstance, IEquatable<ClrFunctionInstance>
     {
         private readonly Func<JsValue, JsValue[], JsValue> _func;
 
-        public ClrFunctionInstance(Engine engine, Func<JsValue, JsValue[], JsValue> func, int length)
-            : base(engine, null, null, false)
+        public ClrFunctionInstance(
+            Engine engine,
+            string name,
+            Func<JsValue, JsValue[], JsValue> func,
+            int length = 0,
+            PropertyFlag lengthFlags = PropertyFlag.AllForbidden) : base(engine, name, null, null, false)
         {
             _func = func;
+
             Prototype = engine.Function.PrototypeObject;
             Extensible = true;
-            _length = new PropertyDescriptor(length, PropertyFlag.AllForbidden);
-        }
 
-        public ClrFunctionInstance(Engine engine, Func<JsValue, JsValue[], JsValue> func)
-            : this(engine, func, 0)
-        {
+            _length = new PropertyDescriptor(length, lengthFlags);
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
@@ -39,5 +40,40 @@ namespace Jint.Runtime.Interop
                 return null;
             }
         }
+        
+        public override bool Equals(JsValue obj)
+        {
+            if (ReferenceEquals(null, obj))
+            {
+                return false;
+            }
+
+            if (!(obj is ClrFunctionInstance s))
+            {
+                return false;
+            }
+
+            return Equals(s);
+        }
+
+        public bool Equals(ClrFunctionInstance other)
+        {
+            if (ReferenceEquals(null, other))
+            {
+                return false;
+            }
+
+            if (ReferenceEquals(this, other))
+            {
+                return true;
+            }
+
+            if (_func == other._func)
+            {
+                return true;
+            }
+            
+            return false;
+        }
     }
 }

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

@@ -15,7 +15,8 @@ namespace Jint.Runtime.Interop
         private readonly Delegate _d;
         private readonly bool _delegateContainsParamsArgument;
 
-        public DelegateWrapper(Engine engine, Delegate d) : base(engine, null, null, false)
+        public DelegateWrapper(Engine engine, Delegate d)
+            : base(engine, "delegate", null, null, false)
         {
             _d = d;
             Prototype = engine.Function.PrototypeObject;

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

@@ -12,7 +12,7 @@ namespace Jint.Runtime.Interop
         private readonly Func<JsValue, JsValue> _getter;
 
         public GetterFunctionInstance(Engine engine, Func<JsValue, JsValue> getter)
-            : base(engine,  null, null, false)
+            : base(engine, "get", null, null, false)
         {
             _getter = getter;
         }

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

@@ -12,7 +12,7 @@ namespace Jint.Runtime.Interop
         private readonly MethodInfo[] _methods;
 
         public MethodInfoFunctionInstance(Engine engine, MethodInfo[] methods)
-            : base(engine, null, null, false)
+            : base(engine, "Function", null, null, false)
         {
             _methods = methods;
             Prototype = engine.Function.PrototypeObject;

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

@@ -12,7 +12,7 @@ namespace Jint.Runtime.Interop
         private readonly Action<JsValue, JsValue> _setter;
 
         public SetterFunctionInstance(Engine engine, Action<JsValue, JsValue> setter)
-            : base(engine, null, null, false)
+            : base(engine, "set", null, null, false)
         {
             _setter = setter;
         }

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

@@ -13,7 +13,7 @@ namespace Jint.Runtime.Interop
     public sealed class TypeReference : FunctionInstance, IConstructor, IObjectWrapper
     {
         private TypeReference(Engine engine)
-            : base(engine, null, null, false, "TypeReference")
+            : base(engine, "typereference", null, null, false, "TypeReference")
         {
         }
 

+ 590 - 0
Jint/Runtime/OrderedDictionary.cs

@@ -0,0 +1,590 @@
+// based on https://github.com/jehugaleahsa/truncon.collections.OrderedDictionary
+// https://github.com/jehugaleahsa/truncon.collections.OrderedDictionary/blob/master/UNLICENSE.txt
+
+
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Globalization;
+
+namespace Jint.Runtime
+{
+    /// <summary>
+    /// Represents a dictionary that tracks the order that items were added.
+    /// </summary>
+    /// <typeparam name="TKey">The type of the dictionary keys.</typeparam>
+    /// <typeparam name="TValue">The type of the dictionary values.</typeparam>
+    /// <remarks>
+    /// This dictionary makes it possible to get the index of a key and a key based on an index.
+    /// It can be costly to find the index of a key because it must be searched for linearly.
+    /// It can be costly to insert a key/value pair because other key's indexes must be adjusted.
+    /// It can be costly to remove a key/value pair because other keys' indexes must be adjusted.
+    /// </remarks>
+    [DebuggerDisplay("Count = {Count}")]
+    internal sealed class OrderedDictionary<TKey, TValue>
+        : IDictionary<TKey, TValue>, IList<KeyValuePair<TKey, TValue>> where TKey : class where TValue : class
+    {
+        private readonly Dictionary<TKey, TValue> dictionary;
+        private readonly List<TKey> keys;
+
+        private const string ArrayTooSmall = "The given array was too small to hold the items.";
+        private const string EditReadOnlyList = "An attempt was made to edit a read-only list.";
+        private const string IndexOutOfRange = "The index is negative or outside the bounds of the collection.";
+        private const string TooSmall = "The given value cannot be less than {0}.";
+
+        /// <summary>
+        /// Initializes a new instance of an OrderedDictionary.
+        /// </summary>
+        public OrderedDictionary()
+        {
+            dictionary = new Dictionary<TKey, TValue>();
+            keys = new List<TKey>();
+        }
+
+        /// <summary>
+        /// Initializes a new instance of an OrderedDictionary.
+        /// </summary>
+        /// <param name="capacity">The initial capacity of the dictionary.</param>
+        /// <exception cref="System.ArgumentOutOfRangeException">The capacity is less than zero.</exception>
+        public OrderedDictionary(int capacity)
+        {
+            dictionary = new Dictionary<TKey, TValue>(capacity);
+            keys = new List<TKey>(capacity);
+        }
+
+        /// <summary>
+        /// Initializes a new instance of an OrderedDictionary.
+        /// </summary>
+        /// <param name="comparer">The equality comparer to use to compare keys.</param>
+        public OrderedDictionary(IEqualityComparer<TKey> comparer)
+        {
+            dictionary = new Dictionary<TKey, TValue>(comparer);
+            keys = new List<TKey>();
+        }
+
+        /// <summary>
+        /// Initializes a new instance of an OrderedDictionary.
+        /// </summary>
+        /// <param name="capacity">The initial capacity of the dictionary.</param>
+        /// <param name="comparer">The equality comparer to use to compare keys.</param>
+        public OrderedDictionary(int capacity, IEqualityComparer<TKey> comparer)
+        {
+            dictionary = new Dictionary<TKey, TValue>(capacity, comparer);
+            keys = new List<TKey>(capacity);
+        }
+
+        /// <summary>
+        /// Adds the given key/value pair to the dictionary.
+        /// </summary>
+        /// <param name="key">The key to add to the dictionary.</param>
+        /// <param name="value">The value to associated with the key.</param>
+        /// <exception cref="System.ArgumentException">The given key already exists in the dictionary.</exception>
+        /// <exception cref="System.ArgumentNullException">The key is null.</exception>
+        public void Add(TKey key, TValue value)
+        {
+            dictionary.Add(key, value);
+            keys.Add(key);
+        }
+
+        /// <summary>
+        /// Inserts the given key/value pair at the specified index.
+        /// </summary>
+        /// <param name="index">The index to insert the key/value pair.</param>
+        /// <param name="key">The key to insert.</param>
+        /// <param name="value">The value to insert.</param>
+        /// <exception cref="System.ArgumentException">The given key already exists in the dictionary.</exception>
+        /// <exception cref="System.ArgumentNullException">The key is null.</exception>
+        /// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- larger than the size of the dictionary.</exception>
+        public void Insert(int index, TKey key, TValue value)
+        {
+            if (index < 0 || index > dictionary.Count)
+            {
+                ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(index), IndexOutOfRange);
+            }
+            dictionary.Add(key, value);
+            keys.Insert(index, key);
+        }
+
+        /// <summary>
+        /// Determines whether the given key exists in the dictionary.
+        /// </summary>
+        /// <param name="key">The key to look for.</param>
+        /// <returns>True if the key exists in the dictionary; otherwise, false.</returns>
+        /// <exception cref="System.ArgumentNullException">The key is null.</exception>
+        public bool ContainsKey(TKey key)
+        {
+            return dictionary.ContainsKey(key);
+        }
+
+        /// <summary>
+        /// Gets the key at the given index.
+        /// </summary>
+        /// <param name="index">The index of the key to get.</param>
+        /// <returns>The key at the given index.</returns>
+        /// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- larger than the number of keys.</exception>
+        public TKey GetKey(int index)
+        {
+            return keys[index];
+        }
+
+        /// <summary>
+        /// Gets the index of the given key.
+        /// </summary>
+        /// <param name="key">The key to get the index of.</param>
+        /// <returns>The index of the key in the dictionary -or- -1 if the key is not found.</returns>
+        /// <remarks>The operation runs in O(n).</remarks>
+        public int IndexOf(TKey key)
+        {
+            if (!dictionary.ContainsKey(key))
+            {
+                return -1;
+            }
+
+            var keysCount = keys.Count;
+            for (int i = 0; i < keysCount; ++i)
+            {
+                if (dictionary.Comparer.Equals(keys[i], key))
+                {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        /// <summary>
+        /// Gets the keys in the dictionary in the order they were added.
+        /// </summary>
+        public KeyCollection Keys => new KeyCollection(this);
+
+        /// <summary>
+        /// Removes the key/value pair with the given key from the dictionary.
+        /// </summary>
+        /// <param name="key">The key of the pair to remove.</param>
+        /// <returns>True if the key was found and the pair removed; otherwise, false.</returns>
+        /// <exception cref="System.ArgumentNullException">The key is null.</exception>
+        /// <remarks>This operation runs in O(n).</remarks>
+        public bool Remove(TKey key)
+        {
+            if (dictionary.Remove(key))
+            {
+                var keysCount = keys.Count;
+                for (int i = 0; i < keysCount; ++i)
+                {
+                    if (dictionary.Comparer.Equals(keys[i], key))
+                    {
+                        keys.RemoveAt(i);
+                        break;
+                    }
+                }
+                return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Removes the key/value pair at the given index.
+        /// </summary>
+        /// <param name="index">The index of the key/value pair to remove.</param>
+        /// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- larger than the size of the dictionary.</exception>
+        /// <remarks>This operation runs in O(n).</remarks>
+        public void RemoveAt(int index)
+        {
+            TKey key = keys[index];
+            dictionary.Remove(key);
+            keys.RemoveAt(index);
+        }
+
+        /// <summary>
+        /// Tries to get the value associated with the given key. If the key is not found,
+        /// default(TValue) value is stored in the value.
+        /// </summary>
+        /// <param name="key">The key to get the value for.</param>
+        /// <param name="value">The value used to hold the results.</param>
+        /// <returns>True if the key was found; otherwise, false.</returns>
+        /// <exception cref="System.ArgumentNullException">The key is null.</exception>
+        public bool TryGetValue(TKey key, out TValue value)
+        {
+            return dictionary.TryGetValue(key, out value);
+        }
+
+        /// <summary>
+        /// Gets the values in the dictionary.
+        /// </summary>
+        public ValueCollection Values => new ValueCollection(this);
+
+        /// <summary>
+        /// Gets or sets the value at the given index.
+        /// </summary>
+        /// <param name="index">The index of the value to get.</param>
+        /// <returns>The value at the given index.</returns>
+        /// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- beyond the length of the dictionary.</exception>
+        public TValue this[int index]
+        {
+            get => dictionary[keys[index]];
+            set => dictionary[keys[index]] = value;
+        }
+
+        /// <summary>
+        /// Gets or sets the value associated with the given key.
+        /// </summary>
+        /// <param name="key">The key to get the associated value by or to associate with the value.</param>
+        /// <returns>The value associated with the given key.</returns>
+        /// <exception cref="System.ArgumentNullException">The key is null.</exception>
+        /// <exception cref="System.Collections.Generic.KeyNotFoundException">The key is not in the dictionary.</exception>
+        public TValue this[TKey key]
+        {
+            get => dictionary[key];
+            set
+            {
+                if (!dictionary.ContainsKey(key))
+                {
+                    keys.Add(key);
+                }
+                dictionary[key] = value;
+            }
+        }
+
+        /// <summary>
+        /// Removes all key/value pairs from the dictionary.
+        /// </summary>
+        public void Clear()
+        {
+            dictionary.Clear();
+            keys.Clear();
+        }
+
+        /// <summary>
+        /// Gets the number of key/value pairs in the dictionary.
+        /// </summary>
+        public int Count => dictionary.Count;
+
+        /// <summary>
+        /// Gets the key/value pairs in the dictionary in the order they were added.
+        /// </summary>
+        /// <returns>An enumerator over the key/value pairs in the dictionary.</returns>
+        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+        {
+            foreach (TKey key in keys)
+            {
+                yield return new KeyValuePair<TKey, TValue>(key, dictionary[key]);
+            }
+        }
+
+        int IList<KeyValuePair<TKey, TValue>>.IndexOf(KeyValuePair<TKey, TValue> item)
+        {
+            if (!dictionary.TryGetValue(item.Key, out var value))
+            {
+                return -1;
+            }
+            if (!Equals(item.Value, value))
+            {
+                return -1;
+            }
+
+            var keysCount = keys.Count;
+            for (int i = 0; i < keysCount; ++i)
+            {
+                if (dictionary.Comparer.Equals(keys[i], item.Key))
+                {
+                    return i;
+                }
+            }
+
+            return -1;
+        }
+
+        void IList<KeyValuePair<TKey, TValue>>.Insert(int index, KeyValuePair<TKey, TValue> item)
+        {
+            if (index < 0 || index > dictionary.Count)
+            {
+                ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(index), IndexOutOfRange);
+            }
+            dictionary.Add(item.Key, item.Value);
+            keys.Insert(index, item.Key);
+        }
+
+        KeyValuePair<TKey, TValue> IList<KeyValuePair<TKey, TValue>>.this[int index]
+        {
+            get
+            {
+                TKey key = keys[index];
+                TValue value = dictionary[key];
+                return new KeyValuePair<TKey, TValue>(key, value);
+            }
+            set
+            {
+                TKey key = keys[index];
+                if (dictionary.Comparer.Equals(key, value.Key))
+                {
+                    dictionary[value.Key] = value.Value;
+                }
+                else
+                {
+                    dictionary.Add(value.Key, value.Value);
+                    dictionary.Remove(key);
+                    keys[index] = value.Key;
+                }
+            }
+        }
+
+        ICollection<TKey> IDictionary<TKey, TValue>.Keys => Keys;
+
+        ICollection<TValue> IDictionary<TKey, TValue>.Values => Values;
+
+        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
+        {
+            dictionary.Add(item.Key, item.Value);
+            keys.Add(item.Key);
+        }
+
+        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
+        {
+            if (!dictionary.TryGetValue(item.Key, out var value))
+            {
+                return false;
+            }
+            return Equals(value, item.Value);
+        }
+
+        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+        {
+            if (array == null)
+            {
+                ExceptionHelper.ThrowArgumentNullException(nameof(array));
+                return;
+            }
+            if (arrayIndex < 0)
+            {
+                ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(arrayIndex), string.Format(CultureInfo.CurrentCulture, TooSmall, 0));
+            }
+            if (dictionary.Count > array.Length - arrayIndex)
+            {
+                ExceptionHelper.ThrowArgumentException(ArrayTooSmall, nameof(array));
+            }
+            foreach (TKey key in keys)
+            {
+                TValue value = dictionary[key];
+                array[arrayIndex] = new KeyValuePair<TKey, TValue>(key, value);
+                ++arrayIndex;
+            }
+        }
+
+        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
+
+        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
+        {
+            if (!dictionary.TryGetValue(item.Key, out var value))
+            {
+                return false;
+            }
+            if (!Equals(item.Value, value))
+            {
+                return false;
+            }
+            // O(n)
+            dictionary.Remove(item.Key);
+
+            var keysCount = keys.Count;
+            for (int i = 0; i < keysCount; ++i)
+            {
+                if (dictionary.Comparer.Equals(keys[i], item.Key))
+                {
+                    keys.RemoveAt(i);
+                }
+            }
+            return true;
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        /// <summary>
+        /// Wraps the keys in an OrderDictionary.
+        /// </summary>
+        public sealed class KeyCollection : ICollection<TKey>
+        {
+            private readonly OrderedDictionary<TKey, TValue> parent;
+
+            /// <summary>
+            /// Initializes a new instance of a KeyCollection.
+            /// </summary>
+            /// <param name="dictionary">The OrderedDictionary whose keys to wrap.</param>
+            /// <exception cref="System.ArgumentNullException">The dictionary is null.</exception>
+            public KeyCollection(OrderedDictionary<TKey, TValue> dictionary)
+            {
+                parent = dictionary
+                         ?? ExceptionHelper.ThrowArgumentNullException<OrderedDictionary<TKey, TValue>>(nameof(dictionary));
+            }
+
+            /// <summary>
+            /// Copies the keys from the OrderedDictionary to the given array, starting at the given index.
+            /// </summary>
+            /// <param name="array">The array to copy the keys to.</param>
+            /// <param name="arrayIndex">The index into the array to start copying the keys.</param>
+            /// <exception cref="System.ArgumentNullException">The array is null.</exception>
+            /// <exception cref="System.ArgumentOutOfRangeException">The arrayIndex is negative.</exception>
+            /// <exception cref="System.ArgumentException">The array, starting at the given index, is not large enough to contain all the keys.</exception>
+            public void CopyTo(TKey[] array, int arrayIndex)
+            {
+                parent.keys.CopyTo(array, arrayIndex);
+            }
+
+            /// <summary>
+            /// Gets the number of keys in the OrderedDictionary.
+            /// </summary>
+            public int Count
+            {
+                get { return parent.dictionary.Count; }
+            }
+
+            /// <summary>
+            /// Gets an enumerator over the keys in the OrderedDictionary.
+            /// </summary>
+            /// <returns>The enumerator.</returns>
+            public IEnumerator<TKey> GetEnumerator()
+            {
+                return parent.keys.GetEnumerator();
+            }
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            bool ICollection<TKey>.Contains(TKey item)
+            {
+                return parent.dictionary.ContainsKey(item);
+            }
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            void ICollection<TKey>.Add(TKey item)
+            {
+                ExceptionHelper.ThrowNotSupportedException(EditReadOnlyList);
+            }
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            void ICollection<TKey>.Clear()
+            {
+                ExceptionHelper.ThrowNotSupportedException(EditReadOnlyList);
+            }
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            bool ICollection<TKey>.IsReadOnly
+            {
+                get { return true; }
+            }
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            bool ICollection<TKey>.Remove(TKey item)
+            {
+                ExceptionHelper.ThrowNotSupportedException(EditReadOnlyList);
+                return false;
+            }
+
+            IEnumerator IEnumerable.GetEnumerator()
+            {
+                return GetEnumerator();
+            }
+        }
+
+        /// <summary>
+        /// Wraps the keys in an OrderDictionary.
+        /// </summary>
+        public sealed class ValueCollection : ICollection<TValue>
+        {
+            private readonly OrderedDictionary<TKey, TValue> parent;
+
+            /// <summary>
+            /// Initializes a new instance of a ValueCollection.
+            /// </summary>
+            /// <param name="dictionary">The OrderedDictionary whose keys to wrap.</param>
+            /// <exception cref="System.ArgumentNullException">The dictionary is null.</exception>
+            public ValueCollection(OrderedDictionary<TKey, TValue> dictionary)
+            {
+                parent = dictionary
+                         ?? ExceptionHelper.ThrowArgumentNullException<OrderedDictionary<TKey, TValue>>(nameof(dictionary));
+            }
+
+            /// <summary>
+            /// Copies the values from the OrderedDictionary to the given array, starting at the given index.
+            /// </summary>
+            /// <param name="array">The array to copy the values to.</param>
+            /// <param name="arrayIndex">The index into the array to start copying the values.</param>
+            /// <exception cref="System.ArgumentNullException">The array is null.</exception>
+            /// <exception cref="System.ArgumentOutOfRangeException">The arrayIndex is negative.</exception>
+            /// <exception cref="System.ArgumentException">The array, starting at the given index, is not large enough to contain all the values.</exception>
+            public void CopyTo(TValue[] array, int arrayIndex)
+            {
+                if (array == null)
+                {
+                    ExceptionHelper.ThrowArgumentNullException(nameof(array));
+                    return;
+                }
+                if (arrayIndex < 0)
+                {
+                    ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(arrayIndex), string.Format(TooSmall, 0));
+                }
+                if (parent.dictionary.Count > array.Length - arrayIndex)
+                {
+                    ExceptionHelper.ThrowArgumentException(ArrayTooSmall, nameof(array));
+                }
+                foreach (TKey key in parent.keys)
+                {
+                    TValue value = parent.dictionary[key];
+                    array[arrayIndex] = value;
+                    ++arrayIndex;
+                }
+            }
+
+            /// <summary>
+            /// Gets the number of values in the OrderedDictionary.
+            /// </summary>
+            public int Count => parent.dictionary.Count;
+
+            /// <summary>
+            /// Gets an enumerator over the values in the OrderedDictionary.
+            /// </summary>
+            /// <returns>The enumerator.</returns>
+            public IEnumerator<TValue> GetEnumerator()
+            {
+                foreach (TKey key in parent.keys)
+                {
+                    TValue value = parent.dictionary[key];
+                    yield return value;
+                }
+            }
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            bool ICollection<TValue>.Contains(TValue item)
+            {
+                return parent.dictionary.ContainsValue(item);
+            }
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            void ICollection<TValue>.Add(TValue item)
+            {
+                ExceptionHelper.ThrowNotSupportedException(EditReadOnlyList);
+            }
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            void ICollection<TValue>.Clear()
+            {
+                ExceptionHelper.ThrowNotSupportedException(EditReadOnlyList);
+            }
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            bool ICollection<TValue>.IsReadOnly => true;
+
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            bool ICollection<TValue>.Remove(TValue item)
+            {
+                ExceptionHelper.ThrowNotSupportedException(EditReadOnlyList);
+                return false;
+            }
+
+            IEnumerator IEnumerable.GetEnumerator()
+            {
+                return GetEnumerator();
+            }
+        }
+    }
+}

+ 52 - 0
Jint/Runtime/OrderedSet.cs

@@ -0,0 +1,52 @@
+using System.Collections.Generic;
+
+namespace Jint.Runtime
+{
+    internal sealed class OrderedSet<T>
+    {
+        internal readonly List<T> _list;
+        private readonly HashSet<T> _set;
+
+        public OrderedSet()
+        {
+            _list = new List<T>();
+            _set = new HashSet<T>();
+        }
+
+        public T this[int index]
+        {
+            get => _list[index];
+            set
+            {
+                if (_set.Add(value))
+                {
+                    _list[index] = value;
+                }
+            }
+        }
+
+        public void Add(T item)
+        {
+            if (_set.Add(item))
+            {
+                _list.Add(item);
+            }
+        }
+
+        public void Clear()
+        {
+            _list.Clear();
+            _set.Clear();
+        }
+
+        public bool Contains(T item) => _set.Contains(item);
+
+        public int Count => _list.Count;
+
+        public bool Remove(T item)
+        {
+            _set.Remove(item);
+            return _list.Remove(item);
+        }
+    }
+}