Browse Source

Merge pull request #561 from sebastienros/dev

New release
Sébastien Ros 6 years ago
parent
commit
04359cc49a
94 changed files with 2991 additions and 1818 deletions
  1. 1 1
      Jint.Benchmark/Jint.Benchmark.csproj
  2. 4 4
      Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj
  3. 39 0
      Jint.Tests.Ecma/EcmaTest.cs
  4. 4 4
      Jint.Tests.Ecma/Jint.Tests.Ecma.csproj
  5. 1 1
      Jint.Tests.Ecma/SingleTest.cs
  6. 359 359
      Jint.Tests.Ecma/TestCases/alltests.json
  7. 1 1
      Jint.Tests.Test262/ArrayTests.cs
  8. 4 4
      Jint.Tests.Test262/Jint.Tests.Test262.csproj
  9. 14 2
      Jint.Tests.Test262/SingleTest.cs
  10. 23 0
      Jint.Tests.Test262/StringTests.cs
  11. 93 11
      Jint.Tests.Test262/Test262Test.cs
  12. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T2.js
  13. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T3.js
  14. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T5.js
  15. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T6.js
  16. 15 4
      Jint.Tests.Test262/test/built-ins/String/prototype/matchAll/regexp-prototype-has-no-matchAll.js
  17. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T1.js
  18. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T2.js
  19. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T3.js
  20. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T4.js
  21. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-line-terminator.js
  22. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-cannot-convert-to-primitive-err.js
  23. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-toprimitive-meth-priority.js
  24. 1 0
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-toprimitive-returns-object-err.js
  25. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-tostring-meth-priority.js
  26. 1 0
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-tostring-returns-object-err.js
  27. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-valueof-meth-priority.js
  28. 1 0
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-valueof-returns-object-err.js
  29. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-whitespace.js
  30. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-line-terminator.js
  31. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-cannot-convert-to-primitive-err.js
  32. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-toprimitive-meth-priority.js
  33. 1 0
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-toprimitive-returns-object-err.js
  34. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-tostring-meth-priority.js
  35. 1 0
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-tostring-returns-object-err.js
  36. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-valueof-meth-priority.js
  37. 1 0
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-valueof-returns-object-err.js
  38. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-whitespace.js
  39. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T1.js
  40. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T2.js
  41. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T3.js
  42. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T4.js
  43. 201 14
      Jint.Tests.Test262/test/skipped.json
  44. 4 4
      Jint.Tests/Jint.Tests.csproj
  45. 11 0
      Jint.Tests/Runtime/EngineTests.cs
  46. 1 0
      Jint.sln.DotSettings
  47. 459 0
      Jint/Collections/StringDictionarySlim.cs
  48. 10 2
      Jint/Directory.Build.props
  49. 3 4
      Jint/Engine.cs
  50. 43 0
      Jint/EsprimaExtensions.cs
  51. 1 1
      Jint/Jint.csproj
  52. 38 24
      Jint/Native/Argument/ArgumentsInstance.cs
  53. 120 6
      Jint/Native/Array/ArrayConstructor.cs
  54. 59 8
      Jint/Native/Array/ArrayInstance.cs
  55. 460 147
      Jint/Native/Array/ArrayPrototype.cs
  56. 0 7
      Jint/Native/Array/IArrayLike.cs
  57. 5 8
      Jint/Native/Boolean/BooleanPrototype.cs
  58. 6 6
      Jint/Native/Date/DatePrototype.cs
  59. 4 7
      Jint/Native/Function/EvalFunctionInstance.cs
  60. 3 3
      Jint/Native/Function/FunctionConstructor.cs
  61. 7 7
      Jint/Native/Function/FunctionPrototype.cs
  62. 5 7
      Jint/Native/Function/ScriptFunctionInstance.cs
  63. 2 2
      Jint/Native/Global/GlobalObject.cs
  64. 39 16
      Jint/Native/Iterator/IteratorConstructor.cs
  65. 78 2
      Jint/Native/Iterator/IteratorInstance.cs
  66. 81 0
      Jint/Native/Iterator/IteratorProtocol.cs
  67. 1 1
      Jint/Native/Iterator/IteratorPrototype.cs
  68. 5 5
      Jint/Native/JsString.cs
  69. 5 0
      Jint/Native/JsSymbol.cs
  70. 29 17
      Jint/Native/JsValue.cs
  71. 44 72
      Jint/Native/Map/MapConstructor.cs
  72. 0 2
      Jint/Native/Map/MapPrototype.cs
  73. 8 4
      Jint/Native/Number/NumberPrototype.cs
  74. 9 6
      Jint/Native/Object/ObjectConstructor.cs
  75. 91 52
      Jint/Native/Object/ObjectInstance.cs
  76. 2 5
      Jint/Native/Object/ObjectPrototype.cs
  77. 28 60
      Jint/Native/Set/SetConstructor.cs
  78. 57 5
      Jint/Native/String/StringConstructor.cs
  79. 260 47
      Jint/Native/String/StringPrototype.cs
  80. 11 11
      Jint/Native/Symbol/GlobalSymbolRegistry.cs
  81. 1 1
      Jint/Native/Symbol/SymbolConstructor.cs
  82. 2 2
      Jint/Native/Symbol/SymbolPrototype.cs
  83. 37 20
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  84. 5 0
      Jint/Runtime/Environments/EnvironmentRecord.cs
  85. 4 0
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  86. 26 0
      Jint/Runtime/ExceptionHelper.cs
  87. 10 8
      Jint/Runtime/ExpressionIntepreter.cs
  88. 1 1
      Jint/Runtime/JavaScriptException.cs
  89. 14 0
      Jint/Runtime/RangeErrorException.cs
  90. 31 1
      Jint/Runtime/StatementInterpreter.cs
  91. 0 802
      Jint/Runtime/StructDictionary.cs
  92. 19 4
      Jint/Runtime/TypeConverter.cs
  93. 14 0
      Jint/Runtime/TypeErrorException.cs
  94. 49 2
      README.md

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

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

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

@@ -9,11 +9,11 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
-    <PackageReference Include="xunit" Version="2.4.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
+    <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.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" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
+    <PackageReference Include="xunit.runner.console" Version="2.4.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 39 - 0
Jint.Tests.Ecma/EcmaTest.cs

@@ -109,10 +109,41 @@ namespace Jint.Tests.Ecma
 
 
     public class Chapter15 : EcmaTest
     public class Chapter15 : EcmaTest
     {
     {
+        // couple of tests are really slow, run in parallel
+        internal const string SlowTest1 = "ch15/15.1/15.1.3/15.1.3.1/S15.1.3.1_A2.5_T1.js";
+        internal const string SlowTest2 = "ch15/15.1/15.1.3/15.1.3.1/S15.1.3.1_A2.5_T1.js";
+
         [Theory(DisplayName = "Ecma Chapter 15")]
         [Theory(DisplayName = "Ecma Chapter 15")]
         [MemberData(nameof(SourceFiles), parameters: new object[] {"ch15", false })]
         [MemberData(nameof(SourceFiles), parameters: new object[] {"ch15", false })]
         [MemberData(nameof(SourceFiles), parameters: new object[] {"ch15", true }, Skip = "Skipped")]
         [MemberData(nameof(SourceFiles), parameters: new object[] {"ch15", true }, Skip = "Skipped")]
         protected void RunTest(SourceFile sourceFile)
         protected void RunTest(SourceFile sourceFile)
+        {
+            if (sourceFile.Source == SlowTest1 || sourceFile.Source == SlowTest2)
+            {
+                return;
+            }
+            
+            RunTestInternal(sourceFile);
+        }
+    }
+    
+    public class Chapter15_SlowTest1 : EcmaTest
+    {
+        [Theory(DisplayName = "Ecma Chapter 15 Slow Test 1")]
+        [MemberData(nameof(SourceFiles), parameters: new object[] {Chapter15.SlowTest1, false })]
+        [MemberData(nameof(SourceFiles), parameters: new object[] {Chapter15.SlowTest1, true }, Skip = "Skipped")]
+        protected void RunTest(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+        
+    public class Chapter15_SlowTest2 : EcmaTest
+    {
+        [Theory(DisplayName = "Ecma Chapter 15 Slow Test 2")]
+        [MemberData(nameof(SourceFiles), parameters: new object[] {Chapter15.SlowTest2, false })]
+        [MemberData(nameof(SourceFiles), parameters: new object[] {Chapter15.SlowTest2, true }, Skip = "Skipped")]
+        protected void RunTest(SourceFile sourceFile)
         {
         {
             RunTestInternal(sourceFile);
             RunTestInternal(sourceFile);
         }
         }
@@ -220,6 +251,14 @@ namespace Jint.Tests.Ecma
                     continue;
                     continue;
                 }
                 }
 
 
+                if (sourceFile.Skip
+                    && (sourceFile.Reason == "part of new test suite"
+                        || sourceFile.Reason.IndexOf("configurable", StringComparison.OrdinalIgnoreCase) > -1))
+                {
+                    // we consider this obsolete and we don't need to process at all
+                    continue;
+                }
+
                 if (skipped == sourceFile.Skip)
                 if (skipped == sourceFile.Skip)
                 {
                 {
                     results.Add(new object [] { sourceFile });
                     results.Add(new object [] { sourceFile });

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

@@ -6,11 +6,11 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
-    <PackageReference Include="xunit" Version="2.4.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
+    <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.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" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
+    <PackageReference Include="xunit.runner.console" Version="2.4.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 1 - 1
Jint.Tests.Ecma/SingleTest.cs

@@ -21,7 +21,7 @@ namespace Jint.Tests.Ecma
         [RunnableInDebugOnly]
         [RunnableInDebugOnly]
         public void TestSingle()
         public void TestSingle()
         {
         {
-            const string Target = @"ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-4-10.js";
+            const string Target = @"ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A3_T3.js";
             var sourceFile = SourceFiles(Target, false)
             var sourceFile = SourceFiles(Target, false)
                 .SelectMany(x => x)
                 .SelectMany(x => x)
                 .Cast<SourceFile>()
                 .Cast<SourceFile>()

File diff suppressed because it is too large
+ 359 - 359
Jint.Tests.Ecma/TestCases/alltests.json


+ 1 - 1
Jint.Tests.Test262/ArrayTests.cs

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

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

@@ -6,11 +6,11 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
-    <PackageReference Include="xunit" Version="2.4.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
+    <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.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" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
+    <PackageReference Include="xunit.runner.console" Version="2.4.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 14 - 2
Jint.Tests.Test262/SingleTest.cs

@@ -1,3 +1,4 @@
+using System;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
@@ -15,20 +16,31 @@ namespace Jint.Tests.Test262
             }
             }
         }
         }
     }
     }
+
     public class SingleTest : Test262Test
     public class SingleTest : Test262Test
     {
     {
         // helper to test single test case
         // helper to test single test case
         [RunnableInDebugOnly]
         [RunnableInDebugOnly]
         public void TestSingle()
         public void TestSingle()
         {
         {
-            const string Target = @"built-ins/Map/iterator-close-after-set-failure.js";
+            const string Target = @"built-ins/String/prototype/repeat/repeat-string-n-times.js";
+            //const string Target = @"built-ins/Array/from/calling-from-valid-2.js";
             var sourceFile = SourceFiles("built-ins", false)
             var sourceFile = SourceFiles("built-ins", false)
                 .SelectMany(x => x)
                 .SelectMany(x => x)
                 .Cast<SourceFile>()
                 .Cast<SourceFile>()
                 .First(x => x.Source == Target);
                 .First(x => x.Source == Target);
 
 
             var code = File.ReadAllText(sourceFile.FullPath);
             var code = File.ReadAllText(sourceFile.FullPath);
-            RunTestCode(code, strict: true);
+
+            if (code.IndexOf("onlyStrict", StringComparison.Ordinal) < 0)
+            {
+                RunTestCode(code, strict: false);
+            }
+
+            if (code.IndexOf("noStrict", StringComparison.Ordinal) < 0)
+            {
+                RunTestCode(code, strict: true);
+            }
         }
         }
     }
     }
 }
 }

+ 23 - 0
Jint.Tests.Test262/StringTests.cs

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

+ 93 - 11
Jint.Tests.Test262/Test262Test.cs

@@ -1,7 +1,9 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
+using System.Linq;
 using System.Reflection;
 using System.Reflection;
+using System.Text.RegularExpressions;
 using Jint.Runtime;
 using Jint.Runtime;
 using Newtonsoft.Json.Linq;
 using Newtonsoft.Json.Linq;
 using Xunit;
 using Xunit;
@@ -83,14 +85,7 @@ namespace Jint.Tests.Test262
 
 
         protected void RunTestInternal(SourceFile sourceFile)
         protected void RunTestInternal(SourceFile sourceFile)
         {
         {
-            var fullName = sourceFile.FullPath;
-            if (!File.Exists(fullName))
-            {
-                throw new ArgumentException("Could not find source file: " + fullName);
-            }
-
-            string code = File.ReadAllText(fullName);
-            RunTestCode(code);
+            RunTestCode(sourceFile.Code);
         }
         }
 
 
         private void RunTestCode(string code)
         private void RunTestCode(string code)
@@ -118,11 +113,96 @@ namespace Jint.Tests.Test262
                 var name = file.Substring(fixturesPath.Length + 1).Replace("\\", "/");
                 var name = file.Substring(fixturesPath.Length + 1).Replace("\\", "/");
                 bool skip = _skipReasons.TryGetValue(name, out var reason);
                 bool skip = _skipReasons.TryGetValue(name, out var reason);
 
 
+                var code = skip ? "" : File.ReadAllText(file);
+
+                var features = Regex.Match(code, "features: \\[(.+?)\\]");
+                if (features.Success)
+                {
+                    var items = features.Groups[1].Captures[0].Value.Split(",");
+                    foreach (var item in items.Select(x => x.Trim()))
+                    {
+                        // TODO implement
+                        if (item == "cross-realm")
+                        {
+                            skip = true;
+                            reason = "realms not implemented";
+                        }
+                        else if (item == "Symbol.species")
+                        {
+                            skip = true;
+                            reason = "Symbol.species not implemented";
+                        }
+                        else if (item == "Proxy")
+                        {
+                            skip = true;
+                            reason = "Proxies not implemented";
+                        }
+                        else if (item == "Symbol.unscopables")
+                        {
+                            skip = true;
+                            reason = "Symbol.unscopables not implemented";
+                        }
+                        else if (item == "Symbol.match")
+                        {
+                            skip = true;
+                            reason = "Symbol.match not implemented";
+                        }
+                        else if (item == "Symbol.matchAll")
+                        {
+                            skip = true;
+                            reason = "Symbol.matchAll not implemented";
+                        }
+                        else if (item == "Symbol.split")
+                        {
+                            skip = true;
+                            reason = "Symbol.split not implemented";
+                        }
+                        else if (item == "String.prototype.matchAll")
+                        {
+                            skip = true;
+                            reason = "proposal stage";
+                        }
+                        else if (item == "Symbol.search")
+                        {
+                            skip = true;
+                            reason = "Symbol.search not implemented";
+                        }
+                        else if (item == "Symbol.replace")
+                        {
+                            skip = true;
+                            reason = "Symbol.replace not implemented";
+                        }
+                        else if (item == "Symbol.toStringTag")
+                        {
+                            skip = true;
+                            reason = "Symbol.toStringTag not implemented";
+                        }
+                        else if (item == "BigInt")
+                        {
+                            skip = true;
+                            reason = "BigInt not implemented";
+                        }
+                    }
+                }
+
+                if (name.StartsWith("built-ins/String/raw/"))
+                {
+                    skip = true;
+                    reason = "requires template string";
+                }
+
+                if (code.IndexOf("SpecialCasing.txt") > -1)
+                {
+                    skip = true;
+                    reason = "SpecialCasing.txt not implemented";
+                }
+
                 var sourceFile = new SourceFile(
                 var sourceFile = new SourceFile(
                     name,
                     name,
                     file,
                     file,
                     skip,
                     skip,
-                    reason);
+                    reason,
+                    code);
 
 
                 if (skipped == sourceFile.Skip)
                 if (skipped == sourceFile.Skip)
                 {
                 {
@@ -143,23 +223,25 @@ namespace Jint.Tests.Test262
             string source,
             string source,
             string fullPath,
             string fullPath,
             bool skip,
             bool skip,
-            string reason)
+            string reason,
+            string code)
         {
         {
             Skip = skip;
             Skip = skip;
             Source = source;
             Source = source;
             Reason = reason;
             Reason = reason;
             FullPath = fullPath;
             FullPath = fullPath;
+            Code = code;
         }
         }
 
 
         public string Source { get; }
         public string Source { get; }
         public bool Skip { get; }
         public bool Skip { get; }
         public string Reason { get; }
         public string Reason { get; }
         public string FullPath { get; }
         public string FullPath { get; }
+        public string Code { get; }
 
 
         public override string ToString()
         public override string ToString()
         {
         {
             return Source;
             return Source;
         }
         }
     }
     }
-
 }
 }

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T2.js

@@ -4,7 +4,7 @@
 /*---
 /*---
 info: String.prototype.indexOf works properly
 info: String.prototype.indexOf works properly
 es5id: 15.5.4.7_A5_T2
 es5id: 15.5.4.7_A5_T2
-description: Search one symbol from it`s position in the string
+description: Search one symbol from its position in the string
 ---*/
 ---*/
 
 
 var TEST_STRING = new String(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
 var TEST_STRING = new String(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T3.js

@@ -4,7 +4,7 @@
 /*---
 /*---
 info: String.prototype.indexOf works properly
 info: String.prototype.indexOf works properly
 es5id: 15.5.4.7_A5_T3
 es5id: 15.5.4.7_A5_T3
-description: Search one symbol from it`s position+1 in the string
+description: Search one symbol from its position+1 in the string
 ---*/
 ---*/
 
 
 var TEST_STRING = new String(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
 var TEST_STRING = new String(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T5.js

@@ -4,7 +4,7 @@
 /*---
 /*---
 info: String.prototype.indexOf works properly
 info: String.prototype.indexOf works properly
 es5id: 15.5.4.7_A5_T5
 es5id: 15.5.4.7_A5_T5
-description: Search substring from it`s position in the string
+description: Search substring from its position in the string
 ---*/
 ---*/
 
 
 var TEST_STRING = new String(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
 var TEST_STRING = new String(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T6.js

@@ -4,7 +4,7 @@
 /*---
 /*---
 info: String.prototype.indexOf works properly
 info: String.prototype.indexOf works properly
 es5id: 15.5.4.7_A5_T6
 es5id: 15.5.4.7_A5_T6
-description: Search substring from it`s position+1 in the string
+description: Search substring from its position+1 in the string
 ---*/
 ---*/
 
 
 var TEST_STRING = new String(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
 var TEST_STRING = new String(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");

+ 15 - 4
Jint.Tests.Test262/test/built-ins/String/prototype/matchAll/regexp-prototype-has-no-matchAll.js

@@ -10,15 +10,26 @@ info: |
       a. Let matcher be ? GetMethod(regexp, @@matchAll).
       a. Let matcher be ? GetMethod(regexp, @@matchAll).
       b. If matcher is not undefined, then
       b. If matcher is not undefined, then
         [...]
         [...]
-    3. Return ? MatchAllIterator(regexp, O).
+    [...]
+    4. Let matcher be ? RegExpCreate(R, "g").
+    [...]
+
+  21.2.3.2.3 Runtime Semantics: RegExpCreate ( P, F )
+    [...]
+    2. Return ? RegExpInitialize(obj, P, F).
+
+  21.2.3.2.2 Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
+    1. If pattern is undefined, let P be the empty String.
+    2. Else, let P be ? ToString(pattern).
+    [...]
 features: [Symbol.matchAll, String.prototype.matchAll]
 features: [Symbol.matchAll, String.prototype.matchAll]
 includes: [compareArray.js, compareIterator.js, regExpUtils.js]
 includes: [compareArray.js, compareIterator.js, regExpUtils.js]
 ---*/
 ---*/
 
 
 delete RegExp.prototype[Symbol.matchAll];
 delete RegExp.prototype[Symbol.matchAll];
-var str = 'a*b';
+var str = '/a/g*/b/g';
 
 
 assert.compareIterator(str.matchAll(/\w/g), [
 assert.compareIterator(str.matchAll(/\w/g), [
-  matchValidator(['a'], 0, str),
-  matchValidator(['b'], 2, str)
+  matchValidator(['/a/g'], 0, str),
+  matchValidator(['/b/g'], 5, str)
 ]);
 ]);

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T1.js

@@ -4,7 +4,7 @@
 /*---
 /*---
 info: String.prototype.toString() returns this string value
 info: String.prototype.toString() returns this string value
 es5id: 15.5.4.2_A1_T1
 es5id: 15.5.4.2_A1_T1
-description: Create new String(number) and check it`s method toString()
+description: Create new String(number) and check its method toString()
 ---*/
 ---*/
 
 
 var __string__obj = new String(1);
 var __string__obj = new String(1);

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T2.js

@@ -4,7 +4,7 @@
 /*---
 /*---
 info: String.prototype.toString() returns this string value
 info: String.prototype.toString() returns this string value
 es5id: 15.5.4.2_A1_T2
 es5id: 15.5.4.2_A1_T2
-description: Create new String(boolean) and check it`s method toString()
+description: Create new String(boolean) and check its method toString()
 ---*/
 ---*/
 
 
 var __string__obj = new String(true);
 var __string__obj = new String(true);

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T3.js

@@ -4,7 +4,7 @@
 /*---
 /*---
 info: String.prototype.toString() returns this string value
 info: String.prototype.toString() returns this string value
 es5id: 15.5.4.2_A1_T3
 es5id: 15.5.4.2_A1_T3
-description: Create new String(string) and check it`s method toString()
+description: Create new String(string) and check its method toString()
 ---*/
 ---*/
 
 
 var __string__obj = new String("metal");
 var __string__obj = new String("metal");

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T4.js

@@ -4,7 +4,7 @@
 /*---
 /*---
 info: String.prototype.toString() returns this string value
 info: String.prototype.toString() returns this string value
 es5id: 15.5.4.2_A1_T4
 es5id: 15.5.4.2_A1_T4
-description: Create new String(function(){}()) and check it`s method toString()
+description: Create new String(function(){}()) and check its method toString()
 ---*/
 ---*/
 
 
 var __string__obj = new String(function() {}());
 var __string__obj = new String(function() {}());

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-line-terminator.js

@@ -26,5 +26,5 @@ var expected = lt + 'a' + lt + 'b';
 
 
 assert.sameValue(
 assert.sameValue(
   trimEnd.call(str),
   trimEnd.call(str),
-  expected,
+  expected
 );
 );

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-cannot-convert-to-primitive-err.js

@@ -55,5 +55,5 @@ assert.throws(
   TypeError,
   TypeError,
   function() {
   function() {
     String.prototype.trimEnd.call(thisVal);
     String.prototype.trimEnd.call(thisVal);
-  },
+  }
 );
 );

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-toprimitive-meth-priority.js

@@ -63,7 +63,7 @@ assert.sameValue(
 assert.sameValue(
 assert.sameValue(
   result,
   result,
   '42',
   '42',
-  'thisVal[Symbol.toPrimitive] expected to have been called.',
+  'thisVal[Symbol.toPrimitive] expected to have been called.'
 );
 );
 
 
 // Test that thisVal.toString and thisVal.valueOf have not been accessedo
 // Test that thisVal.toString and thisVal.valueOf have not been accessedo

+ 1 - 0
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-toprimitive-returns-object-err.js

@@ -33,6 +33,7 @@ var thisVal = {
   },
   },
 };
 };
 
 
+assert.sameValue(typeof String.prototype.trimEnd, 'function');
 assert.throws(TypeError, function() {
 assert.throws(TypeError, function() {
   String.prototype.trimEnd.call(thisVal);
   String.prototype.trimEnd.call(thisVal);
 });
 });

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-tostring-meth-priority.js

@@ -77,7 +77,7 @@ assert.sameValue(
 assert.sameValue(
 assert.sameValue(
   result,
   result,
   '42',
   '42',
-  'thisVal.toString expected to have been called.',
+  'thisVal.toString expected to have been called.'
 );
 );
 
 
 // Test that thisVal[toPrimitive] has been accessed.
 // Test that thisVal[toPrimitive] has been accessed.

+ 1 - 0
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-tostring-returns-object-err.js

@@ -51,6 +51,7 @@ var thisVal = {
   },
   },
 };
 };
 
 
+assert.sameValue(typeof String.prototype.trimEnd, 'function');
 assert.throws(TypeError, function() {
 assert.throws(TypeError, function() {
   String.prototype.trimEnd.call(thisVal);
   String.prototype.trimEnd.call(thisVal);
 });
 });

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-valueof-meth-priority.js

@@ -76,7 +76,7 @@ assert.sameValue(
 assert.sameValue(
 assert.sameValue(
   result,
   result,
   '42',
   '42',
-  'thisVal.valueOf expected to have been called.',
+  'thisVal.valueOf expected to have been called.'
 );
 );
 
 
 // Test that thisVal[toPrimitive] and thisVal.toString has been accessed.
 // Test that thisVal[toPrimitive] and thisVal.toString has been accessed.

+ 1 - 0
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-valueof-returns-object-err.js

@@ -52,6 +52,7 @@ var thisVal = {
   },
   },
 };
 };
 
 
+assert.sameValue(typeof String.prototype.trimEnd, 'function');
 assert.throws(TypeError, function() {
 assert.throws(TypeError, function() {
   String.prototype.trimEnd.call(thisVal);
   String.prototype.trimEnd.call(thisVal);
 });
 });

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-whitespace.js

@@ -29,5 +29,5 @@ var expected = wspc + 'a' + wspc + 'b';
 
 
 assert.sameValue(
 assert.sameValue(
   trimEnd.call(str),
   trimEnd.call(str),
-  expected,
+  expected
 );
 );

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-line-terminator.js

@@ -26,5 +26,5 @@ var expected = 'a' + lt + 'b' + lt;
 
 
 assert.sameValue(
 assert.sameValue(
   trimStart.call(str),
   trimStart.call(str),
-  expected,
+  expected
 );
 );

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-cannot-convert-to-primitive-err.js

@@ -55,5 +55,5 @@ assert.throws(
   TypeError,
   TypeError,
   function() {
   function() {
     String.prototype.trimStart.call(thisVal);
     String.prototype.trimStart.call(thisVal);
-  },
+  }
 );
 );

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-toprimitive-meth-priority.js

@@ -63,7 +63,7 @@ assert.sameValue(
 assert.sameValue(
 assert.sameValue(
   result,
   result,
   '42',
   '42',
-  'thisVal[Symbol.toPrimitive] expected to have been called.',
+  'thisVal[Symbol.toPrimitive] expected to have been called.'
 );
 );
 
 
 // Test that thisVal.toString and thisVal.valueOf have not been accessedo
 // Test that thisVal.toString and thisVal.valueOf have not been accessedo

+ 1 - 0
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-toprimitive-returns-object-err.js

@@ -33,6 +33,7 @@ var thisVal = {
   },
   },
 };
 };
 
 
+assert.sameValue(typeof String.prototype.trimStart, 'function');
 assert.throws(TypeError, function() {
 assert.throws(TypeError, function() {
   String.prototype.trimStart.call(thisVal);
   String.prototype.trimStart.call(thisVal);
 });
 });

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-tostring-meth-priority.js

@@ -77,7 +77,7 @@ assert.sameValue(
 assert.sameValue(
 assert.sameValue(
   result,
   result,
   '42',
   '42',
-  'thisVal.toString expected to have been called.',
+  'thisVal.toString expected to have been called.'
 );
 );
 
 
 // Test that thisVal[toPrimitive] has been accessed.
 // Test that thisVal[toPrimitive] has been accessed.

+ 1 - 0
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-tostring-returns-object-err.js

@@ -51,6 +51,7 @@ var thisVal = {
   },
   },
 };
 };
 
 
+assert.sameValue(typeof String.prototype.trimStart, 'function');
 assert.throws(TypeError, function() {
 assert.throws(TypeError, function() {
   String.prototype.trimStart.call(thisVal);
   String.prototype.trimStart.call(thisVal);
 });
 });

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-valueof-meth-priority.js

@@ -76,7 +76,7 @@ assert.sameValue(
 assert.sameValue(
 assert.sameValue(
   result,
   result,
   '42',
   '42',
-  'thisVal.valueOf expected to have been called.',
+  'thisVal.valueOf expected to have been called.'
 );
 );
 
 
 // Test that thisVal[toPrimitive] and thisVal.toString has been accessed.
 // Test that thisVal[toPrimitive] and thisVal.toString has been accessed.

+ 1 - 0
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-valueof-returns-object-err.js

@@ -52,6 +52,7 @@ var thisVal = {
   },
   },
 };
 };
 
 
+assert.sameValue(typeof String.prototype.trimStart, 'function');
 assert.throws(TypeError, function() {
 assert.throws(TypeError, function() {
   String.prototype.trimStart.call(thisVal);
   String.prototype.trimStart.call(thisVal);
 });
 });

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-whitespace.js

@@ -29,5 +29,5 @@ var expected = 'a' + wspc + 'b' + wspc;
 
 
 assert.sameValue(
 assert.sameValue(
   trimStart.call(str),
   trimStart.call(str),
-  expected,
+  expected
 );
 );

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T1.js

@@ -4,7 +4,7 @@
 /*---
 /*---
 info: String.prototype.valueOf() returns this string value
 info: String.prototype.valueOf() returns this string value
 es5id: 15.5.4.3_A1_T1
 es5id: 15.5.4.3_A1_T1
-description: Create String object as new String(1) and check it`s valueOf()
+description: Create String object as new String(1) and check its valueOf()
 ---*/
 ---*/
 
 
 var __string__obj = new String(1);
 var __string__obj = new String(1);

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T2.js

@@ -4,7 +4,7 @@
 /*---
 /*---
 info: String.prototype.valueOf() returns this string value
 info: String.prototype.valueOf() returns this string value
 es5id: 15.5.4.3_A1_T2
 es5id: 15.5.4.3_A1_T2
-description: Create String object as new String(true) and check it`s valueOf()
+description: Create String object as new String(true) and check its valueOf()
 ---*/
 ---*/
 
 
 var __string__obj = new String(true);
 var __string__obj = new String(true);

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T3.js

@@ -4,7 +4,7 @@
 /*---
 /*---
 info: String.prototype.valueOf() returns this string value
 info: String.prototype.valueOf() returns this string value
 es5id: 15.5.4.3_A1_T3
 es5id: 15.5.4.3_A1_T3
-description: Create String object as new String(string) and check it`s valueOf()
+description: Create String object as new String(string) and check its valueOf()
 ---*/
 ---*/
 
 
 var __string__obj = new String("metal");
 var __string__obj = new String("metal");

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T4.js

@@ -5,7 +5,7 @@
 info: String.prototype.valueOf() returns this string value
 info: String.prototype.valueOf() returns this string value
 es5id: 15.5.4.3_A1_T4
 es5id: 15.5.4.3_A1_T4
 description: >
 description: >
-    Create String object as new String(function(){}()) and check it`s
+    Create String object as new String(function(){}()) and check its
     valueOf()
     valueOf()
 ---*/
 ---*/
 
 

+ 201 - 14
Jint.Tests.Test262/test/skipped.json

@@ -1,15 +1,107 @@
 [
 [
   {
   {
-    "source": "built-ins/Map/iterator-is-undefined-throws.js",
-    "reason": "Esprima problem"
+    "source": "built-ins/Array/prototype/concat/create-ctor-non-object.js",
+    "reason": "Constructor functions not implemented"
   },
   },
   {
   {
-    "source": "built-ins/Boolean/proto-from-ctor-realm.js",
-    "reason": "realms not implemented"
+    "source": "built-ins/Array/prototype/map/create-ctor-non-object.js",
+    "reason": "Constructor functions not implemented"
   },
   },
   {
   {
-    "source": "built-ins/Map/proto-from-ctor-realm.js",
-    "reason": "realms not implemented"
+    "source": "built-ins/Array/prototype/slice/create-ctor-non-object.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/splice/create-ctor-non-object.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/of/construct-this-with-the-number-of-arguments.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/from/iter-set-length-err",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/from/Array.from_forwards-length-for-array-likes.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/from/iter-cstm-ctor.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/from/iter-cstm-ctor-err.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/from/source-object-constructor.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/of/return-abrupt-from-contructor.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/concat/create-ctor-poisoned.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/map/create-ctor-poisoned.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/slice/create-ctor-poisoned.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/splice/create-ctor-poisoned.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/of/return-a-custom-instance.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/of/return-abrupt-from-data-property.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/of/return-abrupt-from-setting-length.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/of/sets-length.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/from/iter-set-length-err.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/filter/create-ctor-non-object.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/filter/create-ctor-poisoned.js",
+    "reason": "Constructor functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/from/items-is-arraybuffer.js",
+    "reason": "ArrayBuffer not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/concat/Array.prototype.concat_non-array.js",
+    "reason": "class keyword not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/concat/Array.prototype.concat_large-typed-array.js",
+    "reason": "Uint8Array not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/concat/Array.prototype.concat_small-typed-array.js",
+    "reason": "Uint8Array not implemented"
   },
   },
   {
   {
     "source": "built-ins/Map/prototype/clear/context-is-weakmap-object-throws.js",
     "source": "built-ins/Map/prototype/clear/context-is-weakmap-object-throws.js",
@@ -27,10 +119,6 @@
     "source": "built-ins/Map/prototype/forEach/does-not-have-mapdata-internal-slot-weakmap.js",
     "source": "built-ins/Map/prototype/forEach/does-not-have-mapdata-internal-slot-weakmap.js",
     "reason": "WeakMap not implemented"
     "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",
     "source": "built-ins/Map/prototype/forEach/iterates-values-deleted-then-readded.js",
     "reason": "delete/add detection not implemented for map iterator during iteration"
     "reason": "delete/add detection not implemented for map iterator during iteration"
@@ -87,10 +175,6 @@
     "source": "built-ins/Set/prototype/forEach/iterates-values-revisits-after-delete-re-add.js",
     "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"
     "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",
     "source": "built-ins/Set/prototype/has/does-not-have-setdata-internal-slot-weakset.js",
     "reason": "WeakSet not implemented"
     "reason": "WeakSet not implemented"
@@ -102,5 +186,108 @@
   {
   {
     "source": "built-ins/Set/prototype/forEach/this-arg-explicit-cannot-override-lexical-this-arrow.js",
     "source": "built-ins/Set/prototype/forEach/this-arg-explicit-cannot-override-lexical-this-arrow.js",
     "reason": "arrow functions not implemented"
     "reason": "arrow functions not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/reverse/length-exceeding-integer-limit-with-proxy.js",
+    "reason": "proxies not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/slice/length-exceeding-integer-limit-proxied-array.js",
+    "reason": "proxies not implemented"
+  },
+  {
+    "source": "built-ins/Array/prototype/splice/create-non-array-invalid-len.js",
+    "reason": "requires constructor functions"
+  },
+  {
+    "source": "built-ins/Array/from/iter-set-elem-prop-err.js",
+    "reason": "requires constructor functions"
+  },
+  {
+    "source": "built-ins/Array/prototype/toLocaleString/primitive_this_value.js",
+    "reason": "requires toLocaleString changes"
+  },
+  {
+    "source": "built-ins/Array/prototype/toLocaleString/primitive_this_value_getter.js",
+    "reason": "requires toLocaleString changes"
+  },
+
+  // experimenta
+
+  {
+    "source": "built-ins/Array/prototype/flat/array-like-objects.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flat/bound-function-call.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flat/empty-array-elements.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flat/empty-object-elements.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flat/length.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flat/name.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flat/non-numeric-depth-should-not-throw.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flat/null-undefined-elements.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flat/positive-infinity.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flat/prop-desc.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flatMap/array-like-objects.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flatMap/bound-function-argument.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flatMap/depth-always-one.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flatMap/length.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flatMap/name.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/Array/prototype/flatMap/thisArg-argument.js",
+    "reason": "experimental"
+  },
+  {
+    "source": "built-ins/String/prototype/padEnd/observable-operations.js",
+    "reason": "observables not implemetned"
+  },
+  {
+    "source": "built-ins/String/prototype/padStart/observable-operations.js",
+    "reason": "observables not implemetned"
+  },
+  {
+    "source": "built-ins/StringIteratorPrototype/next/next-iteration-surrogate-pairs.js",
+    "reason": "code point iteration not implemented"
   }
   }
 ]
 ]

+ 4 - 4
Jint.Tests/Jint.Tests.csproj

@@ -12,11 +12,11 @@
     <Reference Include="Microsoft.CSharp" Condition=" '$(TargetFramework)' == 'net452' " />
     <Reference Include="Microsoft.CSharp" Condition=" '$(TargetFramework)' == 'net452' " />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
-    <PackageReference Include="xunit" Version="2.4.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
+    <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.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" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
+    <PackageReference Include="xunit.runner.console" Version="2.4.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

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

@@ -1106,6 +1106,17 @@ namespace Jint.Tests.Runtime
             Assert.Equal(0, result);
             Assert.Equal(0, result);
         }
         }
 
 
+        [Fact]
+        public void TimeWithinDayShouldHandleNegativeValues()
+        {
+            RunTest(@"
+                // using a date < 1970 so that the primitive value is negative
+                var d = new Date(1958, 0, 1);
+                d.setMonth(-1);
+                assert(d.getDate() == 1);
+            ");
+        }
+
         [Fact]
         [Fact]
         public void LocalDateTimeShouldNotLoseTimezone()
         public void LocalDateTimeShouldNotLoseTimezone()
         {
         {

+ 1 - 0
Jint.sln.DotSettings

@@ -4,6 +4,7 @@
 	<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/=2CC1F6A6_002D7DCC_002D4C7C_002DA619_002DACE1A4296446_002Fd_003ATestCases/@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/=44CF8026_002DA797_002D420C_002DA8BC_002D409BB67D32F6_002Fd_003Atest/@EntryIndexedValue">ExplicitlyExcluded</s:String>
+	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=62FFFDBD_002DAB58_002D490D_002D9A50_002DAA7C53BF0409_002Fd_003Atest/@EntryIndexedValue">ExplicitlyExcluded</s:String>
 	<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=95ED73F7_002D3519_002D4733_002DB361_002D7790053A1A71/@EntryIndexedValue"></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: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_003Aharness/@EntryIndexedValue">ExplicitlyExcluded</s:String>

+ 459 - 0
Jint/Collections/StringDictionarySlim.cs

@@ -0,0 +1,459 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using Jint.Runtime;
+
+namespace Jint.Collections
+{
+    /// <summary>
+    /// DictionarySlim<TKey, TValue> is similar to Dictionary<TKey, TValue> but optimized in three ways:
+    /// 1) It allows access to the value by ref replacing the common TryGetValue and Add pattern.
+    /// 2) It does not store the hash code (assumes it is cheap to equate values).
+    /// 3) It does not accept an equality comparer (assumes Object.GetHashCode() and Object.Equals() or overridden implementation are cheap and sufficient).
+    /// </summary>
+    [DebuggerTypeProxy(typeof(DictionarySlimDebugView<>))]
+    [DebuggerDisplay("Count = {Count}")]
+    internal sealed class StringDictionarySlim<TValue>
+    {
+        // We want to initialize without allocating arrays. We also want to avoid null checks.
+        // Array.Empty would give divide by zero in modulo operation. So we use static one element arrays.
+        // The first add will cause a resize replacing these with real arrays of three elements.
+        // Arrays are wrapped in a class to avoid being duplicated for each <TKey, TValue>
+        private static readonly Entry[] InitialEntries = new Entry[1];
+        private int _count;
+        // 1-based index into _entries; 0 means empty
+        private int[] _buckets;
+        private Entry[] _entries;
+
+        [DebuggerDisplay("({key}, {value})->{next}")]
+        private struct Entry
+        {
+            public string key;
+            public TValue value;
+            // 0-based index of next entry in chain: -1 means end of chain
+            public int next;
+        }
+
+        public StringDictionarySlim()
+        {
+            _buckets = HashHelpers.DictionarySlimSizeOneIntArray;
+            _entries = InitialEntries;
+        }
+
+        public StringDictionarySlim(int capacity)
+        {
+            if (capacity < 2) ExceptionHelper.ThrowArgumentOutOfRangeException();
+            capacity = HashHelpers.PowerOf2(capacity);
+            _buckets = new int[capacity];
+            _entries = new Entry[capacity];
+        }
+
+        public int Count => _count;
+
+        public int Capacity => _entries.Length;
+
+        public bool ContainsKey(string key)
+        {
+            Entry[] entries = _entries;
+            for (int i = _buckets[key.GetHashCode() & (_buckets.Length - 1)] - 1;
+                    (uint)i < (uint)entries.Length; i = entries[i].next)
+            {
+                if (key == entries[i].key)
+                    return true;
+            }
+
+            return false;
+        }
+
+        public TValue GetValueOrDefault(string key)
+        {
+            bool result = TryGetValue(key, out TValue value);
+            return value;
+        }
+
+        public bool TryGetValue(string key, out TValue value)
+        {
+            Entry[] entries = _entries;
+            for (int i = _buckets[key.GetHashCode() & (_buckets.Length - 1)] - 1;
+                    (uint)i < (uint)entries.Length; i = entries[i].next)
+            {
+                if (key == entries[i].key)
+                {
+                    value = entries[i].value;
+                    return true;
+                }
+            }
+
+            value = default;
+            return false;
+        }
+
+        public bool Remove(string key)
+        {
+            Entry[] entries = _entries;
+            int bucketIndex = key.GetHashCode() & (_buckets.Length - 1);
+            int entryIndex = _buckets[bucketIndex] - 1;
+
+            int lastIndex = -1;
+            while (entryIndex != -1)
+            {
+                Entry candidate = entries[entryIndex];
+                if (candidate.key == key)
+                {
+                    if (lastIndex == -1)
+                    {
+                        // Fixup bucket to new head (if any)
+                        _buckets[bucketIndex] = candidate.next + 1;
+                    }
+                    else
+                    {
+                        // Fixup preceding element in chain to point to next (if any)
+                        entries[lastIndex].next = candidate.next;
+                    }
+
+                    // move last item to this index and fix link to it
+                    if (entryIndex != --_count)
+                    {
+                        entries[entryIndex] = entries[_count];
+
+                        bucketIndex = entries[entryIndex].key.GetHashCode() & (_buckets.Length - 1);
+                        lastIndex = _buckets[bucketIndex] - 1;
+
+                        if (lastIndex == _count)
+                        {
+                            // Fixup bucket to this index
+                            _buckets[bucketIndex] = entryIndex + 1;
+                        }
+                        else
+                        {
+                            // Find preceding element in chain and point to this index
+                            while (entries[lastIndex].next != _count)
+                                lastIndex = entries[lastIndex].next;
+                            entries[lastIndex].next = entryIndex;
+                        }
+                    }
+                    entries[_count] = default;
+                    return true;
+                }
+                lastIndex = entryIndex;
+                entryIndex = candidate.next;
+            }
+
+            return false;
+        }
+
+        public void Clear()
+        {
+            int count = _count;
+            if (count > 0)
+            {
+                Array.Clear(_buckets, 0, _buckets.Length);
+                _count = 0;
+                Array.Clear(_entries, 0, count);
+            }
+        }
+
+        public ref TValue this[string key]
+        {
+            get
+            {
+                Entry[] entries = _entries;
+                int bucketIndex = key.GetHashCode() & (_buckets.Length - 1);
+                for (int i = _buckets[bucketIndex] - 1;
+                        (uint)i < (uint)entries.Length; i = entries[i].next)
+                {
+                    if (key == entries[i].key)
+                        return ref entries[i].value;
+                }
+
+                return ref AddKey(key, bucketIndex);
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private ref TValue AddKey(string key, int bucketIndex)
+        {
+            Entry[] entries = _entries;
+
+            if (_count == entries.Length || entries.Length == 1)
+            {
+                entries = Resize();
+                bucketIndex = key.GetHashCode() & (_buckets.Length - 1);
+                // entry indexes were not changed by Resize
+            }
+
+            int entryIndex = _count++;
+            entries[entryIndex].key = key;
+            entries[entryIndex].next = _buckets[bucketIndex] - 1;
+            _buckets[bucketIndex] = entryIndex + 1;
+            return ref entries[entryIndex].value;
+        }
+
+        private Entry[] Resize()
+        {
+            int count = _count;
+            var entries = new Entry[_entries.Length * 2];
+            Array.Copy(_entries, 0, entries, 0, count);
+
+            var newBuckets = new int[entries.Length];
+            while (count-- > 0)
+            {
+                int bucketIndex = entries[count].key.GetHashCode() & (newBuckets.Length - 1);
+                entries[count].next = newBuckets[bucketIndex] - 1;
+                newBuckets[bucketIndex] = count + 1;
+            }
+
+            _buckets = newBuckets;
+            _entries = entries;
+
+            return entries;
+        }
+
+        public KeyCollection Keys => new KeyCollection(this);
+
+        public ValueCollection Values => new ValueCollection(this);
+
+        public void CopyTo(KeyValuePair<string, TValue>[] array, int index)
+        {
+            Entry[] entries = _entries;
+            for (int i = 0; i < _count; i++)
+            {
+                array[index++] = new KeyValuePair<string, TValue>(
+                    entries[i].key,
+                    entries[i].value);
+            }
+        }
+
+        public Enumerator GetEnumerator() => new Enumerator(this); // avoid boxing
+
+        public struct Enumerator : IEnumerator<KeyValuePair<string, TValue>>
+        {
+            private readonly StringDictionarySlim<TValue> _dictionary;
+            private int _index;
+            private KeyValuePair<string, TValue> _current;
+
+            internal Enumerator(StringDictionarySlim<TValue> dictionary)
+            {
+                _dictionary = dictionary;
+                _index = 0;
+                _current = default;
+            }
+
+            public bool MoveNext()
+            {
+                if (_index == _dictionary._count)
+                {
+                    _current = default;
+                    return false;
+                }
+
+                _current = new KeyValuePair<string, TValue>(
+                    _dictionary._entries[_index].key,
+                    _dictionary._entries[_index++].value);
+                return true;
+            }
+
+            public KeyValuePair<string, TValue> Current => _current;
+
+            object IEnumerator.Current => _current;
+
+            void IEnumerator.Reset()
+            {
+                _index = 0;
+            }
+
+            public void Dispose() { }
+        }
+
+        public struct KeyCollection : ICollection<string>, IReadOnlyCollection<string>
+        {
+            private readonly StringDictionarySlim<TValue> _dictionary;
+
+            internal KeyCollection(StringDictionarySlim<TValue> dictionary)
+            {
+                _dictionary = dictionary;
+            }
+
+            public int Count => _dictionary._count;
+
+            bool ICollection<string>.IsReadOnly => true;
+
+            void ICollection<string>.Add(string item) =>
+                ExceptionHelper.ThrowNotSupportedException();
+
+            void ICollection<string>.Clear() =>
+                ExceptionHelper.ThrowNotSupportedException();
+
+            public bool Contains(string item) => _dictionary.ContainsKey(item);
+
+            bool ICollection<string>.Remove(string item) =>
+                ExceptionHelper.ThrowNotSupportedException<bool>();
+
+            public void CopyTo(string[] array, int index)
+            {
+                Entry[] entries = _dictionary._entries;
+                for (int i = 0; i < _dictionary._count; i++)
+                {
+                    array[index++] = entries[i].key;
+                }
+            }
+
+            public Enumerator GetEnumerator() => new Enumerator(_dictionary); // avoid boxing
+            IEnumerator<string> IEnumerable<string>.GetEnumerator() => new Enumerator(_dictionary);
+            IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_dictionary);
+
+            public struct Enumerator : IEnumerator<string>
+            {
+                private readonly StringDictionarySlim<TValue> _dictionary;
+                private int _index;
+                private string _current;
+
+                internal Enumerator(StringDictionarySlim<TValue> dictionary)
+                {
+                    _dictionary = dictionary;
+                    _index = 0;
+                    _current = default;
+                }
+
+                public string Current => _current;
+
+                object IEnumerator.Current => _current;
+
+                public void Dispose() { }
+
+                public bool MoveNext()
+                {
+                    if (_index == _dictionary._count)
+                    {
+                        _current = default;
+                        return false;
+                    }
+
+                    _current = _dictionary._entries[_index++].key;
+                    return true;
+                }
+
+                public void Reset()
+                {
+                    _index = 0;
+                }
+            }
+        }
+
+        public struct ValueCollection : ICollection<TValue>, IReadOnlyCollection<TValue>
+        {
+            private readonly StringDictionarySlim<TValue> _dictionary;
+
+            internal ValueCollection(StringDictionarySlim<TValue> dictionary)
+            {
+                _dictionary = dictionary;
+            }
+
+            public int Count => _dictionary._count;
+
+            bool ICollection<TValue>.IsReadOnly => true;
+
+            void ICollection<TValue>.Add(TValue item) =>
+                ExceptionHelper.ThrowNotSupportedException();
+
+            void ICollection<TValue>.Clear() =>
+                ExceptionHelper.ThrowNotSupportedException();
+
+            bool ICollection<TValue>.Contains(TValue item) =>
+                ExceptionHelper.ThrowNotSupportedException<bool>(); // performance antipattern
+
+            bool ICollection<TValue>.Remove(TValue item) =>
+                ExceptionHelper.ThrowNotSupportedException<bool>();
+
+            public void CopyTo(TValue[] array, int index)
+            {
+                Entry[] entries = _dictionary._entries;
+                for (int i = 0; i < _dictionary._count; i++)
+                {
+                    array[index++] = entries[i].value;
+                }
+            }
+
+            public Enumerator GetEnumerator() => new Enumerator(_dictionary); // avoid boxing
+            IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() => new Enumerator(_dictionary);
+            IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_dictionary);
+
+            public struct Enumerator : IEnumerator<TValue>
+            {
+                private readonly StringDictionarySlim<TValue> _dictionary;
+                private int _index;
+                private TValue _current;
+
+                internal Enumerator(StringDictionarySlim<TValue> dictionary)
+                {
+                    _dictionary = dictionary;
+                    _index = 0;
+                    _current = default;
+                }
+
+                public TValue Current => _current;
+
+                object IEnumerator.Current => _current;
+
+                public void Dispose() { }
+
+                public bool MoveNext()
+                {
+                    if (_index == _dictionary._count)
+                    {
+                        _current = default;
+                        return false;
+                    }
+
+                    _current = _dictionary._entries[_index++].value;
+                    return true;
+                }
+
+                public void Reset()
+                {
+                    _index = 0;
+                }
+            }
+        }
+
+        internal static class HashHelpers
+        {
+            internal static readonly int[] DictionarySlimSizeOneIntArray = new int[1];
+
+            internal static int PowerOf2(int v)
+            {
+                if ((v & (v - 1)) == 0) return v;
+                int i = 2;
+                while (i < v) i <<= 1;
+                return i;
+            }
+        }
+
+        internal sealed class DictionarySlimDebugView<V>
+        {
+            private readonly StringDictionarySlim<V> _dictionary;
+
+            public DictionarySlimDebugView(StringDictionarySlim<V> dictionary)
+            {
+                _dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary));
+            }
+
+            [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+            public KeyValuePair<string, V>[] Items
+            {
+                get
+                {
+                    var array = new KeyValuePair<string, V>[_dictionary.Count];
+                    _dictionary.CopyTo(array, 0);
+                    return array;
+                }
+            }
+        }
+    }
+}

+ 10 - 2
Jint/Directory.Build.props

@@ -11,10 +11,18 @@
     <PackageTags>javascript, interpreter</PackageTags>
     <PackageTags>javascript, interpreter</PackageTags>
     <PackageProjectUrl>https://github.com/sebastienros/jint</PackageProjectUrl>
     <PackageProjectUrl>https://github.com/sebastienros/jint</PackageProjectUrl>
     <PackageLicenseUrl>https://raw.githubusercontent.com/sebastienros/jint/master/LICENSE.txt</PackageLicenseUrl>
     <PackageLicenseUrl>https://raw.githubusercontent.com/sebastienros/jint/master/LICENSE.txt</PackageLicenseUrl>
-    <RepositoryType>git</RepositoryType>
-    <RepositoryUrl>git://github.com/sebastienros/jint</RepositoryUrl>
+
+    <!-- SourceLink support -->
+    <PublishRepositoryUrl>true</PublishRepositoryUrl>
+    <EmbedUntrackedSources>true</EmbedUntrackedSources>
+    <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
+
   </PropertyGroup>
   </PropertyGroup>
 
 
+  <ItemGroup Condition="'$(SourceLinkEnabled)' != 'false'">
+    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-63127-02" PrivateAssets="All"/>
+  </ItemGroup>
+
   <!-- Called after so that the <VersionSuffix> is built after the local ones -->
   <!-- Called after so that the <VersionSuffix> is built after the local ones -->
   <Import Project="../Common.props"/>
   <Import Project="../Common.props"/>
 
 

+ 3 - 4
Jint/Engine.cs

@@ -51,7 +51,7 @@ namespace Jint
 
 
         // cached access
         // cached access
         private readonly bool _isDebugMode;
         private readonly bool _isDebugMode;
-        private readonly bool _isStrict;
+        internal readonly bool _isStrict;
         private readonly int _maxStatements;
         private readonly int _maxStatements;
         private readonly long _memoryLimit;
         private readonly long _memoryLimit;
         private readonly bool _runBeforeStatementChecks;
         private readonly bool _runBeforeStatementChecks;
@@ -713,10 +713,9 @@ namespace Jint
                 }
                 }
             }
             }
 
 
-            var record = (EnvironmentRecord) baseValue;
-            if (ReferenceEquals(record, null))
+            if (!(baseValue is EnvironmentRecord record))
             {
             {
-                ExceptionHelper.ThrowArgumentException();
+                return ExceptionHelper.ThrowArgumentException<JsValue>();
             }
             }
 
 
             var bindingValue = record.GetBindingValue(reference._name, reference._strict);
             var bindingValue = record.GetBindingValue(reference._name, reference._strict);

+ 43 - 0
Jint/EsprimaExtensions.cs

@@ -0,0 +1,43 @@
+using System;
+using Esprima.Ast;
+using Jint.Native.Symbol;
+using Jint.Runtime;
+
+namespace Jint
+{
+    internal static class EsprimaExtensions
+    {
+        public static string GetKey<T>(this T expression) where T : Expression
+        {
+            if (expression is Literal literal)
+            {
+                return literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
+            }
+
+            if (expression is Identifier identifier)
+            {
+                return identifier.Name;
+            }
+
+            if (expression is StaticMemberExpression staticMemberExpression)
+            {
+                var obj = staticMemberExpression.Object.GetKey();
+                var property = staticMemberExpression.Property.GetKey();
+
+                if (obj == "Symbol")
+                {
+                    if (property == "iterator")
+                    {
+                        return GlobalSymbolRegistry.Iterator._value;
+                    }
+                    if (property == "toPrimitive")
+                    {
+                        return GlobalSymbolRegistry.ToPrimitive._value;
+                    }
+                }
+            }
+
+            return ExceptionHelper.ThrowArgumentException<string>("Unable to extract correct key");
+        }
+    }
+}

+ 1 - 1
Jint/Jint.csproj

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

+ 38 - 24
Jint/Native/Argument/ArgumentsInstance.cs

@@ -19,11 +19,11 @@ namespace Jint.Native.Argument
 
 
         private FunctionInstance _func;
         private FunctionInstance _func;
         private string[] _names;
         private string[] _names;
-        private JsValue[] _args;
+        internal JsValue[] _args;
         private EnvironmentRecord _env;
         private EnvironmentRecord _env;
         private bool _strict;
         private bool _strict;
 
 
-        private bool _initialized;
+        internal bool _initialized;
 
 
         internal ArgumentsInstance(Engine engine) : base(engine, objectClass: "Arguments")
         internal ArgumentsInstance(Engine engine) : base(engine, objectClass: "Arguments")
         {
         {
@@ -43,8 +43,7 @@ namespace Jint.Native.Argument
             _strict = strict;
             _strict = strict;
 
 
             _properties?.Clear();
             _properties?.Clear();
-            _intrinsicProperties?.Clear();
-
+            
             _initialized = false;
             _initialized = false;
         }
         }
 
 
@@ -57,49 +56,51 @@ namespace Jint.Native.Argument
 
 
             _initialized = true;
             _initialized = true;
 
 
-            var self = this;
-            var len = _args.Length;
-            self.SetOwnProperty("length", new PropertyDescriptor(len, PropertyFlag.NonEnumerable));
-            if (_args.Length > 0)
+            BuildProperties();
+        }
+
+        private void BuildProperties()
+        {
+            var args = _args;
+            SetOwnProperty("length", new PropertyDescriptor(args.Length, PropertyFlag.NonEnumerable));
+
+            ObjectInstance map = null;
+            if (args.Length > 0)
             {
             {
-                var map = Engine.Object.Construct(Arguments.Empty);
                 var mappedNamed = _mappedNamed.Value;
                 var mappedNamed = _mappedNamed.Value;
                 mappedNamed.Clear();
                 mappedNamed.Clear();
-                for (var indx = 0; indx < len; indx++)
+                for (var i = 0; i < (uint) args.Length; i++)
                 {
                 {
-                    var indxStr = TypeConverter.ToString(indx);
-                    var val = _args[indx];
-                    self.SetOwnProperty(indxStr, new PropertyDescriptor(val, PropertyFlag.ConfigurableEnumerableWritable));
-                    if (indx < _names.Length)
+                    var indxStr = TypeConverter.ToString(i);
+                    var val = args[i];
+                    SetOwnProperty(indxStr, new PropertyDescriptor(val, PropertyFlag.ConfigurableEnumerableWritable));
+                    if (i < _names.Length)
                     {
                     {
-                        var name = _names[indx];
+                        var name = _names[i];
                         if (!_strict && !mappedNamed.Contains(name))
                         if (!_strict && !mappedNamed.Contains(name))
                         {
                         {
+                            map = map ?? Engine.Object.Construct(Arguments.Empty);
                             mappedNamed.Add(name);
                             mappedNamed.Add(name);
                             map.SetOwnProperty(indxStr, new ClrAccessDescriptor(_env, Engine, name));
                             map.SetOwnProperty(indxStr, new ClrAccessDescriptor(_env, Engine, name));
                         }
                         }
                     }
                     }
                 }
                 }
-
-                // step 12
-                if (mappedNamed.Count > 0)
-                {
-                    self.ParameterMap = map;
-                }
             }
             }
 
 
+            ParameterMap = map;
+
             // step 13
             // step 13
             if (!_strict)
             if (!_strict)
             {
             {
-                self.SetOwnProperty("callee", new PropertyDescriptor(_func, PropertyFlag.NonEnumerable));
+                SetOwnProperty("callee", new PropertyDescriptor(_func, PropertyFlag.NonEnumerable));
             }
             }
             // step 14
             // step 14
             else
             else
             {
             {
                 var thrower = Engine.Function.ThrowTypeError;
                 var thrower = Engine.Function.ThrowTypeError;
                 const PropertyFlag flags = PropertyFlag.EnumerableSet | PropertyFlag.ConfigurableSet;
                 const PropertyFlag flags = PropertyFlag.EnumerableSet | PropertyFlag.ConfigurableSet;
-                self.DefineOwnProperty("caller", new GetSetPropertyDescriptor(get: thrower, set: thrower, flags), false);
-                self.DefineOwnProperty("callee", new GetSetPropertyDescriptor(get: thrower, set: thrower, flags), false);
+                DefineOwnProperty("caller", new GetSetPropertyDescriptor(get: thrower, set: thrower, flags), false);
+                DefineOwnProperty("callee", new GetSetPropertyDescriptor(get: thrower, set: thrower, flags), false);
             }
             }
         }
         }
 
 
@@ -233,5 +234,18 @@ namespace Jint.Native.Argument
 
 
             return base.Delete(propertyName, throwOnError);
             return base.Delete(propertyName, throwOnError);
         }
         }
+
+        internal void PersistArguments()
+        {
+            EnsureInitialized();
+
+            var args = _args;
+            var copiedArgs = new JsValue[args.Length];
+            System.Array.Copy(args, copiedArgs, args.Length);
+            _args = copiedArgs;
+
+            // should no longer expose arguments which is special name
+            ParameterMap = null;
+        }
     }
     }
 }
 }

+ 120 - 6
Jint/Native/Array/ArrayConstructor.cs

@@ -1,6 +1,7 @@
 using System.Collections;
 using System.Collections;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 using Jint.Native.Function;
 using Jint.Native.Function;
+using Jint.Native.Iterator;
 using Jint.Native.Object;
 using Jint.Native.Object;
 using Jint.Native.Symbol;
 using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime;
@@ -27,7 +28,7 @@ namespace Jint.Native.Array
             obj.Prototype = engine.Function.PrototypeObject;
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = ArrayPrototype.CreatePrototypeObject(engine, obj);
             obj.PrototypeObject = ArrayPrototype.CreatePrototypeObject(engine, obj);
 
 
-            obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));
+            obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.Configurable));
 
 
             // The initial value of Array.prototype is the Array prototype object
             // The initial value of Array.prototype is the Array prototype object
             obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
             obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
@@ -45,22 +46,135 @@ namespace Jint.Native.Array
         {
         {
             SetOwnProperty("from",new PropertyDescriptor(new ClrFunctionInstance(Engine, "from", From, 1, PropertyFlag.Configurable), 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("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));
+            SetOwnProperty("of", new PropertyDescriptor(new ClrFunctionInstance(Engine, "of", Of, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable));
         }
         }
 
 
         private JsValue From(JsValue thisObj, JsValue[] arguments)
         private JsValue From(JsValue thisObj, JsValue[] arguments)
         {
         {
             var source = arguments.At(0);
             var source = arguments.At(0);
-            if (source is IArrayLike arrayLike)
+            var mapFunction = arguments.At(1);
+            var callable = !mapFunction.IsUndefined() ? GetCallable(mapFunction) : null;
+            var thisArg = arguments.At(2);
+
+            if (source.IsNullOrUndefined())
+            {
+                ExceptionHelper.ThrowTypeError(_engine, "Cannot convert undefined or null to object");
+            }
+
+            if (source is JsString jsString)
+            {
+                var a = _engine.Array.ConstructFast((uint) jsString.Length);
+                for (int i = 0; i < jsString._value.Length; i++)
+                {
+                    a.SetIndexValue((uint) i, JsString.Create(jsString._value[i]), updateLength: false);
+                }
+                return a;
+            }
+
+            if (thisObj.IsNull() || !(source is ObjectInstance objectInstance))
+            {
+                return _engine.Array.ConstructFast(0);
+            }
+
+            if (objectInstance.IsArrayLike)
+            {
+                var operations = ArrayPrototype.ArrayOperations.For(objectInstance);
+
+                var length = operations.GetLength();
+
+                var a = _engine.Array.ConstructFast(length);
+                var args = !ReferenceEquals(callable, null)
+                    ? _engine._jsValueArrayPool.RentArray(2)
+                    : null;
+
+                uint n = 0;
+                for (uint i = 0; i < length; i++)
+                {
+                    JsValue jsValue;
+                    operations.TryGetValue(i, out var value);
+                    if (!ReferenceEquals(callable, null))
+                    {
+                        args[0] = value;
+                        args[1] = i;
+                        jsValue = callable.Call(thisArg, args);
+
+                        // function can alter data
+                        length = operations.GetLength();
+                    }
+                    else
+                    {
+                        jsValue = value;
+                    }
+                    a.SetIndexValue(i, jsValue, updateLength: false);
+                    n++;
+                }
+
+                if (!ReferenceEquals(callable, null))
+                {
+                    _engine._jsValueArrayPool.ReturnArray(args);
+                }
+
+                a.SetLength(length);
+                return a;
+            }
+
+            var instance = _engine.Array.ConstructFast(0);
+            if (objectInstance.TryGetIterator(_engine, out var iterator))
+            {
+                var protocol = new ArrayProtocol(_engine, thisArg, instance, iterator, callable);
+                protocol.Execute();
+            }
+
+            return instance;
+        }
+
+        private sealed class ArrayProtocol : IteratorProtocol
+        {
+            private readonly JsValue _thisArg;
+            private readonly ArrayInstance _instance;
+            private readonly ICallable _callable;
+            private long _index = -1;
+
+            public ArrayProtocol(
+                Engine engine, 
+                JsValue thisArg,
+                ArrayInstance instance,
+                IIterator iterator,
+                ICallable callable) : base(engine, iterator, 2)
+            {
+                _thisArg = thisArg;
+                _instance = instance;
+                _callable = callable;
+            }
+
+            protected override void ProcessItem(JsValue[] args, JsValue currentValue)
+            {
+                _index++;
+                var sourceValue = ExtractValueFromIteratorInstance(currentValue);
+                JsValue jsValue;
+                if (!ReferenceEquals(_callable, null))
+                {
+                    args[0] = sourceValue;
+                    args[1] = _index; 
+                    jsValue = _callable.Call(_thisArg, args);
+                }
+                else
+                {
+                    jsValue = sourceValue;
+                }
+
+                _instance.SetIndexValue((uint) _index, jsValue, updateLength: false);
+            }
+
+            protected override void IterationEnd()
             {
             {
-                arrayLike.ToArray(_engine);
+                _instance.SetLength((uint) (_index + 1));
             }
             }
-            return Undefined;
         }
         }
 
 
         private JsValue Of(JsValue thisObj, JsValue[] arguments)
         private JsValue Of(JsValue thisObj, JsValue[] arguments)
         {
         {
-            return Undefined;
+            return _engine.Array.Construct(arguments);
         }
         }
 
 
         private static JsValue Species(JsValue thisObject, JsValue[] arguments)
         private static JsValue Species(JsValue thisObject, JsValue[] arguments)

+ 59 - 8
Jint/Native/Array/ArrayInstance.cs

@@ -58,6 +58,10 @@ namespace Jint.Native.Array
             _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
             _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
         }
         }
 
 
+        internal override bool IsConcatSpreadable => !TryGetIsConcatSpreadable(out var isConcatSpreadable) || isConcatSpreadable;
+
+        internal override bool IsArrayLike => true;
+
         /// Implementation from ObjectInstance official specs as the one
         /// Implementation from ObjectInstance official specs as the one
         /// in ObjectInstance is optimized for the general case and wouldn't work
         /// in ObjectInstance is optimized for the general case and wouldn't work
         /// for arrays
         /// for arrays
@@ -520,6 +524,27 @@ namespace Jint.Native.Array
             return smallest;
             return smallest;
         }
         }
 
 
+        internal uint GetLargestIndex()
+        {
+            if (_dense != null)
+            {
+                return (uint) (_dense.Length - 1);
+            }
+
+            uint largest = uint.MaxValue;
+            // only try to help if collection reasonable small
+            if (_sparse.Count > 0 && _sparse.Count < 100)
+            {
+                largest = 0;
+                foreach (var key in _sparse.Keys)
+                {
+                    largest = System.Math.Max(key, largest);
+                }
+            }
+
+            return largest;
+        }
+
         public bool TryGetValue(uint index, out JsValue value)
         public bool TryGetValue(uint index, out JsValue value)
         {
         {
             value = Undefined;
             value = Undefined;
@@ -615,7 +640,7 @@ namespace Jint.Native.Array
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal void EnsureCapacity(uint capacity)
         internal void EnsureCapacity(uint capacity)
         {
         {
-            if (capacity > (uint) _dense.Length)
+            if (capacity <= MaxDenseArrayLength && capacity > (uint) _dense.Length)
             {
             {
                 // need to grow
                 // need to grow
                 var newArray = new PropertyDescriptor[capacity];
                 var newArray = new PropertyDescriptor[capacity];
@@ -656,7 +681,7 @@ namespace Jint.Native.Array
                 var desc = new PropertyDescriptor(arguments[i], PropertyFlag.ConfigurableEnumerableWritable);
                 var desc = new PropertyDescriptor(arguments[i], PropertyFlag.ConfigurableEnumerableWritable);
                 if (_dense != null && n < _dense.Length)
                 if (_dense != null && n < _dense.Length)
                 {
                 {
-                    _dense[(int) n] = desc;
+                    _dense[(uint) n] = desc;
                 }
                 }
                 else if (n < uint.MaxValue)
                 else if (n < uint.MaxValue)
                 {
                 {
@@ -720,8 +745,13 @@ namespace Jint.Native.Array
         internal override bool FindWithCallback(
         internal override bool FindWithCallback(
             JsValue[] arguments,
             JsValue[] arguments,
             out uint index,
             out uint index,
-            out JsValue value)
+            out JsValue value,
+            bool visitUnassigned)
         {
         {
+            var thisArg = arguments.At(1);
+            var callbackfn = arguments.At(0);
+            var callable = GetCallable(callbackfn);
+
             var len = GetLength();
             var len = GetLength();
             if (len == 0)
             if (len == 0)
             {
             {
@@ -730,15 +760,11 @@ namespace Jint.Native.Array
                 return false;
                 return false;
             }
             }
 
 
-            var callbackfn = arguments.At(0);
-            var thisArg = arguments.At(1);
-            var callable = GetCallable(callbackfn);
-
             var args = _engine._jsValueArrayPool.RentArray(3);
             var args = _engine._jsValueArrayPool.RentArray(3);
             args[2] = this;
             args[2] = this;
             for (uint k = 0; k < len; k++)
             for (uint k = 0; k < len; k++)
             {
             {
-                if (TryGetValue(k, out var kvalue))
+                if (TryGetValue(k, out var kvalue) || visitUnassigned)
                 {
                 {
                     args[0] = kvalue;
                     args[0] = kvalue;
                     args[1] = k;
                     args[1] = k;
@@ -758,5 +784,30 @@ namespace Jint.Native.Array
             value = Undefined;
             value = Undefined;
             return false;
             return false;
         }
         }
+        
+        public uint Length => GetLength();
+
+        public JsValue this[uint index]
+        {
+            get
+            {
+                TryGetValue(index, out var kValue);
+                return kValue;
+            }
+        }
+
+        internal ArrayInstance ToArray(Engine engine)
+        {
+            var length = GetLength();
+            var array = _engine.Array.ConstructFast(length);
+            for (uint i = 0; i < length; i++)
+            {
+                if (TryGetValue(i, out var kValue))
+                {
+                    array.SetIndexValue(i, kValue, updateLength: false);
+                }
+            }
+            return array;
+        }
     }
     }
 }
 }

File diff suppressed because it is too large
+ 460 - 147
Jint/Native/Array/ArrayPrototype.cs


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

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

+ 5 - 8
Jint/Native/Boolean/BooleanPrototype.cs

@@ -33,20 +33,17 @@ namespace Jint.Native.Boolean
 
 
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
         {
         {
-            var B = thisObj;
-            if (B.IsBoolean())
+            if (thisObj._type == Types.Boolean)
             {
             {
-                return B;
+                return thisObj;
             }
             }
 
 
-            var o = B.TryCast<BooleanInstance>();
-            if (!ReferenceEquals(o, null))
+            if (thisObj is BooleanInstance bi)
             {
             {
-                return o.PrimitiveValue;
+                return bi.PrimitiveValue;
             }
             }
 
 
-            ExceptionHelper.ThrowTypeError(Engine);
-            return null;
+            return ExceptionHelper.ThrowTypeError<JsValue>(Engine);
         }
         }
 
 
         private JsValue ToBooleanString(JsValue thisObj, JsValue[] arguments)
         private JsValue ToBooleanString(JsValue thisObj, JsValue[] arguments)

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

@@ -590,14 +590,14 @@ namespace Jint.Native.Date
         /// </summary>
         /// </summary>
         public static double TimeWithinDay(double t)
         public static double TimeWithinDay(double t)
         {
         {
-            if (t < 0)
-            {
-                return ((t % MsPerDay) + MsPerDay);
-            }
-            else
+            var result = t % MsPerDay;
+
+            if (result < 0)
             {
             {
-                return (t % MsPerDay);
+                result += MsPerDay;
             }
             }
+
+            return result;
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 4 - 7
Jint/Native/Function/EvalFunctionInstance.cs

@@ -1,5 +1,4 @@
 using Esprima;
 using Esprima;
-using Jint.Native.Argument;
 using Jint.Runtime;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Environments;
 using Jint.Runtime.Environments;
@@ -49,7 +48,7 @@ namespace Jint.Native.Function
                                 Engine.EnterExecutionContext(Engine.GlobalEnvironment, Engine.GlobalEnvironment, Engine.Global);
                                 Engine.EnterExecutionContext(Engine.GlobalEnvironment, Engine.GlobalEnvironment, Engine.Global);
                             }
                             }
 
 
-                            var lexicalEnvironment = Engine.ExecutionContext.LexicalEnvironment;
+                            var lexicalEnvironment = _engine.ExecutionContext.LexicalEnvironment;
                             if (StrictModeScope.IsStrictModeCode)
                             if (StrictModeScope.IsStrictModeCode)
                             {
                             {
                                 strictVarEnv = LexicalEnvironment.NewDeclarativeEnvironment(Engine, lexicalEnvironment);
                                 strictVarEnv = LexicalEnvironment.NewDeclarativeEnvironment(Engine, lexicalEnvironment);
@@ -66,12 +65,10 @@ namespace Jint.Native.Function
                             var result = _engine.ExecuteStatement(program);
                             var result = _engine.ExecuteStatement(program);
                             var value = result.GetValueOrDefault();
                             var value = result.GetValueOrDefault();
 
 
-                            // we can safely release arguments if they don't escape the scope
-                            if (argumentInstanceRented
-                                && lexicalEnvironment?._record is DeclarativeEnvironmentRecord der
-                                && !(result.Value is ArgumentsInstance))
+                            if (argumentInstanceRented)
                             {
                             {
-                                der.ReleaseArguments();
+                                lexicalEnvironment?._record?.FunctionWasCalled();
+                                _engine.ExecutionContext.VariableEnvironment?._record?.FunctionWasCalled();
                             }
                             }
 
 
                             if (result.Type == CompletionType.Throw)
                             if (result.Type == CompletionType.Throw)

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

@@ -136,12 +136,12 @@ namespace Jint.Native.Function
             var thisArg = arguments[0];
             var thisArg = arguments[0];
             var argArray = arguments[1];
             var argArray = arguments[1];
 
 
-            if (func == null)
+            if (func is null)
             {
             {
-                ExceptionHelper.ThrowTypeError(Engine);
+                return ExceptionHelper.ThrowTypeError<object>(Engine);
             }
             }
 
 
-            if (argArray.IsNull() || argArray.IsUndefined())
+            if (argArray.IsNullOrUndefined())
             {
             {
                 return func.Call(thisArg, Arguments.Empty);
                 return func.Call(thisArg, Arguments.Empty);
             }
             }

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

@@ -90,20 +90,20 @@ namespace Jint.Native.Function
             var thisArg = arguments.At(0);
             var thisArg = arguments.At(0);
             var argArray = arguments.At(1);
             var argArray = arguments.At(1);
 
 
-            if (func == null)
+            if (func is null)
             {
             {
-                ExceptionHelper.ThrowTypeError(Engine);
+                return ExceptionHelper.ThrowTypeError<JsValue>(Engine);
             }
             }
 
 
-            if (argArray.IsNull() || argArray.IsUndefined())
+            if (argArray.IsNullOrUndefined())
             {
             {
                 return func.Call(thisArg, Arguments.Empty);
                 return func.Call(thisArg, Arguments.Empty);
             }
             }
 
 
             var argArrayObj = argArray.TryCast<ObjectInstance>();
             var argArrayObj = argArray.TryCast<ObjectInstance>();
-            if (ReferenceEquals(argArrayObj, null))
+            if (argArrayObj is null)
             {
             {
-                ExceptionHelper.ThrowTypeError(Engine);
+                return ExceptionHelper.ThrowTypeError<JsValue>(Engine);
             }
             }
 
 
             var len = ((JsNumber) argArrayObj.Get("length"))._value;
             var len = ((JsNumber) argArrayObj.Get("length"))._value;
@@ -126,9 +126,9 @@ namespace Jint.Native.Function
         public JsValue CallImpl(JsValue thisObject, JsValue[] arguments)
         public JsValue CallImpl(JsValue thisObject, JsValue[] arguments)
         {
         {
             var func = thisObject.TryCast<ICallable>();
             var func = thisObject.TryCast<ICallable>();
-            if (func == null)
+            if (func is null)
             {
             {
-                ExceptionHelper.ThrowTypeError(Engine);
+                return ExceptionHelper.ThrowTypeError<JsValue>(Engine);
             }
             }
 
 
             return func.Call(arguments.At(0), arguments.Length == 0 ? arguments : arguments.Skip(1));
             return func.Call(arguments.At(0), arguments.Length == 0 ? arguments : arguments.Skip(1));

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

@@ -1,6 +1,5 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using Esprima.Ast;
 using Esprima.Ast;
-using Jint.Native.Argument;
 using Jint.Native.Object;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
@@ -86,7 +85,8 @@ namespace Jint.Native.Function
         /// <returns></returns>
         /// <returns></returns>
         public override JsValue Call(JsValue thisArg, JsValue[] arguments)
         public override JsValue Call(JsValue thisArg, JsValue[] arguments)
         {
         {
-            using (new StrictModeScope(Strict, true))
+            var strict = Strict || _engine._isStrict;
+            using (new StrictModeScope(strict, true))
             {
             {
                 // setup new execution context http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.3
                 // setup new execution context http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.3
                 JsValue thisBinding;
                 JsValue thisBinding;
@@ -124,12 +124,10 @@ namespace Jint.Native.Function
                     
                     
                     var value = result.GetValueOrDefault();
                     var value = result.GetValueOrDefault();
                     
                     
-                    // we can safely release arguments if they don't escape the scope
-                    if (argumentInstanceRented
-                        && _engine.ExecutionContext.LexicalEnvironment?._record is DeclarativeEnvironmentRecord der
-                        && !(result.Value is ArgumentsInstance))
+                    if (argumentInstanceRented)
                     {
                     {
-                        der.ReleaseArguments();
+                        _engine.ExecutionContext.LexicalEnvironment?._record?.FunctionWasCalled();
+                        _engine.ExecutionContext.VariableEnvironment?._record?.FunctionWasCalled();
                     }
                     }
 
 
                     if (result.Type == CompletionType.Throw)
                     if (result.Type == CompletionType.Throw)

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

@@ -77,7 +77,7 @@ namespace Jint.Native.Global
         public static JsValue ParseInt(JsValue thisObject, JsValue[] arguments)
         public static JsValue ParseInt(JsValue thisObject, JsValue[] arguments)
         {
         {
             string inputString = TypeConverter.ToString(arguments.At(0));
             string inputString = TypeConverter.ToString(arguments.At(0));
-            var s = StringPrototype.TrimEx(inputString, acceptMongolianVowelSeparator: false);
+            var s = StringPrototype.TrimEx(inputString);
 
 
             var sign = 1;
             var sign = 1;
             if (!System.String.IsNullOrEmpty(s))
             if (!System.String.IsNullOrEmpty(s))
@@ -178,7 +178,7 @@ namespace Jint.Native.Global
         public static JsValue ParseFloat(JsValue thisObject, JsValue[] arguments)
         public static JsValue ParseFloat(JsValue thisObject, JsValue[] arguments)
         {
         {
             var inputString = TypeConverter.ToString(arguments.At(0));
             var inputString = TypeConverter.ToString(arguments.At(0));
-            var trimmedString = StringPrototype.TrimStartEx(inputString, acceptMongolianVowelSeparator: false);
+            var trimmedString = StringPrototype.TrimStartEx(inputString);
 
 
             var sign = 1;
             var sign = 1;
             if (trimmedString.Length > 0)
             if (trimmedString.Length > 0)

+ 39 - 16
Jint/Native/Iterator/IteratorConstructor.cs

@@ -1,6 +1,5 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
-using Jint.Native.Array;
 using Jint.Native.Function;
 using Jint.Native.Function;
 using Jint.Native.Map;
 using Jint.Native.Map;
 using Jint.Native.Object;
 using Jint.Native.Object;
@@ -53,8 +52,7 @@ namespace Jint.Native.Iterator
         {
         {
             var instance = new IteratorInstance(Engine, enumerable)
             var instance = new IteratorInstance(Engine, enumerable)
             {
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
             };
             };
 
 
             return instance;
             return instance;
@@ -64,19 +62,17 @@ namespace Jint.Native.Iterator
         {
         {
             var instance = new IteratorInstance.ListIterator(Engine, enumerable)
             var instance = new IteratorInstance.ListIterator(Engine, enumerable)
             {
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
             };
             };
 
 
             return instance;
             return instance;
         }
         }
 
 
-        internal ObjectInstance Construct(ArrayInstance array)
+        internal ObjectInstance Construct(ObjectInstance array)
         {
         {
-            var instance = new IteratorInstance.ArrayIterator(Engine, array)
+            var instance = new IteratorInstance.ArrayLikeIterator(Engine, array)
             {
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
             };
             };
 
 
             return instance;
             return instance;
@@ -86,8 +82,7 @@ namespace Jint.Native.Iterator
         {
         {
             var instance = new IteratorInstance.MapIterator(Engine, map)
             var instance = new IteratorInstance.MapIterator(Engine, map)
             {
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
             };
             };
 
 
             return instance;
             return instance;
@@ -97,8 +92,7 @@ namespace Jint.Native.Iterator
         {
         {
             var instance = new IteratorInstance.SetIterator(Engine, set)
             var instance = new IteratorInstance.SetIterator(Engine, set)
             {
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
             };
             };
 
 
             return instance;
             return instance;
@@ -108,11 +102,40 @@ namespace Jint.Native.Iterator
         {
         {
             var instance = new IteratorInstance.SetEntryIterator(Engine, set)
             var instance = new IteratorInstance.SetEntryIterator(Engine, set)
             {
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
+            };
+
+            return instance;
+        }
+
+        internal ObjectInstance ConstructArrayLikeKeyIterator(ObjectInstance array)
+        {
+            var instance = new IteratorInstance.ArrayLikeKeyIterator(Engine, array)
+            {
+                Prototype = PrototypeObject, Extensible = true
+            };
+
+            return instance;
+        }
+
+        internal ObjectInstance ConstructArrayLikeValueIterator(ObjectInstance array)
+        {
+            var instance = new IteratorInstance.ArrayLikeValueIterator(Engine, array)
+            {
+                Prototype = PrototypeObject, Extensible = true
+            };
+
+            return instance;
+        }
+
+        public ObjectInstance Construct(string str)
+        {
+            var instance = new IteratorInstance.StringIterator(Engine, str)
+            {
+                Prototype = PrototypeObject, Extensible = true
             };
             };
 
 
             return instance;
             return instance;
         }
         }
     }
     }
-}
+}

+ 78 - 2
Jint/Native/Iterator/IteratorInstance.cs

@@ -109,13 +109,13 @@ namespace Jint.Native.Iterator
             }
             }
         }
         }
 
 
-        public class ArrayIterator : IteratorInstance
+        public class ArrayLikeIterator : IteratorInstance
         {
         {
             private readonly ArrayPrototype.ArrayOperations _array;
             private readonly ArrayPrototype.ArrayOperations _array;
             private uint? _end;
             private uint? _end;
             private uint _position;
             private uint _position;
 
 
-            public ArrayIterator(Engine engine, JsValue target) : base(engine)
+            public ArrayLikeIterator(Engine engine, JsValue target) : base(engine)
             {
             {
                 if (!(target is ObjectInstance objectInstance))
                 if (!(target is ObjectInstance objectInstance))
                 {
                 {
@@ -213,6 +213,57 @@ namespace Jint.Native.Iterator
                     return new  ValueIteratorPosition(_engine, value);
                     return new  ValueIteratorPosition(_engine, value);
                 }
                 }
 
 
+                _closed = true;
+                return ValueIteratorPosition.Done;
+            }
+        } 
+
+        public class ArrayLikeKeyIterator : IteratorInstance
+        {
+            private readonly ArrayPrototype.ArrayOperations _operations;
+            private uint _position;
+            private bool _closed;
+
+            public ArrayLikeKeyIterator(Engine engine, ObjectInstance objectInstance) : base(engine)
+            {
+                _operations = ArrayPrototype.ArrayOperations.For(objectInstance);
+                _position = 0;
+            }
+
+            public override ObjectInstance Next()
+            {
+                var length = _operations.GetLength();
+                if (!_closed && _position < length)
+                {
+                    return new  ValueIteratorPosition(_engine, _position++);
+                }
+
+                _closed = true;
+                return ValueIteratorPosition.Done;
+            }
+        }
+
+        public class ArrayLikeValueIterator : IteratorInstance
+        {
+            private readonly ArrayPrototype.ArrayOperations _operations;
+            private uint _position;
+            private bool _closed;
+
+            public ArrayLikeValueIterator(Engine engine, ObjectInstance objectInstance) : base(engine)
+            {
+                _operations = ArrayPrototype.ArrayOperations.For(objectInstance);
+                _position = 0;
+            }
+
+            public override ObjectInstance Next()
+            {
+                var length = _operations.GetLength();
+                if (!_closed && _position < length)
+                {
+                    _operations.TryGetValue(_position++, out var value);
+                    return new ValueIteratorPosition(_engine, value);
+                }
+
                 _closed = true;
                 _closed = true;
                 return ValueIteratorPosition.Done;
                 return ValueIteratorPosition.Done;
             }
             }
@@ -242,5 +293,30 @@ namespace Jint.Native.Iterator
                 }
                 }
             }
             }
         }
         }
+
+        internal class StringIterator : IteratorInstance
+        {
+            private readonly string _str;
+            private int _position;
+            private bool _closed;
+
+            public StringIterator(Engine engine, string str) : base(engine)
+            {
+                _str = str;
+                _position = 0;
+            }
+
+            public override ObjectInstance Next()
+            {
+                var length = _str.Length;
+                if (!_closed && _position < length)
+                {
+                    return new ValueIteratorPosition(_engine, _str[_position++]);
+                }
+
+                _closed = true;
+                return ValueIteratorPosition.Done;
+            }
+        }
     }
     }
 }
 }

+ 81 - 0
Jint/Native/Iterator/IteratorProtocol.cs

@@ -0,0 +1,81 @@
+using Jint.Native.Array;
+
+namespace Jint.Native.Iterator
+{
+    /// <summary>
+    /// Handles looping of iterator values, sub-classes can use to implement wanted actions.
+    /// </summary>
+    internal abstract class IteratorProtocol
+    {
+        protected readonly Engine _engine;
+        private readonly IIterator _iterator;
+        private readonly int _argCount;
+
+        protected IteratorProtocol(
+            Engine engine,
+            IIterator iterator,
+            int argCount)
+        {
+            _engine = engine;
+            _iterator = iterator;
+            _argCount = argCount;
+        }
+
+        internal void Execute()
+        {
+            var args = _engine._jsValueArrayPool.RentArray(_argCount);
+            try
+            {
+                do
+                {
+                    var item = _iterator.Next();
+                    if (item.TryGetValue("done", out var done) && done.AsBoolean())
+                    {
+                        break;
+                    }
+
+                    if (!item.TryGetValue("value", out var currentValue))
+                    {
+                        currentValue = JsValue.Undefined;
+                    }
+
+                    ProcessItem(args, currentValue);
+                } while (true);
+            }
+            catch
+            {
+                _iterator.Return();
+                throw;
+            }
+            finally
+            {
+                _engine._jsValueArrayPool.ReturnArray(args);
+            }
+
+            IterationEnd();
+        }
+
+        protected virtual void IterationEnd()
+        {
+        }
+
+        protected abstract void ProcessItem(JsValue[] args, JsValue currentValue);
+
+        protected 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;
+        }
+    }
+}

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

@@ -23,7 +23,7 @@ namespace Jint.Native.Iterator
 
 
         public void Configure()
         public void Configure()
         {
         {
-            FastAddProperty("next", new ClrFunctionInstance(Engine, "next", Next, 0), true, false, true);
+            FastAddProperty("next", new ClrFunctionInstance(Engine, "next", Next, 0, PropertyFlag.Configurable), true, false, true);
         }
         }
 
 
         private JsValue Next(JsValue thisObj, JsValue[] arguments)
         private JsValue Next(JsValue thisObj, JsValue[] arguments)

+ 5 - 5
Jint/Native/JsString.cs

@@ -5,7 +5,7 @@ using Jint.Runtime;
 
 
 namespace Jint.Native
 namespace Jint.Native
 {
 {
-    public class JsString : JsValue, IEquatable<JsString>, IArrayLike
+    public class JsString : JsValue, IEquatable<JsString>
     {
     {
         private const int AsciiMax = 126;
         private const int AsciiMax = 126;
         private static readonly JsString[] _charToJsValue;
         private static readonly JsString[] _charToJsValue;
@@ -99,10 +99,10 @@ namespace Jint.Native
 
 
         public ArrayInstance ToArray(Engine engine)
         public ArrayInstance ToArray(Engine engine)
         {
         {
-            var array = engine.Array.ConstructFast((uint) this._value.Length);
-            for (uint i = 0; i < _value.Length; ++i)
+            var array = engine.Array.ConstructFast((uint) _value.Length);
+            for (int i = 0; i < _value.Length; ++i)
             {
             {
-                array.SetIndexValue(i, this._value[0], updateLength: false);
+                array.SetIndexValue((uint) i, _value[i], updateLength: false);
             }
             }
 
 
             return array;
             return array;
@@ -135,7 +135,7 @@ namespace Jint.Native
                 return true;
                 return true;
             }
             }
 
 
-            return _value == other._value;
+            return _value == other.ToString();
         }
         }
 
 
         internal sealed class ConcatenatedString : JsString
         internal sealed class ConcatenatedString : JsString

+ 5 - 0
Jint/Native/JsSymbol.cs

@@ -16,6 +16,11 @@ namespace Jint.Native
             _value = value;
             _value = value;
         }
         }
 
 
+        internal JsSymbol(JsValue value) : base(Types.Symbol)
+        {
+            _value = value.IsUndefined() ? "" : TypeConverter.ToString(value);
+        }
+
         public override object ToObject()
         public override object ToObject()
         {
         {
             return _value;
             return _value;

+ 29 - 17
Jint/Native/JsValue.cs

@@ -42,6 +42,13 @@ namespace Jint.Native
             return _type == Types.Undefined;
             return _type == Types.Undefined;
         }
         }
 
 
+        [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal bool IsNullOrUndefined()
+        {
+            return _type < Types.Boolean;
+        }
+
         [Pure]
         [Pure]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsArray()
         public bool IsArray()
@@ -144,36 +151,41 @@ namespace Jint.Native
             }
             }
             return this as ArrayInstance;
             return this as ArrayInstance;
         }
         }
-        
+
         [Pure]
         [Pure]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal IIterator GetIterator()
+        internal IIterator GetIterator(Engine engine)
         {
         {
-            if (!(this is ObjectInstance oi))
+            if (!TryGetIterator(engine, out var iterator))
             {
             {
-                ExceptionHelper.ThrowArgumentException("The value is not iterable");
-                return null;
+                return ExceptionHelper.ThrowTypeError<IIterator>(engine, "The value is not iterable");
             }
             }
 
 
-            // TODO
-            if (!oi.TryGetValue(GlobalSymbolRegistry.Iterator._value, out var value))
+            return iterator;
+        }
+        
+        [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal bool TryGetIterator(Engine engine, out IIterator iterator)
+        {
+            if (!(this is ObjectInstance oi)
+                || !oi.TryGetValue(GlobalSymbolRegistry.Iterator._value, out var value)
+                || !(value is ICallable callable))
             {
             {
-                ExceptionHelper.ThrowArgumentException("The value is not iterable");
-                return null;
+                iterator = null;
+                return false;
             }
             }
 
 
-            if (!(value is ICallable callable))
+            var obj = (ObjectInstance) callable.Call(this, Arguments.Empty);
+            if (obj is IIterator i)
             {
             {
-                ExceptionHelper.ThrowArgumentException("The value is not iterable");
-                return null;
+                iterator = i;
             }
             }
-
-            var obj = (ObjectInstance) callable.Call(this, Arguments.Empty);
-            if (obj is IIterator iterator)
+            else
             {
             {
-                return iterator;
+                iterator = new IteratorInstance.ObjectWrapper(obj);
             }
             }
-            return new IteratorInstance.ObjectWrapper(obj);
+            return true;
         }
         }
 
 
         [Pure]
         [Pure]

+ 44 - 72
Jint/Native/Map/MapConstructor.cs

@@ -1,4 +1,5 @@
 using Jint.Native.Function;
 using Jint.Native.Function;
+using Jint.Native.Iterator;
 using Jint.Native.Object;
 using Jint.Native.Object;
 using Jint.Native.Symbol;
 using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime;
@@ -10,7 +11,7 @@ namespace Jint.Native.Map
 {
 {
     public sealed class MapConstructor : FunctionInstance, IConstructor
     public sealed class MapConstructor : FunctionInstance, IConstructor
     {
     {
-        private MapConstructor(Engine engine, string name) :  base(engine, name, null, null, false)
+        private MapConstructor(Engine engine, string name) : base(engine, name, null, null, false)
         {
         {
         }
         }
 
 
@@ -72,81 +73,52 @@ namespace Jint.Native.Map
                 Extensible = true
                 Extensible = true
             };
             };
 
 
-            if (arguments.Length > 0
-                && !arguments[0].IsUndefined()
-                && !arguments[0].IsNull())
+            if (arguments.Length > 0 && !arguments[0].IsNullOrUndefined())
             {
             {
-                var iterator = arguments.At(0).GetIterator();
-                if (iterator != null)
+                var iterator = arguments.At(0).GetIterator(_engine);
+                var mapProtocol = new MapProtocol(_engine, instance, iterator);
+                mapProtocol.Execute();
+            }
+
+            return instance;
+        }
+
+        private sealed class MapProtocol : IteratorProtocol
+        {
+            private readonly MapInstance _instance;
+            private readonly ICallable _setter;
+
+            public MapProtocol(
+                Engine engine,
+                MapInstance instance,
+                IIterator iterator) : base(engine, iterator, 2)
+            {
+                _instance = instance;
+                var setterProperty = instance.GetProperty("set");
+
+                if (setterProperty is null
+                    || !setterProperty.TryGetValue(instance, out var setterValue)
+                    || (_setter = setterValue as ICallable) is 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);
-                    }
+                    ExceptionHelper.ThrowTypeError(_engine, "set must be callable");
                 }
                 }
             }
             }
 
 
-            return instance;
+            protected override void ProcessItem(JsValue[] args, JsValue currentValue)
+            {
+                if (!(currentValue is ObjectInstance oi))
+                {
+                    ExceptionHelper.ThrowTypeError(_engine, "iterator's value must be an object");
+                    return;
+                }
+
+                oi.TryGetValue("0", out var key);
+                oi.TryGetValue("1", out var value);
+
+                args[0] = key;
+                args[1] = value;
+                _setter.Call(_instance, args);
+            }
         }
         }
     }
     }
-}
+}

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

@@ -127,7 +127,5 @@ namespace Jint.Native.Map
         {
         {
             return ((MapInstance) thisObj).Values();
             return ((MapInstance) thisObj).Values();
         }
         }
-
-
     }
     }
 }
 }

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

@@ -82,13 +82,17 @@ namespace Jint.Native.Number
 
 
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
         {
         {
-            var number = thisObj.TryCast<NumberInstance>();
-            if (ReferenceEquals(number, null))
+            if (thisObj is NumberInstance ni)
             {
             {
-                ExceptionHelper.ThrowTypeError(Engine);
+                return ni.NumberData;
+            }
+
+            if (thisObj is JsNumber)
+            {
+                return thisObj;
             }
             }
 
 
-            return number.NumberData;
+            return ExceptionHelper.ThrowTypeError<JsValue>(Engine);
         }
         }
 
 
         private const double Ten21 = 1e21;
         private const double Ten21 = 1e21;

+ 9 - 6
Jint/Native/Object/ObjectConstructor.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using Jint.Collections;
 using Jint.Native.Array;
 using Jint.Native.Array;
 using Jint.Native.Function;
 using Jint.Native.Function;
 using Jint.Native.String;
 using Jint.Native.String;
@@ -63,7 +64,7 @@ namespace Jint.Native.Object
                 return Construct(arguments);
                 return Construct(arguments);
             }
             }
 
 
-            if(arguments[0].IsNull() || arguments[0].IsUndefined())
+            if(arguments[0].IsNullOrUndefined())
             {
             {
                 return Construct(arguments);
                 return Construct(arguments);
             }
             }
@@ -108,7 +109,9 @@ namespace Jint.Native.Object
             {
             {
                 Extensible = true,
                 Extensible = true,
                 Prototype = Engine.Object.PrototypeObject,
                 Prototype = Engine.Object.PrototypeObject,
-                _properties =  propertyCount > 0 ? new Dictionary<string, PropertyDescriptor>(propertyCount) : null
+                _properties =  propertyCount > 0
+                    ? new StringDictionarySlim<PropertyDescriptor>(System.Math.Max(2, propertyCount))
+                    : null
             };
             };
 
 
             return obj;
             return obj;
@@ -136,7 +139,7 @@ namespace Jint.Native.Object
             }
             }
 
 
             var p = arguments.At(1);
             var p = arguments.At(1);
-            var name = TypeConverter.ToString(p);
+            var name = TypeConverter.ToPropertyKey(p);
 
 
             var desc = o.GetOwnProperty(name);
             var desc = o.GetOwnProperty(name);
             return PropertyDescriptor.FromPropertyDescriptor(Engine, desc);
             return PropertyDescriptor.FromPropertyDescriptor(Engine, desc);
@@ -214,7 +217,7 @@ namespace Jint.Native.Object
             }
             }
 
 
             var p = arguments.At(1);
             var p = arguments.At(1);
-            var name = TypeConverter.ToString(p);
+            var name = TypeConverter.ToPropertyKey(p);
 
 
             var attributes = arguments.At(2);
             var attributes = arguments.At(2);
             var desc = PropertyDescriptor.ToPropertyDescriptor(Engine, attributes);
             var desc = PropertyDescriptor.ToPropertyDescriptor(Engine, attributes);
@@ -298,14 +301,14 @@ namespace Jint.Native.Object
                 {
                 {
                     if (desc.Writable)
                     if (desc.Writable)
                     {
                     {
-                        var mutable = desc as PropertyDescriptor ?? new PropertyDescriptor(desc);
+                        var mutable = desc;
                         mutable.Writable = false;
                         mutable.Writable = false;
                         desc = mutable;
                         desc = mutable;
                     }
                     }
                 }
                 }
                 if (desc.Configurable)
                 if (desc.Configurable)
                 {
                 {
-                    var mutable = desc as PropertyDescriptor ?? new PropertyDescriptor(desc);
+                    var mutable = desc;
                     mutable.Configurable = false;
                     mutable.Configurable = false;
                     desc = mutable;
                     desc = mutable;
                 }
                 }

+ 91 - 52
Jint/Native/Object/ObjectInstance.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Dynamic;
 using System.Dynamic;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
+using Jint.Collections;
 using Jint.Native.Array;
 using Jint.Native.Array;
 using Jint.Native.Boolean;
 using Jint.Native.Boolean;
 using Jint.Native.Date;
 using Jint.Native.Date;
@@ -9,6 +10,7 @@ using Jint.Native.Function;
 using Jint.Native.Number;
 using Jint.Native.Number;
 using Jint.Native.RegExp;
 using Jint.Native.RegExp;
 using Jint.Native.String;
 using Jint.Native.String;
+using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Descriptors.Specialized;
@@ -18,8 +20,9 @@ namespace Jint.Native.Object
 {
 {
     public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     {
     {
-        protected Dictionary<string, PropertyDescriptor> _intrinsicProperties;
-        protected internal Dictionary<string, PropertyDescriptor> _properties;
+        private static readonly string ToPrimitiveSymbolName = GlobalSymbolRegistry.ToPrimitive._value;
+
+        internal StringDictionarySlim<PropertyDescriptor> _properties;
 
 
         private readonly string _class;
         private readonly string _class;
         protected readonly Engine _engine;
         protected readonly Engine _engine;
@@ -36,38 +39,6 @@ namespace Jint.Native.Object
 
 
         public Engine Engine => _engine;
         public Engine Engine => _engine;
 
 
-        protected bool TryGetIntrinsicValue(JsSymbol symbol, out JsValue value)
-        {
-            if (_intrinsicProperties != null && _intrinsicProperties.TryGetValue(symbol.AsSymbol(), out var descriptor))
-            {
-                value = descriptor.Value;
-                return true;
-            }
-
-            if (ReferenceEquals(Prototype, null))
-            {
-                value = Undefined;
-                return false;
-            }
-
-            return Prototype.TryGetIntrinsicValue(symbol, out value);
-        }
-
-        public void SetIntrinsicValue(string name, JsValue value, bool writable, bool enumerable, bool configurable)
-        {
-            SetOwnProperty(name, new PropertyDescriptor(value, writable, enumerable, configurable));
-        }
-
-        protected void SetIntrinsicValue(JsSymbol symbol, JsValue value, bool writable, bool enumerable, bool configurable)
-        {
-            if (_intrinsicProperties == null)
-            {
-                _intrinsicProperties = new Dictionary<string, PropertyDescriptor>();
-            }
-
-            _intrinsicProperties[symbol.AsSymbol()] = new PropertyDescriptor(value, writable, enumerable, configurable);
-        }
-
         /// <summary>
         /// <summary>
         /// The prototype of this object.
         /// The prototype of this object.
         /// </summary>
         /// </summary>
@@ -102,10 +73,10 @@ namespace Jint.Native.Object
         {
         {
             if (_properties == null)
             if (_properties == null)
             {
             {
-                _properties = new Dictionary<string, PropertyDescriptor>();
+                _properties = new StringDictionarySlim<PropertyDescriptor>();
             }
             }
 
 
-            _properties.Add(propertyName, descriptor);
+            _properties[propertyName] = descriptor;
         }
         }
 
 
         protected virtual bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
         protected virtual bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
@@ -204,7 +175,7 @@ namespace Jint.Native.Object
 
 
             if (_properties == null)
             if (_properties == null)
             {
             {
-                _properties = new Dictionary<string, PropertyDescriptor>();
+                _properties = new StringDictionarySlim<PropertyDescriptor>();
             }
             }
 
 
             _properties[propertyName] = desc;
             _properties[propertyName] = desc;
@@ -419,17 +390,34 @@ namespace Jint.Native.Object
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Hint is a String. Returns a default value for the
-        /// object.
+        /// Hint is a String. Returns a default value for the object.
         /// </summary>
         /// </summary>
-        /// <param name="hint"></param>
-        /// <returns></returns>
         public JsValue DefaultValue(Types hint)
         public JsValue DefaultValue(Types hint)
         {
         {
             EnsureInitialized();
             EnsureInitialized();
 
 
             if (hint == Types.String || (hint == Types.None && Class == "Date"))
             if (hint == Types.String || (hint == Types.None && Class == "Date"))
             {
             {
+                var jsValue = Get(ToPrimitiveSymbolName);
+                if (!jsValue.IsNullOrUndefined())
+                {
+                    if (jsValue is ICallable toPrimitive)
+                    {
+                        var str = toPrimitive.Call(this, Arguments.Empty);
+                        if (str.IsPrimitive())
+                        {
+                            return str;
+                        }
+
+                        if (str.IsObject())
+                        {
+                            return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Cannot convert object to primitive value");
+                        }
+                    }
+
+                    const string message = "'Value returned for property 'Symbol(Symbol.toPrimitive)' of object is not a function";
+                    return ExceptionHelper.ThrowTypeError<JsValue>(_engine, message);
+                }
                 if (Get("toString") is ICallable toString)
                 if (Get("toString") is ICallable toString)
                 {
                 {
                     var str = toString.Call(this, Arguments.Empty);
                     var str = toString.Call(this, Arguments.Empty);
@@ -453,6 +441,27 @@ namespace Jint.Native.Object
 
 
             if (hint == Types.Number || hint == Types.None)
             if (hint == Types.Number || hint == Types.None)
             {
             {
+                var jsValue = Get(ToPrimitiveSymbolName);
+                if (!jsValue.IsNullOrUndefined())
+                {
+                    if (jsValue is ICallable toPrimitive)
+                    {
+                        var val = toPrimitive.Call(this, Arguments.Empty);
+                        if (val.IsPrimitive())
+                        {
+                            return val;
+                        }
+
+                        if (val.IsObject())
+                        {
+                            return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Cannot convert object to primitive value");
+                        }
+                    }
+
+                    const string message = "'Value returned for property 'Symbol(Symbol.toPrimitive)' of object is not a function";
+                    return ExceptionHelper.ThrowTypeError<JsValue>(_engine, message);
+                }
+
                 if (Get("valueOf") is ICallable valueOf)
                 if (Get("valueOf") is ICallable valueOf)
                 {
                 {
                     var val = valueOf.Call(this, Arguments.Empty);
                     var val = valueOf.Call(this, Arguments.Empty);
@@ -830,7 +839,7 @@ namespace Jint.Native.Object
                 case "Arguments":
                 case "Arguments":
                 case "Object":
                 case "Object":
 #if __IOS__
 #if __IOS__
-                                IDictionary<string, object> o = new Dictionary<string, object>();
+                                IDictionary<string, object> o = new DictionarySlim<string, object>();
 #else
 #else
                     IDictionary<string, object> o = new ExpandoObject();
                     IDictionary<string, object> o = new ExpandoObject();
 #endif
 #endif
@@ -858,25 +867,35 @@ namespace Jint.Native.Object
         internal virtual bool FindWithCallback(
         internal virtual bool FindWithCallback(
             JsValue[] arguments,
             JsValue[] arguments,
             out uint index,
             out uint index,
-            out JsValue value)
+            out JsValue value,
+            bool visitUnassigned)
         {
         {
-            uint GetLength()
+            long GetLength()
             {
             {
                 var desc = GetProperty("length");
                 var desc = GetProperty("length");
                 var descValue = desc.Value;
                 var descValue = desc.Value;
+                double len;
                 if (desc.IsDataDescriptor() && !ReferenceEquals(descValue, null))
                 if (desc.IsDataDescriptor() && !ReferenceEquals(descValue, null))
                 {
                 {
-                    return TypeConverter.ToUint32(descValue);
+                    len = TypeConverter.ToNumber(descValue);
                 }
                 }
-
-                var getter = desc.Get ?? Undefined;
-                if (getter.IsUndefined())
+                else
                 {
                 {
-                    return 0;
+                    var getter = desc.Get ?? Undefined;
+                    if (getter.IsUndefined())
+                    {
+                        len = 0;
+                    }
+                    else
+                    {
+                        // if getter is not undefined it must be ICallable
+                        len = TypeConverter.ToNumber(((ICallable) getter).Call(this, Arguments.Empty));
+                    }
                 }
                 }
 
 
-                // if getter is not undefined it must be ICallable
-                return TypeConverter.ToUint32(((ICallable) getter).Call(this, Arguments.Empty));
+                return (long) System.Math.Max(
+                    0, 
+                    System.Math.Min(len, ArrayPrototype.ArrayOperations.MaxArrayLikeLength));
             }
             }
 
 
             bool TryGetValue(uint idx, out JsValue jsValue)
             bool TryGetValue(uint idx, out JsValue jsValue)
@@ -903,7 +922,7 @@ namespace Jint.Native.Object
             var length = GetLength();
             var length = GetLength();
             for (uint k = 0; k < length; k++)
             for (uint k = 0; k < length; k++)
             {
             {
-                if (TryGetValue(k, out var kvalue))
+                if (TryGetValue(k, out var kvalue) || visitUnassigned)
                 {
                 {
                     args[0] = kvalue;
                     args[0] = kvalue;
                     args[1] = k;
                     args[1] = k;
@@ -935,6 +954,26 @@ namespace Jint.Native.Object
             return null;
             return null;
         }
         }
 
 
+        internal virtual bool IsConcatSpreadable => TryGetIsConcatSpreadable(out var isConcatSpreadable) && isConcatSpreadable;
+
+        internal virtual bool IsArrayLike => TryGetValue("length", out var lengthValue)
+                                             && lengthValue.IsNumber()
+                                             && ((JsNumber) lengthValue)._value >= 0;
+
+        protected bool TryGetIsConcatSpreadable(out bool isConcatSpreadable)
+        {
+            isConcatSpreadable = false;
+            if (TryGetValue(GlobalSymbolRegistry.IsConcatSpreadable._value, out var isConcatSpreadableValue)
+                && !ReferenceEquals(isConcatSpreadableValue, null)
+                && !isConcatSpreadableValue.IsUndefined())
+            {
+                isConcatSpreadable = TypeConverter.ToBoolean(isConcatSpreadableValue);
+                return true;
+            }
+
+            return false;
+        }
+
         public override bool Equals(JsValue obj)
         public override bool Equals(JsValue obj)
         {
         {
             if (ReferenceEquals(null, obj))
             if (ReferenceEquals(null, obj))

+ 2 - 5
Jint/Native/Object/ObjectPrototype.cs

@@ -31,7 +31,7 @@ namespace Jint.Native.Object
 
 
         private JsValue PropertyIsEnumerable(JsValue thisObject, JsValue[] arguments)
         private JsValue PropertyIsEnumerable(JsValue thisObject, JsValue[] arguments)
         {
         {
-            var p = TypeConverter.ToString(arguments[0]);
+            var p = TypeConverter.ToPropertyKey(arguments[0]);
             var o = TypeConverter.ToObject(Engine, thisObject);
             var o = TypeConverter.ToObject(Engine, thisObject);
             var desc = o.GetOwnProperty(p);
             var desc = o.GetOwnProperty(p);
             if (desc == PropertyDescriptor.Undefined)
             if (desc == PropertyDescriptor.Undefined)
@@ -111,12 +111,9 @@ namespace Jint.Native.Object
         /// <summary>
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.4.5
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.4.5
         /// </summary>
         /// </summary>
-        /// <param name="thisObject"></param>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
         public JsValue HasOwnProperty(JsValue thisObject, JsValue[] arguments)
         public JsValue HasOwnProperty(JsValue thisObject, JsValue[] arguments)
         {
         {
-            var p = TypeConverter.ToString(arguments[0]);
+            var p = TypeConverter.ToPropertyKey(arguments[0]);
             var o = TypeConverter.ToObject(Engine, thisObject);
             var o = TypeConverter.ToObject(Engine, thisObject);
             var desc = o.GetOwnProperty(p);
             var desc = o.GetOwnProperty(p);
             return desc != PropertyDescriptor.Undefined;
             return desc != PropertyDescriptor.Undefined;

+ 28 - 60
Jint/Native/Set/SetConstructor.cs

@@ -1,5 +1,5 @@
-using Jint.Native.Array;
-using Jint.Native.Function;
+using Jint.Native.Function;
+using Jint.Native.Iterator;
 using Jint.Native.Object;
 using Jint.Native.Object;
 using Jint.Native.Symbol;
 using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime;
@@ -11,7 +11,7 @@ namespace Jint.Native.Set
 {
 {
     public sealed class SetConstructor : FunctionInstance, IConstructor
     public sealed class SetConstructor : FunctionInstance, IConstructor
     {
     {
-        private SetConstructor(Engine engine, string name) :  base(engine, name, null, null, false)
+        private SetConstructor(Engine engine, string name) : base(engine, name, null, null, false)
         {
         {
         }
         }
 
 
@@ -72,74 +72,42 @@ namespace Jint.Native.Set
                 Prototype = PrototypeObject,
                 Prototype = PrototypeObject,
                 Extensible = true
                 Extensible = true
             };
             };
-            if (arguments.Length > 0
-                && !arguments[0].IsUndefined()
-                && !arguments[0].IsNull())
+            if (arguments.Length > 0 && !arguments[0].IsNullOrUndefined())
             {
             {
-                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);
-                    }
-                }
+                var iterator = arguments.At(0).GetIterator(_engine);
+                var protocol = new SetProtocol(_engine, instance, iterator);
+                protocol.Execute();
             }
             }
 
 
             return instance;
             return instance;
         }
         }
 
 
-        private static JsValue ExtractValueFromIteratorInstance(JsValue jsValue)
+        private sealed class SetProtocol : IteratorProtocol
         {
         {
-            if (jsValue is ArrayInstance ai)
+            private readonly SetInstance _instance;
+            private readonly ICallable _adder;
+
+            public SetProtocol(
+                Engine engine,
+                SetInstance instance,
+                IIterator iterator) : base(engine, iterator, 1)
             {
             {
-                uint index = 0;
-                if (ai.GetLength() > 1)
+                _instance = instance;
+                var setterProperty = instance.GetProperty("add");
+
+                if (setterProperty is null
+                    || !setterProperty.TryGetValue(instance, out var setterValue)
+                    || (_adder = setterValue as ICallable) is null)
                 {
                 {
-                    index = 1;
+                    ExceptionHelper.ThrowTypeError(_engine, "add must be callable");
                 }
                 }
-                ai.TryGetValue(index, out var value);
-                return value;
             }
             }
 
 
-            return jsValue;
+            protected override void ProcessItem(JsValue[] args, JsValue currentValue)
+            {
+                args[0] = ExtractValueFromIteratorInstance(currentValue);
+                _adder.Call(_instance, args);
+            }
         }
         }
     }
     }
-}
+}

+ 57 - 5
Jint/Native/String/StringConstructor.cs

@@ -1,4 +1,5 @@
-using Jint.Native.Function;
+using System.Collections.Generic;
+using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
@@ -33,6 +34,7 @@ namespace Jint.Native.String
         public void Configure()
         public void Configure()
         {
         {
             SetOwnProperty("fromCharCode", new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromCharCode", FromCharCode, 1), PropertyFlag.NonEnumerable));
             SetOwnProperty("fromCharCode", new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromCharCode", FromCharCode, 1), PropertyFlag.NonEnumerable));
+            SetOwnProperty("fromCodePoint", new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromCodePoint", FromCodePoint, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable));
         }
         }
 
 
         private static JsValue FromCharCode(JsValue thisObj, JsValue[] arguments)
         private static JsValue FromCharCode(JsValue thisObj, JsValue[] arguments)
@@ -43,17 +45,62 @@ namespace Jint.Native.String
                 chars[i] = (char)TypeConverter.ToUint16(arguments[i]);
                 chars[i] = (char)TypeConverter.ToUint16(arguments[i]);
             }
             }
 
 
-            return new System.String(chars);
+            return JsString.Create(new string(chars));
+        }
+
+        private static JsValue FromCodePoint(JsValue thisObj, JsValue[] arguments)
+        {
+            var codeUnits = new List<JsValue>();
+            string result = "";
+            for (var i = 0; i < arguments.Length; i++ )
+            {
+                var codePoint = TypeConverter.ToNumber(arguments[i]);
+                if (codePoint < 0
+                    || codePoint > 0x10FFFF
+                    || double.IsInfinity(codePoint)
+                    || double.IsNaN(codePoint)
+                    || TypeConverter.ToInt32(codePoint) != codePoint)
+                {
+                    return ExceptionHelper.ThrowRangeErrorNoEngine<JsValue>("Invalid code point " + codePoint);
+                }
+
+                var point = (uint) codePoint;
+                if (point <= 0xFFFF)
+                {
+                    // BMP code point
+                    codeUnits.Add(JsNumber.Create(point));
+                }
+                else
+                {
+                    // Astral code point; split in surrogate halves
+                    // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
+                    point -= 0x10000;
+                    codeUnits.Add(JsNumber.Create((point >> 10) + 0xD800)); // highSurrogate
+                    codeUnits.Add(JsNumber.Create((point % 0x400) + 0xDC00)); // lowSurrogate
+                }
+                if (codeUnits.Count >= 0x3fff)
+                {
+                    result += FromCharCode(null, codeUnits.ToArray());
+                    codeUnits.Clear();
+                }
+            }
+
+            return result + FromCharCode(null, codeUnits.ToArray());
         }
         }
 
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
         {
             if (arguments.Length == 0)
             if (arguments.Length == 0)
             {
             {
-                return "";
+                return JsString.Empty;
             }
             }
 
 
-            return TypeConverter.ToString(arguments[0]);
+            var arg = arguments[0];
+            var str = arg is JsSymbol s
+                ? s.ToString()
+                : TypeConverter.ToString(arg);
+
+            return JsString.Create(str);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -63,7 +110,12 @@ namespace Jint.Native.String
         /// <returns></returns>
         /// <returns></returns>
         public ObjectInstance Construct(JsValue[] arguments)
         public ObjectInstance Construct(JsValue[] arguments)
         {
         {
-            return Construct(arguments.Length > 0 ? TypeConverter.ToString(arguments[0]) : "");
+            string value = "";
+            if (arguments.Length > 0)
+            {
+                value = TypeConverter.ToString(arguments[0]);
+            }
+            return Construct(value);
         }
         }
 
 
         public StringPrototype PrototypeObject { get; private set; }
         public StringPrototype PrototypeObject { get; private set; }

+ 260 - 47
Jint/Native/String/StringPrototype.cs

@@ -1,10 +1,12 @@
 using System;
 using System;
 using System.Linq;
 using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
+using System.Text;
 using Jint.Native.Array;
 using Jint.Native.Array;
 using Jint.Native.Function;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Native.Object;
 using Jint.Native.RegExp;
 using Jint.Native.RegExp;
+using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop;
@@ -35,29 +37,45 @@ namespace Jint.Native.String
 
 
         public void Configure()
         public void Configure()
         {
         {
-            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("toString", new ClrFunctionInstance(Engine, "toString", ToStringString, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("charAt", new ClrFunctionInstance(Engine, "charAt", CharAt, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("charCodeAt", new ClrFunctionInstance(Engine, "charCodeAt", CharCodeAt, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("codePointAt", new ClrFunctionInstance(Engine, "codePointAt", CodePointAt, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("concat", new ClrFunctionInstance(Engine, "concat", Concat, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("indexOf", new ClrFunctionInstance(Engine, "indexOf", IndexOf, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("endsWith", new ClrFunctionInstance(Engine, "endsWith", EndsWith, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("startsWith", new ClrFunctionInstance(Engine, "startsWith", StartsWith, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("lastIndexOf", new ClrFunctionInstance(Engine, "lastIndexOf", LastIndexOf, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("localeCompare", new ClrFunctionInstance(Engine, "localeCompare", LocaleCompare, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("match", new ClrFunctionInstance(Engine, "match", Match, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("replace", new ClrFunctionInstance(Engine, "replace", Replace, 2, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("search", new ClrFunctionInstance(Engine, "search", Search, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("slice", new ClrFunctionInstance(Engine, "slice", Slice, 2, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("split", new ClrFunctionInstance(Engine, "split", Split, 2, PropertyFlag.Configurable), true, false, true);
             FastAddProperty("substr", new ClrFunctionInstance(Engine, "substr", Substr, 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);
+            FastAddProperty("substring", new ClrFunctionInstance(Engine, "substring", Substring, 2, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("toLowerCase", new ClrFunctionInstance(Engine, "toLowerCase", ToLowerCase, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("toLocaleLowerCase", new ClrFunctionInstance(Engine, "toLocaleLowerCase", ToLocaleLowerCase, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("toUpperCase", new ClrFunctionInstance(Engine, "toUpperCase", ToUpperCase, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("toLocaleUpperCase", new ClrFunctionInstance(Engine, "toLocaleUpperCase", ToLocaleUpperCase, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("trim", new ClrFunctionInstance(Engine, "trim", Trim, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("trimStart", new ClrFunctionInstance(Engine, "trimStart", TrimStart, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("trimEnd", new ClrFunctionInstance(Engine, "trimEnd", TrimEnd, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("padStart", new ClrFunctionInstance(Engine, "padStart", PadStart, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("padEnd", new ClrFunctionInstance(Engine, "padEnd", PadEnd, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("includes", new ClrFunctionInstance(Engine, "includes", Includes, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("normalize", new ClrFunctionInstance(Engine, "normalize", Normalize, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("repeat", new ClrFunctionInstance(Engine, "repeat", Repeat, 1, PropertyFlag.Configurable), true, false, true);
+
+            FastAddProperty(GlobalSymbolRegistry.Iterator._value, new ClrFunctionInstance(Engine, "[Symbol.iterator]", Iterator, 0, PropertyFlag.Configurable), true, false, true);
+        }
+
+        private ObjectInstance Iterator(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(_engine, thisObj);
+            var str = TypeConverter.ToString(thisObj);
+            return _engine.Iterator.Construct(str);
         }
         }
 
 
         private JsValue ToStringString(JsValue thisObj, JsValue[] arguments)
         private JsValue ToStringString(JsValue thisObj, JsValue[] arguments)
@@ -74,16 +92,11 @@ namespace Jint.Native.String
         // http://msdn.microsoft.com/en-us/library/system.char.iswhitespace(v=vs.110).aspx
         // http://msdn.microsoft.com/en-us/library/system.char.iswhitespace(v=vs.110).aspx
         // http://en.wikipedia.org/wiki/Byte_order_mark
         // http://en.wikipedia.org/wiki/Byte_order_mark
         const char BOM_CHAR = '\uFEFF';
         const char BOM_CHAR = '\uFEFF';
-        const char MONGOLIAN_VOWEL_SEPARATOR = '\u180E';
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static bool IsWhiteSpaceEx(char c, bool acceptMongolianVowelSeparator = true)
+        internal static bool IsWhiteSpaceEx(char c)
         {
         {
-            return
-                char.IsWhiteSpace(c) ||
-                c == BOM_CHAR ||
-                // In .NET 4.6 this was removed from WS based on Unicode 6.3 changes
-                (acceptMongolianVowelSeparator && c == MONGOLIAN_VOWEL_SEPARATOR);
+            return char.IsWhiteSpace(c) || c == BOM_CHAR;
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -113,12 +126,12 @@ namespace Jint.Native.String
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static string TrimStartEx(string s, bool acceptMongolianVowelSeparator = true)
+        public static string TrimStartEx(string s)
         {
         {
             if (s.Length == 0)
             if (s.Length == 0)
                 return string.Empty;
                 return string.Empty;
 
 
-            if (!IsWhiteSpaceEx(s[0], acceptMongolianVowelSeparator))
+            if (!IsWhiteSpaceEx(s[0]))
                 return s;
                 return s;
 
 
             return TrimStart(s);
             return TrimStart(s);
@@ -139,9 +152,9 @@ namespace Jint.Native.String
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static string TrimEx(string s, bool acceptMongolianVowelSeparator = true)
+        public static string TrimEx(string s)
         {
         {
-            return TrimEndEx(TrimStartEx(s, acceptMongolianVowelSeparator));
+            return TrimEndEx(TrimStartEx(s));
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -152,26 +165,44 @@ namespace Jint.Native.String
             return TrimEx(s);
             return TrimEx(s);
         }
         }
 
 
-        private static JsValue ToLocaleUpperCase(JsValue thisObj, JsValue[] arguments)
+        private JsValue TrimStart(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+            var s = TypeConverter.ToString(thisObj);
+            return TrimStartEx(s);
+        }
+
+        private JsValue TrimEnd(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+            var s = TypeConverter.ToString(thisObj);
+            return TrimEndEx(s);
+        }
+
+        private JsValue ToLocaleUpperCase(JsValue thisObj, JsValue[] arguments)
         {
         {
+            TypeConverter.CheckObjectCoercible(_engine, thisObj);
             var s = TypeConverter.ToString(thisObj);
             var s = TypeConverter.ToString(thisObj);
             return s.ToUpper();
             return s.ToUpper();
         }
         }
 
 
-        private static JsValue ToUpperCase(JsValue thisObj, JsValue[] arguments)
+        private JsValue ToUpperCase(JsValue thisObj, JsValue[] arguments)
         {
         {
+            TypeConverter.CheckObjectCoercible(_engine, thisObj);
             var s = TypeConverter.ToString(thisObj);
             var s = TypeConverter.ToString(thisObj);
             return s.ToUpperInvariant();
             return s.ToUpperInvariant();
         }
         }
 
 
-        private static JsValue ToLocaleLowerCase(JsValue thisObj, JsValue[] arguments)
+        private JsValue ToLocaleLowerCase(JsValue thisObj, JsValue[] arguments)
         {
         {
+            TypeConverter.CheckObjectCoercible(_engine, thisObj);
             var s = TypeConverter.ToString(thisObj);
             var s = TypeConverter.ToString(thisObj);
             return s.ToLower();
             return s.ToLower();
         }
         }
 
 
-        private static JsValue ToLowerCase(JsValue thisObj, JsValue[] arguments)
+        private JsValue ToLowerCase(JsValue thisObj, JsValue[] arguments)
         {
         {
+            TypeConverter.CheckObjectCoercible(_engine, thisObj);
             var s = TypeConverter.ToString(thisObj);
             var s = TypeConverter.ToString(thisObj);
             return s.ToLowerInvariant();
             return s.ToLowerInvariant();
         }
         }
@@ -799,6 +830,30 @@ namespace Jint.Native.String
             return (double) s[position];
             return (double) s[position];
         }
         }
 
 
+        private JsValue CodePointAt(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            JsValue pos = arguments.Length > 0 ? arguments[0] : 0;
+            var s = TypeConverter.ToString(thisObj);
+            var position = (int)TypeConverter.ToInteger(pos);
+            if (position < 0 || position >= s.Length)
+            {
+                return Undefined;
+            }
+
+            var first = (double) s[position];
+            if (first >= 0xD800 && first <= 0xDBFF && s.Length > position + 1)
+            {
+                double second = s[position + 1];
+                if (second >= 0xDC00 && second <= 0xDFFF)
+                {
+                    return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
+                }
+            }
+            return first;
+        }
+
         private JsValue CharAt(JsValue thisObj, JsValue[] arguments)
         private JsValue CharAt(JsValue thisObj, JsValue[] arguments)
         {
         {
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
@@ -815,13 +870,17 @@ namespace Jint.Native.String
 
 
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
         {
         {
-            var s = thisObj.TryCast<StringInstance>();
-            if (ReferenceEquals(s, null))
+            if (thisObj is StringInstance si)
             {
             {
-                ExceptionHelper.ThrowTypeError(Engine);
+                return si.PrimitiveValue;
             }
             }
 
 
-            return s.PrimitiveValue;
+            if (thisObj is JsString)
+            {
+                return thisObj;
+            }
+
+            return ExceptionHelper.ThrowTypeError<JsValue>(Engine);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -856,10 +915,14 @@ namespace Jint.Native.String
         {
         {
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
             var targetLength = TypeConverter.ToInt32(arguments.At(0));
             var targetLength = TypeConverter.ToInt32(arguments.At(0));
-            var padString = TypeConverter.ToString(arguments.At(1, " "));
+            var padStringValue = arguments.At(1);
+
+            var padString = padStringValue.IsUndefined()
+                ? " "
+                : TypeConverter.ToString(padStringValue);
 
 
             var s = TypeConverter.ToString(thisObj);
             var s = TypeConverter.ToString(thisObj);
-            if (s.Length > targetLength)
+            if (s.Length > targetLength || padString.Length == 0)
             {
             {
                 return s;
                 return s;
             }
             }
@@ -870,15 +933,14 @@ namespace Jint.Native.String
                 padString = string.Join("", Enumerable.Repeat(padString, (targetLength / padString.Length) + 1));
                 padString = string.Join("", Enumerable.Repeat(padString, (targetLength / padString.Length) + 1));
             }
             }
 
 
-            return padStart ? $"{padString.Substring(0, targetLength)}{s}" : $"{s}{padString.Substring(0, targetLength)}";
+            return padStart
+                ? $"{padString.Substring(0, targetLength)}{s}"
+                : $"{s}{padString.Substring(0, targetLength)}";
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// https://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.startswith
         /// https://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.startswith
         /// </summary>
         /// </summary>
-        /// <param name="thisObj"></param>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
         private JsValue StartsWith(JsValue thisObj, JsValue[] arguments)
         private JsValue StartsWith(JsValue thisObj, JsValue[] arguments)
         {
         {
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
@@ -920,5 +982,156 @@ namespace Jint.Native.String
 
 
             return true;
             return true;
         }
         }
+
+        /// <summary>
+        /// https://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.endswith
+        /// </summary>
+        private JsValue EndsWith(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s = TypeConverter.ToString(thisObj);
+
+            var searchString = arguments.At(0);
+            if (ReferenceEquals(searchString, Null))
+            {
+                searchString = Native.Null.Text;
+            }
+            else
+            {
+                if (searchString.IsRegExp())
+                {
+                    ExceptionHelper.ThrowTypeError(Engine);
+                }
+            }
+
+            var searchStr = TypeConverter.ToString(searchString);
+
+            var len = s.Length;
+            var pos = TypeConverter.ToInt32(arguments.At(1, len));
+            var end = System.Math.Min(System.Math.Max(pos, 0), len);
+            var searchLength = searchStr.Length;
+            var start = end - searchLength;
+
+            if (start < 0)
+            {
+                return false;
+            }
+
+            for (var i = 0; i < searchLength; i++)
+            {
+                if (s[start + i] != searchStr[i])
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private JsValue Includes(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s1 = TypeConverter.ToString(thisObj);
+            var searchString = arguments.At(0);
+
+            if (searchString.IsRegExp())
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "First argument to String.prototype.includes must not be a regular expression");
+            }
+
+            var searchStr = TypeConverter.ToString(searchString);
+            double pos = 0;
+            if (arguments.Length > 1 && !arguments[1].IsUndefined())
+            {
+                pos = TypeConverter.ToInteger(arguments[1]);
+            }
+
+            if (searchStr.Length == 0)
+            {
+                return true;
+            }
+
+            if (pos >= s1.Length)
+            {
+                return false;
+            }
+
+            if (pos < 0)
+            {
+                pos = 0;
+            }
+
+            return s1.IndexOf(searchStr, (int) pos, StringComparison.Ordinal) > -1;
+        }
+
+        private JsValue Normalize(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+            var str = TypeConverter.ToString(thisObj);
+
+            var param = arguments.At(0);
+
+            var form = "NFC";
+           if (!param.IsUndefined())
+           {
+               form = TypeConverter.ToString(param);
+           }
+
+            var nf = NormalizationForm.FormC;
+            switch (form)
+            {
+                case "NFC":
+                    nf = NormalizationForm.FormC;
+                    break;
+                case "NFD":
+                    nf = NormalizationForm.FormD;
+                    break;
+                case "NFKC":
+                    nf = NormalizationForm.FormKC;
+                    break;
+                case "NFKD":
+                    nf = NormalizationForm.FormKD;
+                    break;
+                default:
+                    ExceptionHelper.ThrowRangeError(
+                        _engine,
+                        "The normalization form should be one of NFC, NFD, NFKC, NFKD.");
+                    break;
+            }
+
+            return str.Normalize(nf);
+        }
+
+        private JsValue Repeat(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+            var str = TypeConverter.ToString(thisObj);
+            var n = (int) TypeConverter.ToInteger(arguments.At(0));
+
+            if (n < 0)
+            {
+                return ExceptionHelper.ThrowRangeError<JsValue>(_engine, "Invalid count value");
+            }
+
+            if (n == 0 || str.Length == 0)
+            {
+                return JsString.Empty;
+            }
+
+            if (str.Length == 1)
+            {
+                return new string(str[0], n);
+            }
+
+            var sb = new StringBuilder(n * str.Length);
+            for (var i = 0; i < n; ++i)
+            {
+                sb.Append(str);
+            }
+
+            return sb.ToString();
+        }
     }
     }
 }
 }

+ 11 - 11
Jint/Native/Symbol/GlobalSymbolRegistry.cs

@@ -4,16 +4,16 @@ namespace Jint.Native.Symbol
 {
 {
     public class GlobalSymbolRegistry : Dictionary<string, JsSymbol>
     public class GlobalSymbolRegistry : Dictionary<string, JsSymbol>
     {
     {
-        public static JsSymbol HasInstance { get; } = new JsSymbol("Symbol.hasInstance");
-        public static JsSymbol IsConcatSpreadable { get; } = new JsSymbol("Symbol.isConcatSpreadable");
-        public static JsSymbol Iterator { get; } = new JsSymbol("Symbol.iterator");
-        public static JsSymbol Match { get; } = new JsSymbol("Symbol.match");
-        public static JsSymbol Replace { get; } = new JsSymbol("Symbol.replace");
-        public static JsSymbol Search { get; } = new JsSymbol("Symbol.search");
-        public static JsSymbol Species { get; } = new JsSymbol("Symbol.species");
-        public static JsSymbol Split { get; } = new JsSymbol("Symbol.split");
-        public static JsSymbol ToPrimitive { get; } = new JsSymbol("Symbol.toPrimitive");
-        public static JsSymbol ToStringTag { get; } = new JsSymbol("Symbol.toStringTag");
-        public static JsSymbol Unscopables { get; } = new JsSymbol("Symbol.unscopables");
+        public static readonly JsSymbol HasInstance = new JsSymbol("Symbol.hasInstance");
+        public static readonly JsSymbol IsConcatSpreadable = new JsSymbol("Symbol.isConcatSpreadable");
+        public static readonly JsSymbol Iterator = new JsSymbol("Symbol.iterator");
+        public static readonly JsSymbol Match = new JsSymbol("Symbol.match");
+        public static readonly JsSymbol Replace = new JsSymbol("Symbol.replace");
+        public static readonly JsSymbol Search = new JsSymbol("Symbol.search");
+        public static readonly JsSymbol Species = new JsSymbol("Symbol.species");
+        public static readonly JsSymbol Split = new JsSymbol("Symbol.split");
+        public static readonly JsSymbol ToPrimitive = new JsSymbol("Symbol.toPrimitive");
+        public static readonly JsSymbol ToStringTag = new JsSymbol("Symbol.toStringTag");
+        public static readonly JsSymbol Unscopables = new JsSymbol("Symbol.unscopables");
     }
     }
 }
 }

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

@@ -71,7 +71,7 @@ namespace Jint.Native.Symbol
                 return descString;
                 return descString;
             }
             }
 
 
-            var value = new JsSymbol(TypeConverter.ToString(description));
+            var value = new JsSymbol(description);
             return value;
             return value;
         }
         }
 
 

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

@@ -32,8 +32,8 @@ namespace Jint.Native.Symbol
             FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOf", ValueOf), true, false, true);
             FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOf", ValueOf), true, false, true);
             FastAddProperty("toStringTag", new JsString("Symbol"), false, false, true);
             FastAddProperty("toStringTag", new JsString("Symbol"), false, false, true);
 
 
-            SetIntrinsicValue(GlobalSymbolRegistry.ToPrimitive, new ClrFunctionInstance(Engine, "toPrimitive", ToPrimitive), false, false, true);
-            SetIntrinsicValue(GlobalSymbolRegistry.ToStringTag, new JsString("Symbol"), false, false, true);
+            FastAddProperty(GlobalSymbolRegistry.ToPrimitive._value, new ClrFunctionInstance(Engine, "toPrimitive", ToPrimitive), false, false, true);
+            FastAddProperty(GlobalSymbolRegistry.ToStringTag._value, new JsString("Symbol"), false, false, true);
         }
         }
 
 
         public string SymbolDescriptiveString(JsSymbol sym)
         public string SymbolDescriptiveString(JsSymbol sym)

+ 37 - 20
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using Esprima.Ast;
 using Esprima.Ast;
+using Jint.Collections;
 using Jint.Native;
 using Jint.Native;
 using Jint.Native.Argument;
 using Jint.Native.Argument;
 using Jint.Native.Function;
 using Jint.Native.Function;
@@ -13,7 +14,7 @@ namespace Jint.Runtime.Environments
     /// </summary>
     /// </summary>
     public sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
     public sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
     {
     {
-        private StructDictionary<Binding> _dictionary;
+        private StringDictionarySlim<Binding> _dictionary;
         private bool _set;
         private bool _set;
         private string _key;
         private string _key;
         private Binding _value;
         private Binding _value;
@@ -21,6 +22,9 @@ namespace Jint.Runtime.Environments
         private const string BindingNameArguments = "arguments";
         private const string BindingNameArguments = "arguments";
         private Binding _argumentsBinding;
         private Binding _argumentsBinding;
 
 
+        // false = not accessed, true = accessed, null = values copied
+        private bool? _argumentsBindingWasAccessed = false;
+
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         {
         {
         }
         }
@@ -31,17 +35,20 @@ namespace Jint.Runtime.Environments
             {
             {
                 if (_dictionary == null)
                 if (_dictionary == null)
                 {
                 {
-                    _dictionary = new StructDictionary<Binding>();
+                    _dictionary = new StringDictionarySlim<Binding>();
                 }
                 }
 
 
-                _dictionary.TryInsert(_key, _value, InsertionBehavior.OverwriteExisting);
+                _dictionary[_key] = _value;
             }
             }
 
 
             _set = true;
             _set = true;
             _key = key;
             _key = key;
             _value = value;
             _value = value;
 
 
-            _dictionary?.TryInsert(key, value, InsertionBehavior.OverwriteExisting);
+            if (_dictionary != null)
+            {
+                _dictionary[key] = value;
+            }
         }
         }
 
 
         private ref Binding GetExistingItem(string key)
         private ref Binding GetExistingItem(string key)
@@ -53,10 +60,11 @@ namespace Jint.Runtime.Environments
 
 
             if (key.Length == 9 && key == BindingNameArguments)
             if (key.Length == 9 && key == BindingNameArguments)
             {
             {
+                _argumentsBindingWasAccessed = true;
                 return ref _argumentsBinding;
                 return ref _argumentsBinding;
             }
             }
 
 
-            return ref _dictionary.GetItem(key);
+            return ref _dictionary[key];
         }
         }
 
 
         private bool ContainsKey(string key)
         private bool ContainsKey(string key)
@@ -91,7 +99,6 @@ namespace Jint.Runtime.Environments
             {
             {
                 _dictionary?.Remove(key);
                 _dictionary?.Remove(key);
             }
             }
-
         }
         }
 
 
         private bool TryGetValue(string key, out Binding value)
         private bool TryGetValue(string key, out Binding value)
@@ -103,7 +110,7 @@ namespace Jint.Runtime.Environments
                 return true;
                 return true;
             }
             }
 
 
-            return _dictionary?.TryGetValue(key, out value) == true;
+            return _dictionary != null && _dictionary.TryGetValue(key, out value);
         }
         }
 
 
         public override bool HasBinding(string name)
         public override bool HasBinding(string name)
@@ -200,23 +207,11 @@ namespace Jint.Runtime.Environments
                 keys[n++] = BindingNameArguments;
                 keys[n++] = BindingNameArguments;
             }
             }
 
 
-            if (_dictionary != null)
-            {
-                foreach (var key in _dictionary.Keys)
-                {
-                    keys[n++] = key;
-                }
-            }
+            _dictionary?.Keys.CopyTo(keys, n);
 
 
             return keys;
             return keys;
         }
         }
 
 
-        internal void ReleaseArguments()
-        {
-            _engine._argumentsInstancePool.Return(_argumentsBinding.Value as ArgumentsInstance);
-            _argumentsBinding = default;
-        }
-
         /// <summary>
         /// <summary>
         /// Optimized version for function calls.
         /// Optimized version for function calls.
         /// </summary>
         /// </summary>
@@ -301,5 +296,27 @@ namespace Jint.Runtime.Environments
                 }
                 }
             }
             }
         }
         }
+        
+        internal override void FunctionWasCalled()
+        {
+            // we can safely release arguments only if it doesn't have possibility to escape the scope
+            // so check if someone ever accessed it
+            if (!(_argumentsBinding.Value is ArgumentsInstance argumentsInstance))
+            {
+                return;
+            }
+            
+            if (!argumentsInstance._initialized && _argumentsBindingWasAccessed == false)
+            {
+                _engine._argumentsInstancePool.Return(argumentsInstance);
+                _argumentsBinding = default;
+            }
+            else if (_argumentsBindingWasAccessed != null && argumentsInstance._args.Length > 0)
+            {
+                // we need to ensure we hold on to arguments given
+                argumentsInstance.PersistArguments();
+                _argumentsBindingWasAccessed = null;
+            }
+        }
     }
     }
 }
 }

+ 5 - 0
Jint/Runtime/Environments/EnvironmentRecord.cs

@@ -77,6 +77,11 @@ namespace Jint.Runtime.Environments
             return false;
             return false;
         }
         }
 
 
+        /// <summary>
+        /// Informs whether arguments instance was accessed and maybe thus stored,
+        /// which makes it unsuitable for pooling and reuse.
+        /// </summary>
+        internal abstract void FunctionWasCalled();
     }
     }
 }
 }
 
 

+ 4 - 0
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -78,5 +78,9 @@ namespace Jint.Runtime.Environments
 
 
             return ArrayExt.Empty<string>();
             return ArrayExt.Empty<string>();
         }
         }
+
+        internal override void FunctionWasCalled()
+        {
+        }
     }
     }
 }
 }

+ 26 - 0
Jint/Runtime/ExceptionHelper.cs

@@ -5,6 +5,12 @@ namespace Jint.Runtime
 {
 {
     internal static class ExceptionHelper
     internal static class ExceptionHelper
     {
     {
+        public static T ThrowSyntaxError<T>(Engine engine, string message = null)
+        {
+            ThrowSyntaxError(engine, message);
+            return default;
+        }
+
         public static void ThrowSyntaxError(Engine engine, string message = null)
         public static void ThrowSyntaxError(Engine engine, string message = null)
         {
         {
             throw new JavaScriptException(engine.SyntaxError, message);
             throw new JavaScriptException(engine.SyntaxError, message);
@@ -31,6 +37,11 @@ namespace Jint.Runtime
             throw new JavaScriptException(engine.ReferenceError, message);
             throw new JavaScriptException(engine.ReferenceError, message);
         }
         }
 
 
+        public static T ThrowTypeErrorNoEngine<T>(string message = null, Exception exception = null)
+        {
+            throw new TypeErrorException(message);
+        }
+
         public static T ThrowTypeError<T>(Engine engine, string message = null, Exception exception = null)
         public static T ThrowTypeError<T>(Engine engine, string message = null, Exception exception = null)
         {
         {
             ThrowTypeError(engine, message, exception);
             ThrowTypeError(engine, message, exception);
@@ -42,6 +53,16 @@ namespace Jint.Runtime
             throw new JavaScriptException(engine.TypeError, message, exception);
             throw new JavaScriptException(engine.TypeError, message, exception);
         }
         }
 
 
+        public static T ThrowRangeError<T>(Engine engine, string message = null)
+        {
+            throw new JavaScriptException(engine.RangeError, message);
+        }
+
+        public static T ThrowRangeErrorNoEngine<T>(string message)
+        {
+            throw new RangeErrorException(message);
+        }
+
         public static void ThrowRangeError(Engine engine, string message = null)
         public static void ThrowRangeError(Engine engine, string message = null)
         {
         {
             throw new JavaScriptException(engine.RangeError, message);
             throw new JavaScriptException(engine.RangeError, message);
@@ -77,6 +98,11 @@ namespace Jint.Runtime
             throw new ArgumentOutOfRangeException();
             throw new ArgumentOutOfRangeException();
         }
         }
 
 
+        public static T ThrowNotSupportedException<T>(string message = null)
+        {
+            throw new NotSupportedException(message);
+        }
+
         public static void ThrowNotSupportedException(string message = null)
         public static void ThrowNotSupportedException(string message = null)
         {
         {
             throw new NotSupportedException(message);
             throw new NotSupportedException(message);

+ 10 - 8
Jint/Runtime/ExpressionIntepreter.cs

@@ -346,6 +346,9 @@ namespace Jint.Runtime
                 case BinaryOperator.UnsignedRightShift:
                 case BinaryOperator.UnsignedRightShift:
                     return (uint)TypeConverter.ToInt32(left) >> (int)(TypeConverter.ToUint32(right) & 0x1F);
                     return (uint)TypeConverter.ToInt32(left) >> (int)(TypeConverter.ToUint32(right) & 0x1F);
 
 
+                case BinaryOperator.Exponentiation:
+                    return Math.Pow(TypeConverter.ToNumber(left), TypeConverter.ToNumber(right));
+
                 case BinaryOperator.InstanceOf:
                 case BinaryOperator.InstanceOf:
                     var f = right.TryCast<FunctionInstance>();
                     var f = right.TryCast<FunctionInstance>();
                     if (ReferenceEquals(f, null))
                     if (ReferenceEquals(f, null))
@@ -657,7 +660,7 @@ namespace Jint.Runtime
 
 
                     if (function == null)
                     if (function == null)
                     {
                     {
-                        ExceptionHelper.ThrowSyntaxError(_engine);
+                        return ExceptionHelper.ThrowSyntaxError<JsValue>(_engine);
                     }
                     }
 
 
                     ScriptFunctionInstance functionInstance;
                     ScriptFunctionInstance functionInstance;
@@ -744,7 +747,7 @@ namespace Jint.Runtime
             {
             {
                 var propertyNameReference = _engine.EvaluateExpression(memberExpression.Property);
                 var propertyNameReference = _engine.EvaluateExpression(memberExpression.Property);
                 var propertyNameValue = _engine.GetValue(propertyNameReference, true);
                 var propertyNameValue = _engine.GetValue(propertyNameReference, true);
-                propertyNameString = TypeConverter.ToString(propertyNameValue);
+                propertyNameString = TypeConverter.ToPropertyKey(propertyNameValue);
             }
             }
 
 
             TypeConverter.CheckObjectCoercible(_engine, baseValue, memberExpression, baseReference);
             TypeConverter.CheckObjectCoercible(_engine, baseValue, memberExpression, baseReference);
@@ -765,8 +768,7 @@ namespace Jint.Runtime
                 _engine,
                 _engine,
                 functionExpression,
                 functionExpression,
                 funcEnv,
                 funcEnv,
-                functionExpression.Strict
-                );
+                functionExpression.Strict);
 
 
             if (!string.IsNullOrEmpty(functionExpression.Id?.Name))
             if (!string.IsNullOrEmpty(functionExpression.Id?.Name))
             {
             {
@@ -846,10 +848,9 @@ namespace Jint.Runtime
                 }
                 }
             }
             }
 
 
-            var callable = func as ICallable;
-            if (callable == null)
+            if (!(func is ICallable callable))
             {
             {
-                ExceptionHelper.ThrowTypeError(_engine);
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine);
             }
             }
 
 
             var thisObject = Undefined.Instance;
             var thisObject = Undefined.Instance;
@@ -1057,7 +1058,8 @@ namespace Jint.Runtime
                     {
                     {
                         return "object";
                         return "object";
                     }
                     }
-                    switch (v.Type)
+
+                    switch (v._type)
                     {
                     {
                         case Types.Boolean: return "boolean";
                         case Types.Boolean: return "boolean";
                         case Types.Number: return "number";
                         case Types.Number: return "number";

+ 1 - 1
Jint/Runtime/JavaScriptException.cs

@@ -49,7 +49,7 @@ namespace Jint.Runtime
                     if (index != 0)
                     if (index != 0)
                         sb.Append(", ");
                         sb.Append(", ");
                     var arg = cse.CallExpression.Arguments[index];
                     var arg = cse.CallExpression.Arguments[index];
-                    if (arg is PropertyKey pke)
+                    if (arg is Expression pke)
                         sb.Append(pke.GetKey());
                         sb.Append(pke.GetKey());
                     else
                     else
                         sb.Append(arg);
                         sb.Append(arg);

+ 14 - 0
Jint/Runtime/RangeErrorException.cs

@@ -0,0 +1,14 @@
+using System;
+
+namespace Jint.Runtime
+{
+    /// <summary>
+    /// Workaround for situation where engine is not easily accessible.
+    /// </summary>
+    internal sealed class RangeErrorException : Exception
+    {
+        public RangeErrorException(string message) : base(message)
+        {
+        }
+    }
+}

+ 31 - 1
Jint/Runtime/StatementInterpreter.cs

@@ -213,7 +213,7 @@ namespace Jint.Runtime
 
 
             var varRef = _engine.EvaluateExpression(identifier) as Reference;
             var varRef = _engine.EvaluateExpression(identifier) as Reference;
             var experValue = _engine.GetValue(_engine.EvaluateExpression(forInStatement.Right), true);
             var experValue = _engine.GetValue(_engine.EvaluateExpression(forInStatement.Right), true);
-            if (experValue.IsUndefined() || experValue.IsNull())
+            if (experValue.IsNullOrUndefined())
             {
             {
                 return new Completion(CompletionType.Normal, null, null);
                 return new Completion(CompletionType.Normal, null, null);
             }
             }
@@ -343,6 +343,16 @@ namespace Jint.Runtime
             {
             {
                 c = new Completion(CompletionType.Throw, e.Error, null, withStatement.Location);
                 c = new Completion(CompletionType.Throw, e.Error, null, withStatement.Location);
             }
             }
+            catch (TypeErrorException e)
+            {
+                var error = _engine.TypeError.Construct(new JsValue[] {e.Message});
+                c = new Completion(CompletionType.Throw, error, null, withStatement.Location);
+            }
+            catch (RangeErrorException e)
+            {
+                var error = _engine.RangeError.Construct(new JsValue[] {e.Message});
+                c = new Completion(CompletionType.Throw, error, null, withStatement.Location);
+            }
             finally
             finally
             {
             {
                 _engine.UpdateLexicalEnvironment(oldEnv);
                 _engine.UpdateLexicalEnvironment(oldEnv);
@@ -456,6 +466,16 @@ namespace Jint.Runtime
                 var completion = new Completion(CompletionType.Throw, v.Error, null, v.Location ?? s?.Location);
                 var completion = new Completion(CompletionType.Throw, v.Error, null, v.Location ?? s?.Location);
                 return completion;
                 return completion;
             }
             }
+            catch (TypeErrorException e)
+            {
+                var error = _engine.TypeError.Construct(new JsValue[] {e.Message});
+                c = new Completion(CompletionType.Throw, error, null, s?.Location);
+            }
+            catch (RangeErrorException e)
+            {
+                var error = _engine.RangeError.Construct(new JsValue[] {e.Message});
+                c = new Completion(CompletionType.Throw, error, null, s?.Location);
+            }
 
 
             return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
             return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
         }
         }
@@ -482,6 +502,16 @@ namespace Jint.Runtime
             {
             {
                 return new Completion(CompletionType.Throw, v.Error, null, v.Location ?? s?.Location);
                 return new Completion(CompletionType.Throw, v.Error, null, v.Location ?? s?.Location);
             }
             }
+            catch (TypeErrorException e)
+            {
+                var error = _engine.TypeError.Construct(new JsValue[] {e.Message});
+                return new Completion(CompletionType.Throw, error, null, s?.Location);
+            }
+            catch (RangeErrorException e)
+            {
+                var error = _engine.RangeError.Construct(new JsValue[] {e.Message});
+                return new Completion(CompletionType.Throw, error, null, s?.Location);
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 0 - 802
Jint/Runtime/StructDictionary.cs

@@ -1,802 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Reflection;
-
-namespace Jint.Runtime
-{
-    internal enum InsertionBehavior : byte
-    {
-        /// <summary>
-        /// Specifies that an existing entry with the same key should be overwritten if encountered.
-        /// </summary>
-        OverwriteExisting = 1,
-
-        /// <summary>
-        /// Specifies that if an existing entry with the same key is encountered, an exception should be thrown.
-        /// </summary>
-        ThrowOnExisting = 2,
-        
-        /// <summary>
-        /// Specifies that if existing entry with the same key is encountered, the update will be skipped.
-        /// </summary>
-        SkipIfExists = 3
-    }
-
-    /// <summary>
-    /// Taken from .NET source to create performant specialized dictionary containing structs for Jint.
-    /// </summary>
-    internal sealed class StructDictionary<TValue> where TValue : struct
-    {
-        private static readonly EqualityComparer<string> _comparer; 
-        
-        static StructDictionary()
-        {
-            // we want to use same comparer as default dictionary impl that is hidden from us
-            // .NET Core uses non-randomized hash code generation that is faster than default
-            try
-            {
-                Dictionary<string, TValue> dictionary = new Dictionary<string, TValue>();
-                var field = dictionary.GetType().GetField("_comparer", BindingFlags.Instance | BindingFlags.NonPublic);
-                field = field ?? dictionary.GetType().GetField("comparer", BindingFlags.Instance | BindingFlags.NonPublic);
-                _comparer = field?.GetValue(dictionary) as EqualityComparer<string> ?? EqualityComparer<string>.Default;
-            }
-            catch
-            {
-                _comparer = EqualityComparer<string>.Default;
-            }
-        }
-        
-        private struct Entry
-        {
-            public int hashCode; // Lower 31 bits of hash code, -1 if unused
-            public int next; // Index of next entry, -1 if last
-            public string key; // Key of entry
-            public TValue value; // Value of entry
-        }
-
-        private int[] _buckets;
-        private Entry[] _entries;
-        private int _count;
-        private int _freeList;
-        private int _freeCount;
-        private KeyCollection _keys;
-        private ValueCollection _values;
-
-        public StructDictionary() : this(0)
-        {
-        }
-
-        public StructDictionary(int capacity)
-        {
-            if (capacity > 0) Initialize(capacity);
-        }
-                                
-        public int Count => _count - _freeCount;
-
-        public KeyCollection Keys
-        {
-            get
-            {
-                if (_keys == null) _keys = new KeyCollection(this);
-                return _keys;
-            }
-        }
-
-        public ValueCollection Values
-        {
-            get
-            {
-                if (_values == null) _values = new ValueCollection(this);
-                return _values;
-            }
-        }
-
-        public ref TValue GetItem(string key)
-        {
-            int i = FindEntry(key);
-            if (i >= 0) return ref _entries[i].value;
-            ExceptionHelper.ThrowArgumentException("key " + key + " not part of dictionary");
-            return ref _entries[0].value;
-        }
-
-        public void Clear()
-        {
-            int count = _count;
-            if (count > 0)
-            {
-                Array.Clear(_buckets, 0, _buckets.Length);
-
-                _count = 0;
-                _freeList = -1;
-                _freeCount = 0;
-                Array.Clear(_entries, 0, count);
-            }
-        }
-
-        public bool ContainsKey(string key)
-            => FindEntry(key) >= 0;
-
-        public Enumerator GetEnumerator()
-            => new Enumerator(this, Enumerator.KeyValuePair);
-
-        private int FindEntry(string key)
-        {
-            int i = -1;
-            int[] buckets = _buckets;
-            Entry[] entries = _entries;
-            int collisionCount = 0;
-            if (buckets != null)
-            {
-                int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
-                // Value in _buckets is 1-based
-                i = buckets[hashCode % buckets.Length] - 1;
-                do
-                {
-                    // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
-                    // Test in if to drop range check for following array access
-                    if ((uint) i >= (uint) entries.Length ||
-                        (entries[i].hashCode == hashCode && entries[i].key == key))
-                    {
-                        break;
-                    }
-
-                    i = entries[i].next;
-                    if (collisionCount >= entries.Length)
-                    {
-                        // The chain of entries forms a loop; which means a concurrent update has happened.
-                        // Break out of the loop and throw, rather than looping forever.
-                        ExceptionHelper.ThrowInvalidOperationException();
-                    }
-
-                    collisionCount++;
-                } while (true);
-            }
-
-            return i;
-        }
-
-        private int Initialize(int capacity)
-        {
-            int size = HashHelpers.GetPrime(capacity);
-
-            _freeList = -1;
-            _buckets = new int[size];
-            _entries = new Entry[size];
-
-            return size;
-        }
-
-        public bool TryInsert(string key, in TValue value, InsertionBehavior behavior)
-        {
-            if (_buckets == null)
-            {
-                Initialize(0);
-            }
-
-            Entry[] entries = _entries;
-
-            int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
-
-            int collisionCount = 0;
-            ref int bucket = ref _buckets[hashCode % _buckets.Length];
-            // Value in _buckets is 1-based
-            int i = bucket - 1;
-            do
-            {
-                // Should be a while loop https://github.com/dotnet/coreclr/issues/15476
-                // Test uint in if rather than loop condition to drop range check for following array access
-                if ((uint) i >= (uint) entries.Length)
-                {
-                    break;
-                }
-
-                if (entries[i].hashCode == hashCode && entries[i].key == key)
-                {
-                    if (behavior == InsertionBehavior.OverwriteExisting)
-                    {
-                        entries[i].value = value;
-                        return true;
-                    }
-
-                    if (behavior == InsertionBehavior.SkipIfExists)
-                    {
-                        return true;
-                    }
-
-                    if (behavior == InsertionBehavior.ThrowOnExisting)
-                    {
-                        ExceptionHelper.ThrowArgumentException("key already exists");
-                    }
-
-                    return false;
-                }
-
-                i = entries[i].next;
-                if (collisionCount >= entries.Length)
-                {
-                    // The chain of entries forms a loop; which means a concurrent update has happened.
-                    // Break out of the loop and throw, rather than looping forever.
-                    ExceptionHelper.ThrowInvalidOperationException();
-                }
-
-                collisionCount++;
-            } while (true);
-
-            bool updateFreeList = false;
-            int index;
-            if (_freeCount > 0)
-            {
-                index = _freeList;
-                updateFreeList = true;
-                _freeCount--;
-            }
-            else
-            {
-                int count = _count;
-                if (count == entries.Length)
-                {
-                    Resize();
-                    bucket = ref _buckets[hashCode % _buckets.Length];
-                }
-
-                index = count;
-                _count = count + 1;
-                entries = _entries;
-            }
-
-            ref Entry entry = ref entries[index];
-
-            if (updateFreeList)
-            {
-                _freeList = entry.next;
-            }
-
-            entry.hashCode = hashCode;
-            // Value in _buckets is 1-based
-            entry.next = bucket - 1;
-            entry.key = key;
-            entry.value = value;
-            // Value in _buckets is 1-based
-            bucket = index + 1;
-
-            return true;
-        }
-
-        private void Resize()
-            => Resize(HashHelpers.ExpandPrime(_count), false);
-
-        private void Resize(int newSize, bool forceNewHashCodes)
-        {
-            // Value types never rehash
-            Debug.Assert(!forceNewHashCodes || default(string) == null);
-            Debug.Assert(newSize >= _entries.Length);
-
-            int[] buckets = new int[newSize];
-            Entry[] entries = new Entry[newSize];
-
-            int count = _count;
-            Array.Copy(_entries, 0, entries, 0, count);
-
-            if (forceNewHashCodes)
-            {
-                for (int i = 0; i < count; i++)
-                {
-                    if (entries[i].hashCode >= 0)
-                    {
-                        entries[i].hashCode = (_comparer.GetHashCode(entries[i].key) & 0x7FFFFFFF);
-                    }
-                }
-            }
-
-            for (int i = 0; i < count; i++)
-            {
-                if (entries[i].hashCode >= 0)
-                {
-                    int bucket = entries[i].hashCode % newSize;
-                    // Value in _buckets is 1-based
-                    entries[i].next = buckets[bucket] - 1;
-                    // Value in _buckets is 1-based
-                    buckets[bucket] = i + 1;
-                }
-            }
-
-            _buckets = buckets;
-            _entries = entries;
-        }
-
-        // The overload Remove(string key, out TValue value) is a copy of this method with one additional
-        // statement to copy the value for entry being removed into the output parameter.
-        // Code has been intentionally duplicated for performance reasons.
-        public bool Remove(string key)
-        {
-            int[] buckets = _buckets;
-            Entry[] entries = _entries;
-            int collisionCount = 0;
-            if (buckets != null)
-            {
-                int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
-                int bucket = hashCode % buckets.Length;
-                int last = -1;
-                // Value in buckets is 1-based
-                int i = buckets[bucket] - 1;
-                while (i >= 0)
-                {
-                    ref Entry entry = ref entries[i];
-
-                    if (entry.hashCode == hashCode && entry.key == key)
-                    {
-                        if (last < 0)
-                        {
-                            // Value in buckets is 1-based
-                            buckets[bucket] = entry.next + 1;
-                        }
-                        else
-                        {
-                            entries[last].next = entry.next;
-                        }
-
-                        entry.hashCode = -1;
-                        entry.next = _freeList;
-                        entry.key = null;
-                        entry.value = default;
-
-                        _freeList = i;
-                        _freeCount++;
-                        return true;
-                    }
-
-                    last = i;
-                    i = entry.next;
-                    if (collisionCount >= entries.Length)
-                    {
-                        // The chain of entries forms a loop; which means a concurrent update has happened.
-                        // Break out of the loop and throw, rather than looping forever.
-                        ExceptionHelper.ThrowInvalidOperationException();
-                    }
-
-                    collisionCount++;
-                }
-            }
-
-            return false;
-        }
-
-        // This overload is a copy of the overload Remove(string key) with one additional
-        // statement to copy the value for entry being removed into the output parameter.
-        // Code has been intentionally duplicated for performance reasons.
-        public bool Remove(string key, out TValue value)
-        {
-            int[] buckets = _buckets;
-            Entry[] entries = _entries;
-            int collisionCount = 0;
-            if (buckets != null)
-            {
-                int hashCode = _comparer.GetHashCode(key) & 0x7FFFFFFF;
-                int bucket = hashCode % buckets.Length;
-                int last = -1;
-                // Value in buckets is 1-based
-                int i = buckets[bucket] - 1;
-                while (i >= 0)
-                {
-                    ref Entry entry = ref entries[i];
-
-                    if (entry.hashCode == hashCode && entry.key == key)
-                    {
-                        if (last < 0)
-                        {
-                            // Value in buckets is 1-based
-                            buckets[bucket] = entry.next + 1;
-                        }
-                        else
-                        {
-                            entries[last].next = entry.next;
-                        }
-
-                        value = entry.value;
-
-                        entry.hashCode = -1;
-                        entry.next = _freeList;
-                        entry.key = null;
-                        entry.value = default;
-
-                        _freeList = i;
-                        _freeCount++;
-                        return true;
-                    }
-
-                    last = i;
-                    i = entry.next;
-                    if (collisionCount >= entries.Length)
-                    {
-                        // The chain of entries forms a loop; which means a concurrent update has happened.
-                        // Break out of the loop and throw, rather than looping forever.
-                        ExceptionHelper.ThrowInvalidOperationException();
-                    }
-
-                    collisionCount++;
-                }
-            }
-
-            value = default;
-            return false;
-        }
-
-        public bool TryGetValue(string key, out TValue value)
-        {
-            int i = FindEntry(key);
-            if (i >= 0)
-            {
-                value = _entries[i].value;
-                return true;
-            }
-
-            value = default;
-            return false;
-        }
-
-        /// <summary>
-        /// Ensures that the dictionary can hold up to 'capacity' entries without any further expansion of its backing storage
-        /// </summary>
-        public int EnsureCapacity(int capacity)
-        {
-            int currentCapacity = _entries == null ? 0 : _entries.Length;
-            if (currentCapacity >= capacity)
-                return currentCapacity;
-            if (_buckets == null)
-                return Initialize(capacity);
-            int newSize = HashHelpers.GetPrime(capacity);
-            Resize(newSize, forceNewHashCodes: false);
-            return newSize;
-        }
-                                    
-        public struct Enumerator : IEnumerator<KeyValuePair<string, TValue>>,
-            IDictionaryEnumerator
-        {
-            private readonly StructDictionary<TValue> _dictionary;
-            private int _index;
-            private KeyValuePair<string, TValue> _current;
-            private readonly int _getEnumeratorRetType; // What should Enumerator.Current return?
-
-            internal const int DictEntry = 1;
-            internal const int KeyValuePair = 2;
-
-            internal Enumerator(StructDictionary<TValue> dictionary, int getEnumeratorRetType)
-            {
-                _dictionary = dictionary;
-                _index = 0;
-                _getEnumeratorRetType = getEnumeratorRetType;
-                _current = new KeyValuePair<string, TValue>();
-            }
-
-            public bool MoveNext()
-            {
-                // Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
-                // dictionary.count+1 could be negative if dictionary.count is int.MaxValue
-                while ((uint) _index < (uint) _dictionary._count)
-                {
-                    ref Entry entry = ref _dictionary._entries[_index++];
-
-                    if (entry.hashCode >= 0)
-                    {
-                        _current = new KeyValuePair<string, TValue>(entry.key, entry.value);
-                        return true;
-                    }
-                }
-
-                _index = _dictionary._count + 1;
-                _current = new KeyValuePair<string, TValue>();
-                return false;
-            }
-
-            public KeyValuePair<string, TValue> Current => _current;
-
-            public void Dispose()
-            {
-            }
-
-            object IEnumerator.Current
-            {
-                get
-                {
-                    if (_index == 0 || (_index == _dictionary._count + 1))
-                    {
-                        ExceptionHelper.ThrowInvalidOperationException();
-                    }
-
-                    if (_getEnumeratorRetType == DictEntry)
-                    {
-                        return new DictionaryEntry(_current.Key, _current.Value);
-                    }
-                    else
-                    {
-                        return new KeyValuePair<string, TValue>(_current.Key, _current.Value);
-                    }
-                }
-            }
-
-            void IEnumerator.Reset()
-            {
-                _index = 0;
-                _current = new KeyValuePair<string, TValue>();
-            }
-
-            DictionaryEntry IDictionaryEnumerator.Entry
-            {
-                get
-                {
-                    if (_index == 0 || (_index == _dictionary._count + 1))
-                    {
-                        ExceptionHelper.ThrowInvalidOperationException();
-                    }
-
-                    return new DictionaryEntry(_current.Key, _current.Value);
-                }
-            }
-
-            object IDictionaryEnumerator.Key
-            {
-                get
-                {
-                    if (_index == 0 || (_index == _dictionary._count + 1))
-                    {
-                        ExceptionHelper.ThrowInvalidOperationException();
-                    }
-
-                    return _current.Key;
-                }
-            }
-
-            object IDictionaryEnumerator.Value
-            {
-                get
-                {
-                    if (_index == 0 || (_index == _dictionary._count + 1))
-                    {
-                        ExceptionHelper.ThrowInvalidOperationException();
-                    }
-
-                    return _current.Value;
-                }
-            }
-        }
-
-        public sealed class KeyCollection
-        {
-            private readonly StructDictionary<TValue> _dictionary;
-
-            public KeyCollection(StructDictionary<TValue> dictionary)
-            {
-                if (dictionary == null)
-                {
-                    ExceptionHelper.ThrowArgumentNullException(nameof(dictionary));
-                }
-
-                _dictionary = dictionary;
-            }
-
-            public Enumerator GetEnumerator()
-                => new Enumerator(_dictionary);
-
-            public int Count => _dictionary.Count;
-
-            public struct Enumerator : IEnumerator<string>, IEnumerator
-            {
-                private readonly StructDictionary<TValue> _dictionary;
-                private int _index;
-                private string _currenstring;
-
-                internal Enumerator(StructDictionary<TValue> dictionary)
-                {
-                    _dictionary = dictionary;
-                    _index = 0;
-                    _currenstring = default;
-                }
-
-                public void Dispose()
-                {
-                }
-
-                public bool MoveNext()
-                {
-                    while ((uint) _index < (uint) _dictionary._count)
-                    {
-                        ref Entry entry = ref _dictionary._entries[_index++];
-
-                        if (entry.hashCode >= 0)
-                        {
-                            _currenstring = entry.key;
-                            return true;
-                        }
-                    }
-
-                    _index = _dictionary._count + 1;
-                    _currenstring = default;
-                    return false;
-                }
-
-                public string Current => _currenstring;
-
-                object IEnumerator.Current
-                {
-                    get
-                    {
-                        if (_index == 0 || (_index == _dictionary._count + 1))
-                        {
-                            ExceptionHelper.ThrowInvalidOperationException();
-                        }
-
-                        return _currenstring;
-                    }
-                }
-
-                void IEnumerator.Reset()
-                {
-                    _index = 0;
-                    _currenstring = default;
-                }
-            }
-        }
-
-        public sealed class ValueCollection
-        {
-            private readonly StructDictionary<TValue> _dictionary;
-
-            public ValueCollection(StructDictionary<TValue> dictionary)
-            {
-                _dictionary = dictionary;
-            }
-
-            public Enumerator GetEnumerator()
-                => new Enumerator(_dictionary);
-
-            public int Count => _dictionary.Count;
-
-            public struct Enumerator : IEnumerator<TValue>
-            {
-                private readonly StructDictionary<TValue> _dictionary;
-                private int _index;
-                private TValue _currentValue;
-
-                internal Enumerator(StructDictionary<TValue> dictionary)
-                {
-                    _dictionary = dictionary;
-                    _index = 0;
-                    _currentValue = default;
-                }
-
-                public void Dispose()
-                {
-                }
-
-                public bool MoveNext()
-                {
-                    while ((uint) _index < (uint) _dictionary._count)
-                    {
-                        ref Entry entry = ref _dictionary._entries[_index++];
-
-                        if (entry.hashCode >= 0)
-                        {
-                            _currentValue = entry.value;
-                            return true;
-                        }
-                    }
-
-                    _index = _dictionary._count + 1;
-                    _currentValue = default;
-                    return false;
-                }
-
-                public TValue Current => _currentValue;
-
-                object IEnumerator.Current
-                {
-                    get
-                    {
-                        if (_index == 0 || (_index == _dictionary._count + 1))
-                        {
-                            ExceptionHelper.ThrowInvalidOperationException();
-                        }
-
-                        return _currentValue;
-                    }
-                }
-
-                void IEnumerator.Reset()
-                {
-                    _index = 0;
-                    _currentValue = default;
-                }
-            }
-        }
-
-        private static class HashHelpers
-        {
-            public const int HashCollisionThreshold = 100;
-
-            // This is the maximum prime smaller than Array.MaxArrayLength
-            public const int MaxPrimeArrayLength = 0x7FEFFFFD;
-
-            public const int HashPrime = 101;
-
-            // Table of prime numbers to use as hash table sizes. 
-            // A typical resize algorithm would pick the smallest prime number in this array
-            // that is larger than twice the previous capacity. 
-            // Suppose our Hashtable currently has capacity x and enough elements are added 
-            // such that a resize needs to occur. Resizing first computes 2x then finds the 
-            // first prime in the table greater than 2x, i.e. if primes are ordered 
-            // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n. 
-            // Doubling is important for preserving the asymptotic complexity of the 
-            // hashtable operations such as add.  Having a prime guarantees that double 
-            // hashing does not lead to infinite loops.  IE, your hash function will be 
-            // h1(key) + i*h2(key), 0 <= i < size.  h2 and the size must be relatively prime.
-            // We prefer the low computation costs of higher prime numbers over the increased
-            // memory allocation of a fixed prime number i.e. when right sizing a HashSet.
-            public static readonly int[] primes =
-            {
-                3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
-                1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
-                17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
-                187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
-                1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369
-            };
-
-            public static bool IsPrime(int candidate)
-            {
-                if ((candidate & 1) != 0)
-                {
-                    int limit = (int) Math.Sqrt(candidate);
-                    for (int divisor = 3; divisor <= limit; divisor += 2)
-                    {
-                        if ((candidate % divisor) == 0)
-                            return false;
-                    }
-
-                    return true;
-                }
-
-                return (candidate == 2);
-            }
-
-            public static int GetPrime(int min)
-            {
-                if (min < 0)
-                    throw new ArgumentException();
-
-                for (int i = 0; i < primes.Length; i++)
-                {
-                    int prime = primes[i];
-                    if (prime >= min)
-                        return prime;
-                }
-
-                //outside of our predefined table. 
-                //compute the hard way. 
-                for (int i = (min | 1); i < int.MaxValue; i += 2)
-                {
-                    if (IsPrime(i) && ((i - 1) % HashPrime != 0))
-                        return i;
-                }
-
-                return min;
-            }
-
-            // Returns size of hashtable to grow to.
-            public static int ExpandPrime(int oldSize)
-            {
-                int newSize = 2 * oldSize;
-
-                // Allow the hashtables to grow to maximum possible size (~2G elements) before encountering capacity overflow.
-                // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
-                if ((uint) newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
-                {
-                    Debug.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
-                    return MaxPrimeArrayLength;
-                }
-
-                return GetPrime(newSize);
-            }
-        }
-    }
-}

+ 19 - 4
Jint/Runtime/TypeConverter.cs

@@ -104,6 +104,9 @@ namespace Jint.Runtime
                     return ((JsBoolean) o)._value ? 1 : 0;
                     return ((JsBoolean) o)._value ? 1 : 0;
                 case Types.String:
                 case Types.String:
                     return ToNumber(o.AsStringWithoutTypeCheck());
                     return ToNumber(o.AsStringWithoutTypeCheck());
+                case Types.Symbol:
+                    // TODO proper TypeError would require Engine instance and a lot of API changes
+                    return ExceptionHelper.ThrowTypeErrorNoEngine<double>("Cannot convert a Symbol value to a number");
                 default:
                 default:
                     return ToNumber(ToPrimitive(o, Types.Number));
                     return ToNumber(ToPrimitive(o, Types.Number));
             }
             }
@@ -291,10 +294,22 @@ namespace Jint.Runtime
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.8
+        /// http://www.ecma-international.org/ecma-262/6.0/#sec-topropertykey
+        /// </summary>
+        public static string ToPropertyKey(JsValue o)
+        {
+            var key = ToPrimitive(o, Types.String);
+            if (key is JsSymbol s)
+            {
+                return s._value;
+            }
+
+            return ToString(key);
+        }
+
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/6.0/#sec-tostring
         /// </summary>
         /// </summary>
-        /// <param name="o"></param>
-        /// <returns></returns>
         public static string ToString(JsValue o)
         public static string ToString(JsValue o)
         {
         {
             switch (o._type)
             switch (o._type)
@@ -306,7 +321,7 @@ namespace Jint.Runtime
                 case Types.Number:
                 case Types.Number:
                     return ToString(((JsNumber) o)._value);
                     return ToString(((JsNumber) o)._value);
                 case Types.Symbol:
                 case Types.Symbol:
-                    return o.AsSymbol();
+                    return ExceptionHelper.ThrowTypeErrorNoEngine<string>("Cannot convert a Symbol value to a string");
                 case Types.Undefined:
                 case Types.Undefined:
                     return Undefined.Text;
                     return Undefined.Text;
                 case Types.Null:
                 case Types.Null:

+ 14 - 0
Jint/Runtime/TypeErrorException.cs

@@ -0,0 +1,14 @@
+using System;
+
+namespace Jint.Runtime
+{
+    /// <summary>
+    /// Workaround for situation where engine is not easily accessible.
+    /// </summary>
+    internal sealed class TypeErrorException : Exception
+    {
+        public TypeErrorException(string message) : base(message)
+        {
+        }
+    }
+}

+ 49 - 2
README.md

@@ -53,8 +53,9 @@ You can also directly pass POCOs or anonymous objects and use them from JavaScri
 
 
     var engine = new Engine()
     var engine = new Engine()
         .SetValue("p", p)
         .SetValue("p", p)
-        .Execute("p.Name === 'Mickey Mouse'")
+        .Execute("p.Name = 'Minnie'")
         ;
         ;
+    Assert.AreEqual("Minnie", p.Name);
 ```
 ```
 You can invoke JavaScript function reference
 You can invoke JavaScript function reference
 ```c#
 ```c#
@@ -143,7 +144,43 @@ This example is using French as the default culture.
 
 
 ## Implemented features:
 ## Implemented features:
 
 
-- ECMAScript 5.1 test suite (http://test262.ecmascript.org/) 
+### ECMAScript 5.1
+
+- Complete implementation
+  - ECMAScript 5.1 test suite (http://test262.ecmascript.org/) 
+
+### ECMAScript 6.0
+
+ES6 features which are being implemented:
+- [ ] [arrows](https://github.com/lukehoban/es6features/blob/master/README.md#arrows)
+- [ ] [classes](https://github.com/lukehoban/es6features/blob/master/README.md#classes)
+- [ ] [enhanced object literals](https://github.com/lukehoban/es6features/blob/master/README.md#enhanced-object-literals)
+- [ ] [template strings](https://github.com/lukehoban/es6features/blob/master/README.md#template-strings)
+- [ ] [destructuring](https://github.com/lukehoban/es6features/blob/master/README.md#destructuring)
+- [ ] [default + rest + spread](https://github.com/lukehoban/es6features/blob/master/README.md#default--rest--spread)
+- [ ] [let + const](https://github.com/lukehoban/es6features/blob/master/README.md#let--const)
+- [x] [iterators + for..of](https://github.com/lukehoban/es6features/blob/master/README.md#iterators--forof)
+- [ ] [generators](https://github.com/lukehoban/es6features/blob/master/README.md#generators)
+- [ ] [unicode](https://github.com/lukehoban/es6features/blob/master/README.md#unicode)
+- [ ] [modules](https://github.com/lukehoban/es6features/blob/master/README.md#modules)
+- [ ] [module loaders](https://github.com/lukehoban/es6features/blob/master/README.md#module-loaders)
+- [x] [map + set](https://github.com/lukehoban/es6features/blob/master/README.md#map--set--weakmap--weakset)
+- [ ] [weakmap + weakset](https://github.com/lukehoban/es6features/blob/master/README.md#map--set--weakmap--weakset)
+- [ ] [proxies](https://github.com/lukehoban/es6features/blob/master/README.md#proxies)
+- [x] [symbols](https://github.com/lukehoban/es6features/blob/master/README.md#symbols)
+- [ ] [subclassable built-ins](https://github.com/lukehoban/es6features/blob/master/README.md#subclassable-built-ins)
+- [ ] [promises](https://github.com/lukehoban/es6features/blob/master/README.md#promises)
+- [ ] [math APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)
+- [ ] [number APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)
+- [x] [string APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)
+- [x] [array APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)
+- [ ] [object APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)
+- [x] [binary and octal literals](https://github.com/lukehoban/es6features/blob/master/README.md#binary-and-octal-literals)
+- [ ] [reflect api](https://github.com/lukehoban/es6features/blob/master/README.md#reflect-api)
+- [ ] [tail calls](https://github.com/lukehoban/es6features/blob/master/README.md#tail-calls)
+
+### .NET Interoperability
+
 - Manipulate CLR objects from JavaScript, including:
 - Manipulate CLR objects from JavaScript, including:
   - Single values
   - Single values
   - Objects
   - Objects
@@ -162,5 +199,15 @@ This example is using French as the default culture.
   - Regex -> RegExp
   - Regex -> RegExp
   - Function -> Delegate
   - Function -> Delegate
 
 
+### Security
+
+The following features provide you with a secure, sand-boxed environment to run user scripts.
+
+- Define memory limits, to prevent allocations from depleting the memory.
+- Enable/disable usage of BCL to prevent scripts from invoking .NET code.
+- Limit number of statements to prevent infinite loops.
+- Limit depth of calls to prevent deep recursion calls.
+- Define a timeout, to prevent scripts from taking too long to finish.
+
 Continuous Integration kindly provided by  [AppVeyor](https://www.appveyor.com)
 Continuous Integration kindly provided by  [AppVeyor](https://www.appveyor.com)
 
 

Some files were not shown because too many files changed in this diff