Browse Source

Merge pull request #524 from sebastienros/dev

Sébastien Ros 7 years ago
parent
commit
cb1be3ef94
91 changed files with 2744 additions and 2056 deletions
  1. 278 0
      Jint.Benchmark/InteropBenchmark.cs
  2. 0 1
      Jint.Benchmark/Properties/AssemblyInfo.cs
  3. 1 15
      Jint.Repl/Jint.Repl.csproj
  4. 0 20
      Jint.Repl/Properties/AssemblyInfo.cs
  5. 4 15
      Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj
  6. 0 35
      Jint.Tests.CommonScripts/Properties/AssemblyInfo.cs
  7. 3 19
      Jint.Tests.Ecma/Jint.Tests.Ecma.csproj
  8. 0 38
      Jint.Tests.Ecma/Properties/AssemblyInfo.cs
  9. 4 15
      Jint.Tests/Jint.Tests.csproj
  10. 0 36
      Jint.Tests/Properties/AssemblyInfo.cs
  11. 0 3
      Jint.Tests/Runtime/Domain/Company.cs
  12. 1 7
      Jint.Tests/Runtime/Domain/IPerson.cs
  13. 2 2
      Jint.Tests/Runtime/InteropTests.cs
  14. 165 203
      Jint/Engine.cs
  15. 1 1
      Jint/Jint.csproj
  16. 5 11
      Jint/JsValueExtensions.cs
  17. 15 11
      Jint/Native/Argument/ArgumentsInstance.cs
  18. 16 13
      Jint/Native/Array/ArrayConstructor.cs
  19. 31 43
      Jint/Native/Array/ArrayInstance.cs
  20. 32 31
      Jint/Native/Array/ArrayPrototype.cs
  21. 11 4
      Jint/Native/Boolean/BooleanConstructor.cs
  22. 2 1
      Jint/Native/Boolean/BooleanPrototype.cs
  23. 2 2
      Jint/Native/Date/DateConstructor.cs
  24. 2 1
      Jint/Native/Date/DateInstance.cs
  25. 17 27
      Jint/Native/Date/DatePrototype.cs
  26. 1 1
      Jint/Native/Error/ErrorConstructor.cs
  27. 1 1
      Jint/Native/Error/ErrorPrototype.cs
  28. 11 6
      Jint/Native/Function/BindFunctionInstance.cs
  29. 11 8
      Jint/Native/Function/EvalFunctionInstance.cs
  30. 6 6
      Jint/Native/Function/FunctionConstructor.cs
  31. 17 13
      Jint/Native/Function/FunctionInstance.cs
  32. 7 10
      Jint/Native/Function/FunctionPrototype.cs
  33. 6 6
      Jint/Native/Function/ScriptFunctionInstance.cs
  34. 2 1
      Jint/Native/Function/ThrowTypeError.cs
  35. 14 14
      Jint/Native/Global/GlobalObject.cs
  36. 2 2
      Jint/Native/JsBoolean.cs
  37. 5 4
      Jint/Native/JsNumber.cs
  38. 25 15
      Jint/Native/JsString.cs
  39. 9 20
      Jint/Native/JsValue.cs
  40. 16 16
      Jint/Native/Json/JsonParser.cs
  41. 4 5
      Jint/Native/Json/JsonSerializer.cs
  42. 5 5
      Jint/Native/Number/Dtoa/DiyFp.cs
  43. 4 3
      Jint/Native/Number/Dtoa/FastDtoa.cs
  44. 11 4
      Jint/Native/Number/NumberConstructor.cs
  45. 1 1
      Jint/Native/Number/NumberInstance.cs
  46. 19 17
      Jint/Native/Number/NumberPrototype.cs
  47. 33 22
      Jint/Native/Object/ObjectConstructor.cs
  48. 57 68
      Jint/Native/Object/ObjectInstance.cs
  49. 1 1
      Jint/Native/Object/ObjectPrototype.cs
  50. 3 4
      Jint/Native/RegExp/RegExpConstructor.cs
  51. 2 2
      Jint/Native/RegExp/RegExpPrototype.cs
  52. 11 5
      Jint/Native/String/StringConstructor.cs
  53. 2 2
      Jint/Native/String/StringInstance.cs
  54. 11 11
      Jint/Native/String/StringPrototype.cs
  55. 15 7
      Jint/Native/Symbol/SymbolConstructor.cs
  56. 3 3
      Jint/Native/Symbol/SymbolPrototype.cs
  57. 2 7
      Jint/Options.cs
  58. 5 8
      Jint/Pooling/ArgumentsInstancePool.cs
  59. 5 5
      Jint/Pooling/ObjectPool.cs
  60. 0 65
      Jint/ReflectionExtensions.cs
  61. 1 1
      Jint/Runtime/Arguments.cs
  62. 7 7
      Jint/Runtime/CallStack/CallStackElement.cs
  63. 4 4
      Jint/Runtime/CallStack/JintCallStack.cs
  64. 5 5
      Jint/Runtime/Debugger/BreakPoint.cs
  65. 35 24
      Jint/Runtime/Descriptors/PropertyDescriptor.cs
  66. 1 1
      Jint/Runtime/Descriptors/Specialized/FieldInfoDescriptor.cs
  67. 14 7
      Jint/Runtime/Descriptors/Specialized/IndexDescriptor.cs
  68. 1 1
      Jint/Runtime/Descriptors/Specialized/PropertyInfoDescriptor.cs
  69. 10 3
      Jint/Runtime/Environments/Binding.cs
  70. 199 72
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  71. 19 3
      Jint/Runtime/Environments/EnvironmentRecord.cs
  72. 5 5
      Jint/Runtime/Environments/LexicalEnvironment.cs
  73. 2 2
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  74. 120 0
      Jint/Runtime/ExceptionHelper.cs
  75. 184 266
      Jint/Runtime/ExpressionIntepreter.cs
  76. 3 2
      Jint/Runtime/Interop/ClrFunctionInstance.cs
  77. 14 13
      Jint/Runtime/Interop/DefaultTypeConverter.cs
  78. 19 9
      Jint/Runtime/Interop/DelegateWrapper.cs
  79. 11 11
      Jint/Runtime/Interop/MethodInfoFunctionInstance.cs
  80. 29 26
      Jint/Runtime/Interop/NamespaceReference.cs
  81. 117 62
      Jint/Runtime/Interop/ObjectWrapper.cs
  82. 27 24
      Jint/Runtime/Interop/TypeReference.cs
  83. 1 1
      Jint/Runtime/Interop/TypeReferencePrototype.cs
  84. 0 165
      Jint/Runtime/MruPropertyCache.cs
  85. 0 172
      Jint/Runtime/MruPropertyCache2.cs
  86. 4 9
      Jint/Runtime/RefStack.cs
  87. 15 6
      Jint/Runtime/References/Reference.cs
  88. 56 34
      Jint/Runtime/StatementInterpreter.cs
  89. 802 0
      Jint/Runtime/StructDictionary.cs
  90. 115 194
      Jint/Runtime/TypeConverter.cs
  91. 5 5
      appveyor.yml

+ 278 - 0
Jint.Benchmark/InteropBenchmark.cs

@@ -0,0 +1,278 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using BenchmarkDotNet.Attributes;
+using Jint.Native;
+using Jint.Native.Array;
+
+namespace Jint.Benchmark
+{
+    [MemoryDiagnoser]
+    public class InteropBenchmark
+    {
+        private const int OperationsPerInvoke = 1_000;
+
+        public class Person
+        {
+            public string Name { get; set; }
+        }
+
+        private Engine _engine;
+
+        [GlobalSetup]
+        public void Setup()
+        {
+            _engine = new Engine(cfg => cfg.AllowClr(
+                    typeof(Person).GetTypeInfo().Assembly,
+                    typeof(Console).GetTypeInfo().Assembly,
+                    typeof(System.IO.File).GetTypeInfo().Assembly))
+                .SetValue("log", new Action<object>(Console.WriteLine))
+                .SetValue("assert", new Action<bool>(x => { }))
+                .SetValue("equal", new Action<object, object>((x, y) => { }));
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void DelegatesCanBeSet()
+        {
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("square", new Func<double, double>(x => x * x));
+                _engine.Execute("assert(square(10) === 100);");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void ExtraParametersAreIgnored()
+        {
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("passNumber", new Func<int, int>(x => x));
+                _engine.Execute("assert(passNumber(123,'test',{},[],null) === 123);");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void GetObjectProperties()
+        {
+            var p = new Person
+            {
+                Name = "Mickey Mouse"
+            };
+
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("p", p);
+                _engine.Execute("assert(p.Name === 'Mickey Mouse');");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void InvokeObjectMethods()
+        {
+            var p = new Person
+            {
+                Name = "Mickey Mouse"
+            };
+
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("p", p);
+                _engine.Execute(@"assert(p.ToString() === 'Mickey Mouse');");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void SetObjectProperties()
+        {
+            var p = new Person
+            {
+                Name = "Mickey Mouse"
+            };
+
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("p", p);
+                _engine.Execute("p.Name = 'Donald Duck'; assert(p.Name === 'Donald Duck');");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void GetIndexUsingStringKey()
+        {
+            var dictionary = new Dictionary<string, Person>();
+            dictionary.Add("person1", new Person {Name = "Mickey Mouse"});
+            dictionary.Add("person2", new Person {Name = "Goofy"});
+
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("dictionary", dictionary);
+                _engine.Execute("assert(dictionary['person1'].Name === 'Mickey Mouse'); assert(dictionary['person2'].Name === 'Goofy');");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void GenericMethods()
+        {
+            var dictionary = new Dictionary<int, string>
+            {
+                {1, "Mickey Mouse"}
+            };
+
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("dictionary", dictionary);
+                _engine.Execute("dictionary.Clear(); dictionary.Add(2, 'Goofy'); assert(dictionary[2] === 'Goofy');");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void MultiGenericTypes()
+        {
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.Execute(@"
+                var type = System.Collections.Generic.Dictionary(System.Int32, System.String);
+                var dictionary = new type();
+                dictionary.Add(1, 'Mickey Mouse');
+                dictionary.Add(2, 'Goofy');
+                assert(dictionary[2] === 'Goofy');
+            ");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void IndexOnList()
+        {
+            var list = new List<object>(2);
+            list.Add("Mickey Mouse");
+            list.Add("Goofy");
+
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("list", list);
+                _engine.Execute("list[1] = 'Donald Duck'; assert(list[1] === 'Donald Duck');");
+            }
+        }
+
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void EcmaValuesAreAutomaticallyConvertedWhenSetInPoco()
+        {
+            var p = new Person
+            {
+                Name = "foo",
+            };
+
+
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("p", p);
+                _engine.Execute(@"
+                assert(p.Name === 'foo');
+                assert(p.Age === 0);
+                p.Name = 'bar';
+                p.Age = 10;
+            ");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void Trim()
+        {
+            var p = new Person
+            {
+                Name = "Mickey Mouse "
+            };
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("p", p);
+                _engine.Execute(@"
+                assert(p.Name === 'Mickey Mouse ');
+                p.Name = p.Name.trim();
+                assert(p.Name === 'Mickey Mouse');
+            ");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void MathFloor()
+        {
+            var p = new Person();
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("p", p);
+                _engine.Execute("p.Age = Math.floor(1.6); assert(p.Age === 1);");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void DelegateAsFunction()
+        {
+            var even = new Func<int, bool>(x => x % 2 == 0);
+
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("even", even);
+                _engine.Execute("assert(even(2) === true);");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void ConvertArrayToArrayInstance()
+        {
+            var ints = new[] {1, 2, 3, 4, 5, 6};
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine
+                    .SetValue("values", ints)
+                    .Execute("values.filter(function(x){ return x % 2 == 0; })");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void ConvertListsToArrayInstance()
+        {
+            var ints = new List<object> {1, 2, 3, 4, 5, 6};
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine
+                    .SetValue("values", ints)
+                    .Execute("new Array(values).filter(function(x){ return x % 2 == 0; })");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void ConvertArrayInstanceToArray()
+        {
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.Execute("'[email protected]'.split('@');");
+            }
+        }
+
+        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
+        public void LoopWithNativeEnumerator()
+        {
+            JsValue Adder(JsValue argValue)
+            {
+                ArrayInstance args = argValue.AsArray();
+                double sum = 0;
+                foreach (var item in args)
+                {
+                    if (item.IsNumber())
+                    {
+                        sum += item.AsNumber();
+                    }
+                }
+
+                return sum;
+            }
+
+            for (int i = 0; i < OperationsPerInvoke; ++i)
+            {
+                _engine.SetValue("getSum", new Func<JsValue, JsValue>(Adder));
+                _engine.Execute("getSum([1,2,3]);");
+            }
+        }
+    }
+}

+ 0 - 1
Jint.Benchmark/Properties/AssemblyInfo.cs

@@ -1,5 +1,4 @@
 using System.Reflection;
 using System.Reflection;
-using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 
 
 // General Information about an assembly is controlled through the following 
 // General Information about an assembly is controlled through the following 

+ 1 - 15
Jint.Repl/Jint.Repl.csproj

@@ -1,23 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
-
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>netcoreapp2.0</TargetFramework>
     <TargetFramework>netcoreapp2.0</TargetFramework>
-    <AssemblyName>Jint.Repl</AssemblyName>
     <OutputType>Exe</OutputType>
     <OutputType>Exe</OutputType>
-    <PackageId>Jint.Repl</PackageId>
-    <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
-    <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
-    <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
-    <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
-    <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
-    <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
-    <GenerateNeutralResourcesLanguageAttribute>false</GenerateNeutralResourcesLanguageAttribute>
-    <GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
-    <GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
   </PropertyGroup>
   </PropertyGroup>
-
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Jint\Jint.csproj" />
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   </ItemGroup>
-
-</Project>
+</Project>

+ 0 - 20
Jint.Repl/Properties/AssemblyInfo.cs

@@ -1,20 +0,0 @@
-using System.Reflection;
-using System.Resources;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following 
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Jint CLI")]
-[assembly: AssemblyDescription("Javascript Interpreter for .NET")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Jint")]
-[assembly: AssemblyCopyright("Copyright © Sébastien Ros 2013")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-[assembly: NeutralResourcesLanguage("en")]
-
-[assembly: AssemblyVersion("2.9.1")]
-[assembly: AssemblyFileVersion("2.9.1")]

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

@@ -1,28 +1,17 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFrameworks>netcoreapp2.0</TargetFrameworks>
-    <AssemblyName>Jint.Tests.CommonScripts</AssemblyName>
-    <PackageId>Jint.Tests.CommonScripts</PackageId>
-    <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
-    <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
-    <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
-    <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
-    <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
-    <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
-    <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
-    <GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
-    <GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
-    <EmbeddedResource Include="Scripts\*.*" Exclude="bin\**;obj\**;**\*.xproj;packages\**;@(EmbeddedResource)" />
+    <EmbeddedResource Include="Scripts\*.*" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Jint\Jint.csproj" />
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
     <PackageReference Include="xunit" Version="2.3.1" />
     <PackageReference Include="xunit" Version="2.3.1" />
-    <PackageReference Include="xunit.analyzers" Version="0.8.0" />
+    <PackageReference Include="xunit.analyzers" Version="0.9.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
     <PackageReference Include="xunit.runner.console" Version="2.3.1" />
     <PackageReference Include="xunit.runner.console" Version="2.3.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />

+ 0 - 35
Jint.Tests.CommonScripts/Properties/AssemblyInfo.cs

@@ -1,35 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Jint.Tests.CommonScripts")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Jint.Tests.CommonScripts")]
-[assembly: AssemblyCopyright("Copyright ©  2014")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components.  If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("4e76239b-c013-4f4a-b537-bbba4b19b45d")]
-
-// Version information for an assembly consists of the following four values:
-//
-//      Major Version
-//      Minor Version
-//      Build Number
-//      Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]

+ 3 - 19
Jint.Tests.Ecma/Jint.Tests.Ecma.csproj

@@ -1,32 +1,16 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFramework>netcoreapp2.0</TargetFramework>
-    <AssemblyName>Jint.Tests.Ecma</AssemblyName>
-    <PackageId>Jint.Tests.Ecma</PackageId>
-    <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
-    <RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">1.0.4</RuntimeFrameworkVersion>
-    <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
-    <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
-    <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
-    <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
-    <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
-    <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
-    <GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
-    <GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Jint\Jint.csproj" />
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
     <PackageReference Include="xunit" Version="2.3.1" />
     <PackageReference Include="xunit" Version="2.3.1" />
-    <PackageReference Include="xunit.analyzers" Version="0.8.0" />
+    <PackageReference Include="xunit.analyzers" Version="0.9.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
     <PackageReference Include="xunit.runner.console" Version="2.3.1" />
     <PackageReference Include="xunit.runner.console" Version="2.3.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
   </ItemGroup>
   </ItemGroup>
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
-    <Reference Include="System" />
-    <Reference Include="Microsoft.CSharp" />
-  </ItemGroup>
 </Project>
 </Project>

+ 0 - 38
Jint.Tests.Ecma/Properties/AssemblyInfo.cs

@@ -1,38 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-using Xunit;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Jint.Tests.Ecma")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Jint.Tests.Ecma")]
-[assembly: AssemblyCopyright("Copyright ©  2013")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components.  If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("9a0aba24-ca5d-4ff9-aef9-354544a2d86b")]
-
-// Version information for an assembly consists of the following four values:
-//
-//      Major Version
-//      Minor Version
-//      Build Number
-//      Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
-
-[assembly: CollectionBehavior(DisableTestParallelization = true)]

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

@@ -1,28 +1,17 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFramework>netcoreapp2.0</TargetFramework>
-    <AssemblyName>Jint.Tests</AssemblyName>
-    <PackageId>Jint.Tests</PackageId>
-    <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
-    <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
-    <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
-    <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
-    <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
-    <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
-    <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
-    <GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
-    <GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
-    <EmbeddedResource Include="Runtime\Scripts\*.*;Parser\Scripts\*.*" Exclude="bin\**;obj\**;**\*.xproj;packages\**;@(EmbeddedResource)" />
+    <EmbeddedResource Include="Runtime\Scripts\*.*;Parser\Scripts\*.*" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Jint\Jint.csproj" />
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
     <PackageReference Include="xunit" Version="2.3.1" />
     <PackageReference Include="xunit" Version="2.3.1" />
-    <PackageReference Include="xunit.analyzers" Version="0.8.0" />
+    <PackageReference Include="xunit.analyzers" Version="0.9.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
     <PackageReference Include="xunit.runner.console" Version="2.3.1" />
     <PackageReference Include="xunit.runner.console" Version="2.3.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
     <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />

+ 0 - 36
Jint.Tests/Properties/AssemblyInfo.cs

@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following 
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Jint.Tests")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Jint.Tests")]
-[assembly: AssemblyCopyright("Copyright © Sébastien Ros 2013")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible 
-// to COM components.  If you need to access a type in this assembly from 
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("ed2c320b-457f-40f1-9829-e0e8200fa311")]
-
-// Version information for an assembly consists of the following four values:
-//
-//      Major Version
-//      Minor Version 
-//      Build Number
-//      Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers 
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]

+ 0 - 3
Jint.Tests/Runtime/Domain/Company.cs

@@ -1,8 +1,5 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 
 namespace Jint.Tests.Runtime.Domain
 namespace Jint.Tests.Runtime.Domain
 {
 {

+ 1 - 7
Jint.Tests/Runtime/Domain/IPerson.cs

@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Jint.Tests.Runtime.Domain
+namespace Jint.Tests.Runtime.Domain
 {
 {
     public interface IPerson
     public interface IPerson
     {
     {

+ 2 - 2
Jint.Tests/Runtime/InteropTests.cs

@@ -1335,8 +1335,8 @@ namespace Jint.Tests.Runtime
         public void ShouldReturnUndefinedProperty()
         public void ShouldReturnUndefinedProperty()
         {
         {
             _engine.SetValue("uo", new { foo = "bar" });
             _engine.SetValue("uo", new { foo = "bar" });
-            _engine.SetValue("ud", new Dictionary<string, object>() { {"foo", "bar"} });
-            _engine.SetValue("ul", new List<string>() { "foo", "bar" });
+            _engine.SetValue("ud", new Dictionary<string, object> { {"foo", "bar"} });
+            _engine.SetValue("ul", new List<string> { "foo", "bar" });
 
 
             RunTest(@"
             RunTest(@"
                 assert(!uo.undefinedProperty);
                 assert(!uo.undefinedProperty);

+ 165 - 203
Jint/Engine.cs

@@ -28,7 +28,7 @@ using Jint.Runtime.References;
 
 
 namespace Jint
 namespace Jint
 {
 {
-    public class Engine
+    public sealed class Engine
     {
     {
         private static readonly ParserOptions DefaultParserOptions = new ParserOptions
         private static readonly ParserOptions DefaultParserOptions = new ParserOptions
         {
         {
@@ -50,7 +50,12 @@ namespace Jint
         private readonly bool _isDebugMode;
         private readonly bool _isDebugMode;
         private readonly bool _isStrict;
         private readonly bool _isStrict;
         private readonly int _maxStatements;
         private readonly int _maxStatements;
+        private readonly long _memoryLimit;
+        private readonly bool _runBeforeStatementChecks;
         private readonly IReferenceResolver _referenceResolver;
         private readonly IReferenceResolver _referenceResolver;
+        internal readonly ReferencePool _referencePool;
+        internal readonly ArgumentsInstancePool _argumentsInstancePool;
+        internal readonly JsValueArrayPool _jsValueArrayPool;
         
         
         public ITypeConverter ClrTypeConverter;
         public ITypeConverter ClrTypeConverter;
 
 
@@ -78,6 +83,9 @@ namespace Jint
             { typeof(System.Text.RegularExpressions.Regex), (Engine engine, object v) => engine.RegExp.Construct((System.Text.RegularExpressions.Regex)v, "") }
             { typeof(System.Text.RegularExpressions.Regex), (Engine engine, object v) => engine.RegExp.Construct((System.Text.RegularExpressions.Regex)v, "") }
         };
         };
 
 
+        internal readonly Dictionary<(Type, string), Func<Engine, object, PropertyDescriptor>> ClrPropertyDescriptorFactories =
+            new Dictionary<(Type, string), Func<Engine, object, PropertyDescriptor>>();
+
         internal JintCallStack CallStack = new JintCallStack();
         internal JintCallStack CallStack = new JintCallStack();
 
 
         static Engine()
         static Engine()
@@ -176,10 +184,15 @@ namespace Jint
             _isStrict = Options.IsStrict;
             _isStrict = Options.IsStrict;
             _maxStatements = Options._MaxStatements;
             _maxStatements = Options._MaxStatements;
             _referenceResolver = Options.ReferenceResolver;
             _referenceResolver = Options.ReferenceResolver;
+            _memoryLimit = Options._MemoryLimit;
+            _runBeforeStatementChecks = (_maxStatements > 0 &&_maxStatements < int.MaxValue) 
+                                        || Options._TimeoutInterval.Ticks > 0
+                                        || _memoryLimit > 0 
+                                        || _isDebugMode;
 
 
-            ReferencePool = new ReferencePool();
-            ArgumentsInstancePool = new ArgumentsInstancePool(this);
-            JsValueArrayPool = new JsValueArrayPool();
+            _referencePool = new ReferencePool();
+            _argumentsInstancePool = new ArgumentsInstancePool(this);
+            _jsValueArrayPool = new JsValueArrayPool();
 
 
             Eval = new EvalFunctionInstance(this, System.Array.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
             Eval = new EvalFunctionInstance(this, System.Array.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
             Global.FastAddProperty("eval", Eval, true, false, true);
             Global.FastAddProperty("eval", Eval, true, false, true);
@@ -234,15 +247,6 @@ namespace Jint
 
 
         internal Options Options { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; }
         internal Options Options { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; }
 
 
-        internal ReferencePool ReferencePool
-        {
-            [MethodImpl(MethodImplOptions.AggressiveInlining)]
-            get;
-        }
-
-        internal ArgumentsInstancePool ArgumentsInstancePool { get; }
-        internal JsValueArrayPool JsValueArrayPool { get; }
-
         #region Debugger
         #region Debugger
         public delegate StepMode DebugStepDelegate(object sender, DebugInformation e);
         public delegate StepMode DebugStepDelegate(object sender, DebugInformation e);
         public delegate StepMode BreakDelegate(object sender, DebugInformation e);
         public delegate StepMode BreakDelegate(object sender, DebugInformation e);
@@ -364,7 +368,7 @@ namespace Jint
         {
         {
             ResetStatementsCount();
             ResetStatementsCount();
             
             
-            if (Options._MemoryLimit > 0)
+            if (_memoryLimit > 0)
             {
             {
                 ResetMemoryUsage();
                 ResetMemoryUsage();
             }
             }
@@ -405,46 +409,24 @@ namespace Jint
 
 
         public Completion ExecuteStatement(Statement statement)
         public Completion ExecuteStatement(Statement statement)
         {
         {
-            if (_maxStatements > 0 && _statementsCount++ > _maxStatements)
-            {
-                ThrowStatementsCountOverflowException();
-            }
-
-            if (_timeoutTicks > 0 && _timeoutTicks < DateTime.UtcNow.Ticks)
-            {
-                ThrowTimeoutException();
-            }
-
-            if (Options._MemoryLimit > 0)
-            {
-                if (GetAllocatedBytesForCurrentThread != null)
-                {
-                    var memoryUsage = GetAllocatedBytesForCurrentThread() - _initialMemoryUsage;
-                    if (memoryUsage > Options._MemoryLimit)
-                    {
-                        throw new MemoryLimitExceededException($"Script has allocated {memoryUsage} but is limited to {Options._MemoryLimit}");
-                    }
-                }
-                else
-                {
-                    throw new PlatformNotSupportedException("The current platform doesn't support MemoryLimit.");
-                }
-            }
-
             _lastSyntaxNode = statement;
             _lastSyntaxNode = statement;
 
 
-            if (_isDebugMode)
+            if (_runBeforeStatementChecks)
             {
             {
-                DebugHandler.OnStep(statement);
+                BeforeExecuteStatement(statement);
             }
             }
 
 
             switch (statement.Type)
             switch (statement.Type)
             {
             {
                 case Nodes.BlockStatement:
                 case Nodes.BlockStatement:
-                    return _statements.ExecuteBlockStatement((BlockStatement) statement);
+                    return _statements.ExecuteStatementList(((BlockStatement) statement).Body);
 
 
                 case Nodes.ReturnStatement:
                 case Nodes.ReturnStatement:
-                    return _statements.ExecuteReturnStatement((ReturnStatement) statement);
+                    var jsValue = ((ReturnStatement) statement).Argument == null
+                        ? Undefined.Instance
+                        : GetValue(EvaluateExpression(((ReturnStatement) statement).Argument), true);
+
+                    return new Completion(CompletionType.Return, jsValue, null);
 
 
                 case Nodes.VariableDeclaration:
                 case Nodes.VariableDeclaration:
                     return _statements.ExecuteVariableDeclaration((VariableDeclaration) statement);
                     return _statements.ExecuteVariableDeclaration((VariableDeclaration) statement);
@@ -459,10 +441,13 @@ namespace Jint
                     return _statements.ExecuteDoWhileStatement((DoWhileStatement) statement);
                     return _statements.ExecuteDoWhileStatement((DoWhileStatement) statement);
 
 
                 case Nodes.EmptyStatement:
                 case Nodes.EmptyStatement:
-                    return _statements.ExecuteEmptyStatement((EmptyStatement) statement);
+                    return new Completion(CompletionType.Normal, null, null);
 
 
                 case Nodes.ExpressionStatement:
                 case Nodes.ExpressionStatement:
-                    return _statements.ExecuteExpressionStatement((ExpressionStatement) statement);
+                    return new Completion(
+                        CompletionType.Normal, 
+                        GetValue(EvaluateExpression(((ExpressionStatement) statement).Expression), true),
+                        null);
 
 
                 case Nodes.ForStatement:
                 case Nodes.ForStatement:
                     return _statements.ExecuteForStatement((ForStatement) statement);
                     return _statements.ExecuteForStatement((ForStatement) statement);
@@ -501,11 +486,45 @@ namespace Jint
                     return _statements.ExecuteProgram((Program) statement);
                     return _statements.ExecuteProgram((Program) statement);
 
 
                 default:
                 default:
-                    ThrowArgumentOutOfRange();
+                    ExceptionHelper.ThrowArgumentOutOfRangeException();
                     return new Completion(CompletionType.Normal, null, null);
                     return new Completion(CompletionType.Normal, null, null);
             }
             }
         }
         }
 
 
+        private void BeforeExecuteStatement(Statement statement)
+        {
+            if (_maxStatements > 0 && _statementsCount++ > _maxStatements)
+            {
+                ExceptionHelper.ThrowStatementsCountOverflowException();
+            }
+
+            if (_timeoutTicks > 0 && _timeoutTicks < DateTime.UtcNow.Ticks)
+            {
+                ExceptionHelper.ThrowTimeoutException();
+            }
+
+            if (_memoryLimit > 0)
+            {
+                if (GetAllocatedBytesForCurrentThread != null)
+                {
+                    var memoryUsage = GetAllocatedBytesForCurrentThread() - _initialMemoryUsage;
+                    if (memoryUsage > _memoryLimit)
+                    {
+                        ExceptionHelper.ThrowMemoryLimitExceededException($"Script has allocated {memoryUsage} but is limited to {_memoryLimit}");
+                    }
+                }
+                else
+                {
+                    ExceptionHelper.ThrowPlatformNotSupportedException("The current platform doesn't support MemoryLimit.");
+                }
+            }
+
+            if (_isDebugMode)
+            {
+                DebugHandler.OnStep(statement);
+            }
+        }
+
         public object EvaluateExpression(INode expression)
         public object EvaluateExpression(INode expression)
         {
         {
             _lastSyntaxNode = expression;
             _lastSyntaxNode = expression;
@@ -561,7 +580,7 @@ namespace Jint
                     return _expressions.EvaluateUnaryExpression((UnaryExpression) expression);
                     return _expressions.EvaluateUnaryExpression((UnaryExpression) expression);
 
 
                 default:
                 default:
-                    ThrowArgumentOutOfRange();
+                    ExceptionHelper.ThrowArgumentOutOfRangeException();
                     return null;
                     return null;
             }
             }
         }
         }
@@ -569,30 +588,11 @@ namespace Jint
         /// <summary>
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1
         /// </summary>
         /// </summary>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue GetValue(object value)
         public JsValue GetValue(object value)
         {
         {
-            if (value is JsValue jsValue)
-            {
-                return jsValue;
-            }
-
-            return GetValueFromReference(value, false);
+            return GetValue(value, false);
         }
         }
         
         
-        
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal JsValue GetValue(in Completion completion)
-        {
-            var value = completion.Value;
-            if (value is JsValue jsValue)
-            {
-                return jsValue;
-            }
-            return GetValueFromReference(completion.Value, false);
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal JsValue GetValue(object value, bool returnReferenceToPool)
         internal JsValue GetValue(object value, bool returnReferenceToPool)
         {
         {
             if (value is JsValue jsValue)
             if (value is JsValue jsValue)
@@ -600,31 +600,22 @@ namespace Jint
                 return jsValue;
                 return jsValue;
             }
             }
 
 
-            return GetValueFromReference(value, returnReferenceToPool);
-        }
-       
-        internal JsValue GetValueFromReference(object value, bool returnReferenceToPool)
-        {
-            var reference = value as Reference;
-            if (reference == null)
+            if (!(value is Reference reference))
             {
             {
-                if (value is Completion completion)
-                {
-                    return GetValue(completion.Value, returnReferenceToPool);
-                }
+                return ((Completion) value).Value;
             }
             }
 
 
-            if (reference.IsUnresolvableReference())
+            if (reference._baseValue._type == Types.Undefined)
             {
             {
                 if (_referenceResolver != null &&
                 if (_referenceResolver != null &&
                     _referenceResolver.TryUnresolvableReference(this, reference, out JsValue val))
                     _referenceResolver.TryUnresolvableReference(this, reference, out JsValue val))
                 {
                 {
                     return val;
                     return val;
                 }
                 }
-                throw new JavaScriptException(ReferenceError, reference.GetReferencedName() + " is not defined");
+                ExceptionHelper.ThrowReferenceError(this, reference.GetReferencedName() + " is not defined");
             }
             }
 
 
-            var baseValue = reference.GetBase();
+            var baseValue = reference._baseValue;
 
 
             if (reference.IsPropertyReference())
             if (reference.IsPropertyReference())
             {
             {
@@ -634,12 +625,12 @@ namespace Jint
                     return baseValue;
                     return baseValue;
                 }
                 }
 
 
-                var referencedName = reference.GetReferencedName();
+                var referencedName = reference._name;
                 if (returnReferenceToPool)
                 if (returnReferenceToPool)
                 {
                 {
-                    ReferencePool.Return(reference);
+                    _referencePool.Return(reference);
                 }
                 }
-                if (reference.HasPrimitiveBase() == false)
+                if (!(reference._baseValue._type != Types.Object && reference._baseValue._type != Types.None))
                 {
                 {
                     var o = TypeConverter.ToObject(this, baseValue);
                     var o = TypeConverter.ToObject(this, baseValue);
                     var v = o.Get(referencedName);
                     var v = o.Get(referencedName);
@@ -673,14 +664,14 @@ namespace Jint
             var record = (EnvironmentRecord) baseValue;
             var record = (EnvironmentRecord) baseValue;
             if (ReferenceEquals(record, null))
             if (ReferenceEquals(record, null))
             {
             {
-                throw new ArgumentException();
+                ExceptionHelper.ThrowArgumentException();
             }
             }
 
 
-            var bindingValue = record.GetBindingValue(reference.GetReferencedName(), reference.IsStrict());
+            var bindingValue = record.GetBindingValue(reference._name, reference._strict);
 
 
             if (returnReferenceToPool)
             if (returnReferenceToPool)
             {
             {
-                ReferencePool.Return(reference);
+                _referencePool.Return(reference);
             }
             }
 
 
             return bindingValue;
             return bindingValue;
@@ -693,37 +684,31 @@ namespace Jint
         /// <param name="value"></param>
         /// <param name="value"></param>
         public void PutValue(Reference reference, JsValue value)
         public void PutValue(Reference reference, JsValue value)
         {
         {
-            if (reference.IsUnresolvableReference())
+            if (reference._baseValue._type == Types.Undefined)
             {
             {
-                if (reference.IsStrict())
+                if (reference._strict)
                 {
                 {
-                    ThrowReferenceError();
+                    ExceptionHelper.ThrowReferenceError(this);
                 }
                 }
 
 
-                Global.Put(reference.GetReferencedName(), value, false);
+                Global.Put(reference._name, value, false);
             }
             }
             else if (reference.IsPropertyReference())
             else if (reference.IsPropertyReference())
             {
             {
-                var baseValue = reference.GetBase();
-                if (!reference.HasPrimitiveBase())
+                var baseValue = reference._baseValue;
+                if (reference._baseValue._type == Types.Object || reference._baseValue._type == Types.None)
                 {
                 {
-                    baseValue.AsObject().Put(reference.GetReferencedName(), value, reference.IsStrict());
+                    ((ObjectInstance) baseValue).Put(reference._name, value, reference._strict);
                 }
                 }
                 else
                 else
                 {
                 {
-                    PutPrimitiveBase(baseValue, reference.GetReferencedName(), value, reference.IsStrict());
+                    PutPrimitiveBase(baseValue, reference._name, value, reference._strict);
                 }
                 }
             }
             }
             else
             else
             {
             {
-                var baseValue = reference.GetBase();
-                if (!(baseValue is EnvironmentRecord record))
-                {
-                    ThrowArgumentNullException();
-                    return;
-                }
-
-                record.SetMutableBinding(reference.GetReferencedName(), value, reference.IsStrict());
+                var baseValue = reference._baseValue;
+                ((EnvironmentRecord) baseValue).SetMutableBinding(reference._name, value, reference._strict);
             }
             }
         }
         }
 
 
@@ -737,7 +722,7 @@ namespace Jint
             {
             {
                 if (throwOnError)
                 if (throwOnError)
                 {
                 {
-                    ThrowTypeError();
+                    ExceptionHelper.ThrowTypeError(this);
                 }
                 }
                 return;
                 return;
             }
             }
@@ -748,7 +733,7 @@ namespace Jint
             {
             {
                 if (throwOnError)
                 if (throwOnError)
                 {
                 {
-                    ThrowTypeError();
+                    ExceptionHelper.ThrowTypeError(this);
                 }
                 }
                 return;
                 return;
             }
             }
@@ -764,7 +749,7 @@ namespace Jint
             {
             {
                 if (throwOnError)
                 if (throwOnError)
                 {
                 {
-                    ThrowTypeError();
+                    ExceptionHelper.ThrowTypeError(this);
                 }
                 }
             }
             }
         }
         }
@@ -814,21 +799,16 @@ namespace Jint
         /// <returns>The value returned by the function call.</returns>
         /// <returns>The value returned by the function call.</returns>
         public JsValue Invoke(JsValue value, object thisObj, object[] arguments)
         public JsValue Invoke(JsValue value, object thisObj, object[] arguments)
         {
         {
-            var callable = value.TryCast<ICallable>();
+            var callable = value as ICallable ?? ExceptionHelper.ThrowArgumentException<ICallable>("Can only invoke functions");
 
 
-            if (callable == null)
-            {
-                throw new ArgumentException("Can only invoke functions");
-            }
-
-            var items = JsValueArrayPool.RentArray(arguments.Length);
+            var items = _jsValueArrayPool.RentArray(arguments.Length);
             for (int i = 0; i < arguments.Length; ++i)
             for (int i = 0; i < arguments.Length; ++i)
             {
             {
                 items[i] = JsValue.FromObject(this, arguments[i]);
                 items[i] = JsValue.FromObject(this, arguments[i]);
             }
             }
 
 
             var result = callable.Call(JsValue.FromObject(this, thisObj), items);
             var result = callable.Call(JsValue.FromObject(this, thisObj), items);
-            JsValueArrayPool.ReturnArray(items);
+            _jsValueArrayPool.ReturnArray(items);
 
 
             return result;
             return result;
         }
         }
@@ -859,9 +839,9 @@ namespace Jint
         {
         {
             AssertNotNullOrEmpty(nameof(propertyName), propertyName);
             AssertNotNullOrEmpty(nameof(propertyName), propertyName);
 
 
-            var reference = ReferencePool.Rent(scope, propertyName, _isStrict);
-            var jsValue = GetValue(reference);
-            ReferencePool.Return(reference);
+            var reference = _referencePool.Rent(scope, propertyName, _isStrict);
+            var jsValue = GetValue(reference, false);
+            _referencePool.Return(reference);
             return jsValue;
             return jsValue;
         }
         }
 
 
@@ -873,27 +853,82 @@ namespace Jint
             FunctionInstance functionInstance,
             FunctionInstance functionInstance,
             JsValue[] arguments)
             JsValue[] arguments)
         {
         {
-            var env = ExecutionContext.VariableEnvironment.Record;
+            var env = ExecutionContext.VariableEnvironment._record;
             bool configurableBindings = declarationBindingType == DeclarationBindingType.EvalCode;
             bool configurableBindings = declarationBindingType == DeclarationBindingType.EvalCode;
             var strict = StrictModeScope.IsStrictModeCode;
             var strict = StrictModeScope.IsStrictModeCode;
 
 
+            var der = env as DeclarativeEnvironmentRecord;
+            bool canReleaseArgumentsInstance = false;
             if (declarationBindingType == DeclarationBindingType.FunctionCode)
             if (declarationBindingType == DeclarationBindingType.FunctionCode)
             {
             {
-                var parameters = functionInstance.FormalParameters;
-                for (var i = 0; i < parameters.Length; i++)
+                var argsObj = _argumentsInstancePool.Rent(functionInstance, functionInstance._formalParameters, arguments, env, strict);
+                canReleaseArgumentsInstance = true;
+
+                if (!ReferenceEquals(der, null))
                 {
                 {
-                    var argName = parameters[i];
-                    var v = i + 1 > arguments.Length ? Undefined.Instance : arguments[i];
-                    var argAlreadyDeclared = env.HasBinding(argName);
-                    if (!argAlreadyDeclared)
+                    der.AddFunctionParameters(functionInstance, arguments, argsObj);
+                }
+                else
+                {
+                    // slow path
+                    var parameters = functionInstance._formalParameters;
+                    for (var i = 0; i < parameters.Length; i++)
                     {
                     {
-                        env.CreateMutableBinding(argName, v);
+                        var argName = parameters[i];
+                        var v = i + 1 > arguments.Length ? Undefined.Instance : arguments[i];
+                        var argAlreadyDeclared = env.HasBinding(argName);
+                        if (!argAlreadyDeclared)
+                        {
+                            env.CreateMutableBinding(argName, v);
+                        }
+
+                        env.SetMutableBinding(argName, v, strict);
                     }
                     }
+                    env.CreateMutableBinding("arguments", argsObj);
+                }
+            }
+
+            if (functionDeclarations.Count > 0)
+            {
+                AddFunctionDeclarations(functionDeclarations, env, configurableBindings, strict);
+            }
+
+            if (variableDeclarations.Count == 0)
+            {
+                return canReleaseArgumentsInstance;
+            }
 
 
-                    env.SetMutableBinding(argName, v, strict);
+            // process all variable declarations in the current parser scope
+            if (!ReferenceEquals(der, null))
+            {
+                der.AddVariableDeclarations(variableDeclarations);
+            }
+            else
+            {
+                // slow path
+                var variableDeclarationsCount = variableDeclarations.Count;
+                for (var i = 0; i < variableDeclarationsCount; i++)
+                {
+                    var variableDeclaration = variableDeclarations[i];
+                    var declarationsCount = variableDeclaration.Declarations.Count;
+                    for (var j = 0; j < declarationsCount; j++)
+                    {
+                        var d = variableDeclaration.Declarations[j];
+                        var dn = ((Identifier) d.Id).Name;
+                        var varAlreadyDeclared = env.HasBinding(dn);
+                        if (!varAlreadyDeclared)
+                        {
+                            env.CreateMutableBinding(dn, Undefined.Instance);
+                        }
+                    }
                 }
                 }
             }
             }
 
 
+            return canReleaseArgumentsInstance;
+        }
+
+        private void AddFunctionDeclarations(List<FunctionDeclaration> functionDeclarations, EnvironmentRecord env, bool configurableBindings, bool strict)
+        {
             var functionDeclarationsCount = functionDeclarations.Count;
             var functionDeclarationsCount = functionDeclarations.Count;
             for (var i = 0; i < functionDeclarationsCount; i++)
             for (var i = 0; i < functionDeclarationsCount; i++)
             {
             {
@@ -907,7 +942,7 @@ namespace Jint
                 }
                 }
                 else
                 else
                 {
                 {
-                    if (ReferenceEquals(env, GlobalEnvironment.Record))
+                    if (ReferenceEquals(env, GlobalEnvironment._record))
                     {
                     {
                         var go = Global;
                         var go = Global;
                         var existingProp = go.GetProperty(fn);
                         var existingProp = go.GetProperty(fn);
@@ -926,7 +961,7 @@ namespace Jint
                         {
                         {
                             if (existingProp.IsAccessorDescriptor() || !existingProp.Enumerable)
                             if (existingProp.IsAccessorDescriptor() || !existingProp.Enumerable)
                             {
                             {
-                                throw new JavaScriptException(TypeError);
+                                ExceptionHelper.ThrowTypeError(this);
                             }
                             }
                         }
                         }
                     }
                     }
@@ -934,49 +969,6 @@ namespace Jint
 
 
                 env.SetMutableBinding(fn, fo, strict);
                 env.SetMutableBinding(fn, fo, strict);
             }
             }
-
-            bool canReleaseArgumentsInstance = false;
-            if (declarationBindingType == DeclarationBindingType.FunctionCode && !env.HasBinding("arguments"))
-            {
-                var argsObj = ArgumentsInstancePool.Rent(functionInstance, functionInstance.FormalParameters, arguments, env, strict);
-                canReleaseArgumentsInstance = true;
-
-                if (strict)
-                {
-                    var declEnv = env as DeclarativeEnvironmentRecord;
-
-                    if (ReferenceEquals(declEnv, null))
-                    {
-                        throw new ArgumentException();
-                    }
-
-                    declEnv.CreateImmutableBinding("arguments", argsObj);
-                }
-                else
-                {
-                    env.CreateMutableBinding("arguments", argsObj);
-                }
-            }
-
-            // process all variable declarations in the current parser scope
-            var variableDeclarationsCount = variableDeclarations.Count;
-            for (var i = 0; i < variableDeclarationsCount; i++)
-            {
-                var variableDeclaration = variableDeclarations[i];
-                var declarationsCount = variableDeclaration.Declarations.Count;
-                for (var j = 0; j < declarationsCount; j++)
-                {
-                    var d = variableDeclaration.Declarations[j];
-                    var dn = ((Identifier) d.Id).Name;
-                    var varAlreadyDeclared = env.HasBinding(dn);
-                    if (!varAlreadyDeclared)
-                    {
-                        env.CreateMutableBinding(dn, Undefined.Instance);
-                    }
-                }
-            }
-
-            return canReleaseArgumentsInstance;
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -984,43 +976,13 @@ namespace Jint
         {
         {
             _executionContexts.ReplaceTopLexicalEnvironment(newEnv);
             _executionContexts.ReplaceTopLexicalEnvironment(newEnv);
         }
         }
-
-        private static void ThrowTimeoutException()
-        {
-            throw new TimeoutException();
-        }
-
-        private static void ThrowStatementsCountOverflowException()
-        {
-            throw new StatementsCountOverflowException();
-        }
-
-        private static void ThrowArgumentOutOfRange()
-        {
-            throw new ArgumentOutOfRangeException();
-        }
-        
-        private static void ThrowArgumentNullException()
-        {
-            throw new ArgumentNullException();
-        }
-        
-        private void ThrowReferenceError()
-        {
-            throw new JavaScriptException(ReferenceError);
-        }
         
         
         private static void AssertNotNullOrEmpty(string propertyname, string propertyValue)
         private static void AssertNotNullOrEmpty(string propertyname, string propertyValue)
         {
         {
             if (string.IsNullOrEmpty(propertyValue))
             if (string.IsNullOrEmpty(propertyValue))
             {
             {
-                throw new ArgumentException(propertyname);
+                ExceptionHelper.ThrowArgumentException(propertyname);
             }
             }
         }
         }
-        
-        private void ThrowTypeError()
-        {
-            throw new JavaScriptException(TypeError);
-        }
     }
     }
 }
 }

+ 1 - 1
Jint/Jint.csproj

@@ -9,4 +9,4 @@
   <ItemGroup>
   <ItemGroup>
     <PackageReference Include="Esprima" Version="1.0.0-beta-1036" />
     <PackageReference Include="Esprima" Version="1.0.0-beta-1036" />
   </ItemGroup>
   </ItemGroup>
-</Project>
+</Project>

+ 5 - 11
Jint/JsValueExtensions.cs

@@ -1,5 +1,4 @@
-using System;
-using System.Runtime.CompilerServices;
+using System.Runtime.CompilerServices;
 using Jint.Native;
 using Jint.Native;
 using Jint.Runtime;
 using Jint.Runtime;
 
 
@@ -12,7 +11,7 @@ namespace Jint
         {
         {
             if (value._type != Types.Boolean)
             if (value._type != Types.Boolean)
             {
             {
-                ThrowWrongTypeException("The value is not a boolean");
+                ExceptionHelper.ThrowArgumentException("The value is not a boolean");
             }
             }
 
 
             return ((JsBoolean) value)._value;
             return ((JsBoolean) value)._value;
@@ -23,7 +22,7 @@ namespace Jint
         {
         {
             if (value._type != Types.Number)
             if (value._type != Types.Number)
             {
             {
-                ThrowWrongTypeException("The value is not a number");
+                ExceptionHelper.ThrowArgumentException("The value is not a number");
             }
             }
 
 
             return ((JsNumber) value)._value;
             return ((JsNumber) value)._value;
@@ -34,7 +33,7 @@ namespace Jint
         {
         {
             if (value._type != Types.String)
             if (value._type != Types.String)
             {
             {
-                ThrowWrongTypeException("The value is not a string");
+                ExceptionHelper.ThrowArgumentException("The value is not a string");
             }
             }
 
 
             return AsStringWithoutTypeCheck(value);
             return AsStringWithoutTypeCheck(value);
@@ -51,15 +50,10 @@ namespace Jint
         {
         {
             if (value._type != Types.Symbol)
             if (value._type != Types.Symbol)
             {
             {
-                ThrowWrongTypeException("The value is not a symbol");
+                ExceptionHelper.ThrowArgumentException("The value is not a symbol");
             }
             }
 
 
             return ((JsSymbol) value)._value;
             return ((JsSymbol) value)._value;
         }
         }
-        
-        private static void ThrowWrongTypeException(string message)
-        {
-            throw new ArgumentException(message);
-        }
     }
     }
 }
 }

+ 15 - 11
Jint/Native/Argument/ArgumentsInstance.cs

@@ -12,7 +12,7 @@ namespace Jint.Native.Argument
     /// <summary>
     /// <summary>
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.6
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.6
     /// </summary>
     /// </summary>
-    public class ArgumentsInstance : ObjectInstance
+    public sealed class ArgumentsInstance : ObjectInstance
     {
     {
         // cache key container for array iteration for less allocations
         // cache key container for array iteration for less allocations
         private static readonly ThreadLocal<HashSet<string>> _mappedNamed = new ThreadLocal<HashSet<string>>(() => new HashSet<string>());
         private static readonly ThreadLocal<HashSet<string>> _mappedNamed = new ThreadLocal<HashSet<string>>(() => new HashSet<string>());
@@ -29,21 +29,25 @@ namespace Jint.Native.Argument
         {
         {
         }
         }
 
 
-        internal void Prepare(FunctionInstance func, string[] names, JsValue[] args, EnvironmentRecord env, bool strict)
+        internal void Prepare(
+            FunctionInstance func, 
+            string[] names, 
+            JsValue[] args, 
+            EnvironmentRecord env, 
+            bool strict)
         {
         {
-            Clear();
-
             _func = func;
             _func = func;
             _names = names;
             _names = names;
             _args = args;
             _args = args;
             _env = env;
             _env = env;
             _strict = strict;
             _strict = strict;
 
 
+            _properties?.Clear();
+            _intrinsicProperties?.Clear();
+
             _initialized = false;
             _initialized = false;
         }
         }
 
 
-        public bool Strict { get; set; }
-
         protected override void EnsureInitialized()
         protected override void EnsureInitialized()
         {
         {
             if (_initialized)
             if (_initialized)
@@ -105,7 +109,7 @@ namespace Jint.Native.Argument
         {
         {
             EnsureInitialized();
             EnsureInitialized();
 
 
-            if (!Strict && !ReferenceEquals(ParameterMap, null))
+            if (!_strict && !ReferenceEquals(ParameterMap, null))
             {
             {
                 var desc = base.GetOwnProperty(propertyName);
                 var desc = base.GetOwnProperty(propertyName);
                 if (desc == PropertyDescriptor.Undefined)
                 if (desc == PropertyDescriptor.Undefined)
@@ -136,7 +140,7 @@ namespace Jint.Native.Argument
             {
             {
                 if (throwOnError)
                 if (throwOnError)
                 {
                 {
-                    throw new JavaScriptException(Engine.TypeError);
+                    ExceptionHelper.ThrowTypeError(Engine);
                 }
                 }
 
 
                 return;
                 return;
@@ -170,7 +174,7 @@ namespace Jint.Native.Argument
         {
         {
             EnsureInitialized();
             EnsureInitialized();
 
 
-            if (!Strict && !ReferenceEquals(ParameterMap, null))
+            if (!_strict && !ReferenceEquals(ParameterMap, null))
             {
             {
                 var map = ParameterMap;
                 var map = ParameterMap;
                 var isMapped = map.GetOwnProperty(propertyName);
                 var isMapped = map.GetOwnProperty(propertyName);
@@ -179,7 +183,7 @@ namespace Jint.Native.Argument
                 {
                 {
                     if (throwOnError)
                     if (throwOnError)
                     {
                     {
-                        throw new JavaScriptException(Engine.TypeError);
+                        ExceptionHelper.ThrowTypeError(Engine);
                     }
                     }
                 }
                 }
 
 
@@ -214,7 +218,7 @@ namespace Jint.Native.Argument
         {
         {
             EnsureInitialized();
             EnsureInitialized();
 
 
-            if (!Strict && !ReferenceEquals(ParameterMap, null))
+            if (!_strict && !ReferenceEquals(ParameterMap, null))
             {
             {
                 var map = ParameterMap;
                 var map = ParameterMap;
                 var isMapped = map.GetOwnProperty(propertyName);
                 var isMapped = map.GetOwnProperty(propertyName);

+ 16 - 13
Jint/Native/Array/ArrayConstructor.cs

@@ -1,5 +1,5 @@
-using System;
-using System.Collections;
+using System.Collections;
+using System.Runtime.CompilerServices;
 using Jint.Native.Function;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime;
@@ -74,7 +74,7 @@ namespace Jint.Native.Array
         {
         {
             if (capacity < 0)
             if (capacity < 0)
             {
             {
-                throw new ArgumentException("invalid array length", nameof(capacity));
+                ExceptionHelper.ThrowArgumentException("invalid array length", nameof(capacity));
             }
             }
             return Construct(System.Array.Empty<JsValue>(), (uint) capacity);
             return Construct(System.Array.Empty<JsValue>(), (uint) capacity);
         }
         }
@@ -93,32 +93,32 @@ namespace Jint.Native.Array
             if (arguments.Length == 1 && arguments.At(0).IsNumber())
             if (arguments.Length == 1 && arguments.At(0).IsNumber())
             {
             {
                 var length = TypeConverter.ToUint32(arguments.At(0));
                 var length = TypeConverter.ToUint32(arguments.At(0));
-                if (!TypeConverter.ToNumber(arguments[0]).Equals(length))
+                if (((JsNumber) arguments[0])._value != length)
                 {
                 {
-                    throw new JavaScriptException(Engine.RangeError, "Invalid array length");
+                    ExceptionHelper.ThrowRangeError(_engine, "Invalid array length");
                 }
                 }
 
 
-                instance.SetOwnProperty("length", new PropertyDescriptor(length, PropertyFlag.OnlyWritable));
+                instance._length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
             }
             }
             else if (arguments.Length == 1 && arguments[0] is ObjectWrapper objectWrapper)
             else if (arguments.Length == 1 && arguments[0] is ObjectWrapper objectWrapper)
             {
             {
                 if (objectWrapper.Target is IEnumerable enumerable)
                 if (objectWrapper.Target is IEnumerable enumerable)
                 {
                 {
                     var jsArray = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
                     var jsArray = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
-                    var tempArray = Engine.JsValueArrayPool.RentArray(1);
+                    var tempArray = _engine._jsValueArrayPool.RentArray(1);
                     foreach (var item in enumerable)
                     foreach (var item in enumerable)
                     {
                     {
                         var jsItem = FromObject(Engine, item);
                         var jsItem = FromObject(Engine, item);
                         tempArray[0] = jsItem;
                         tempArray[0] = jsItem;
                         Engine.Array.PrototypeObject.Push(jsArray, tempArray);
                         Engine.Array.PrototypeObject.Push(jsArray, tempArray);
                     }
                     }
-                    Engine.JsValueArrayPool.ReturnArray(tempArray);
+                    _engine._jsValueArrayPool.ReturnArray(tempArray);
                     return jsArray;
                     return jsArray;
                 }
                 }
             }
             }
             else
             else
             {
             {
-                instance.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.OnlyWritable));
+                instance._length = new PropertyDescriptor(0, PropertyFlag.OnlyWritable);
                 if (arguments.Length > 0)
                 if (arguments.Length > 0)
                 {
                 {
                     PrototypeObject.Push(instance, arguments);
                     PrototypeObject.Push(instance, arguments);
@@ -128,12 +128,15 @@ namespace Jint.Native.Array
             return instance;
             return instance;
         }
         }
 
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal ArrayInstance ConstructFast(uint length)
         internal ArrayInstance ConstructFast(uint length)
         {
         {
-            var instance = new ArrayInstance(Engine, length);
-            instance.Prototype = PrototypeObject;
-            instance.Extensible = true;
-            instance.SetOwnProperty("length", new PropertyDescriptor(length, PropertyFlag.OnlyWritable));
+            var instance = new ArrayInstance(Engine, length)
+            {
+                Prototype = PrototypeObject,
+                Extensible = true,
+                _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable)
+            };
             return instance;
             return instance;
         }
         }
     }
     }

+ 31 - 43
Jint/Native/Array/ArrayInstance.cs

@@ -16,7 +16,7 @@ namespace Jint.Native.Array
         private const string PropertyNameLength = "length";
         private const string PropertyNameLength = "length";
         private const int PropertyNameLengthLength = 6;
         private const int PropertyNameLengthLength = 6;
 
 
-        private PropertyDescriptor _length;
+        internal PropertyDescriptor _length;
 
 
         private const int MaxDenseArrayLength = 1024 * 10;
         private const int MaxDenseArrayLength = 1024 * 10;
 
 
@@ -69,7 +69,7 @@ namespace Jint.Native.Array
             {
             {
                 if (throwOnError)
                 if (throwOnError)
                 {
                 {
-                    throw new JavaScriptException(Engine.TypeError);
+                    ExceptionHelper.ThrowTypeError(Engine);
                 }
                 }
 
 
                 return;
                 return;
@@ -116,7 +116,7 @@ namespace Jint.Native.Array
                 uint newLen = TypeConverter.ToUint32(value);
                 uint newLen = TypeConverter.ToUint32(value);
                 if (newLen != TypeConverter.ToNumber(value))
                 if (newLen != TypeConverter.ToNumber(value))
                 {
                 {
-                    throw new JavaScriptException(_engine.RangeError);
+                    ExceptionHelper.ThrowRangeError(_engine);
                 }
                 }
 
 
                 newLenDesc.Value = newLen;
                 newLenDesc.Value = newLen;
@@ -129,7 +129,7 @@ namespace Jint.Native.Array
                 {
                 {
                     if (throwOnError)
                     if (throwOnError)
                     {
                     {
-                        throw new JavaScriptException(_engine.TypeError);
+                        ExceptionHelper.ThrowTypeError(_engine);
                     }
                     }
 
 
                     return false;
                     return false;
@@ -180,7 +180,7 @@ namespace Jint.Native.Array
 
 
                                     if (throwOnError)
                                     if (throwOnError)
                                     {
                                     {
-                                        throw new JavaScriptException(_engine.TypeError);
+                                        ExceptionHelper.ThrowTypeError(_engine);
                                     }
                                     }
 
 
                                     return false;
                                     return false;
@@ -214,7 +214,7 @@ namespace Jint.Native.Array
 
 
                                     if (throwOnError)
                                     if (throwOnError)
                                     {
                                     {
-                                        throw new JavaScriptException(_engine.TypeError);
+                                        ExceptionHelper.ThrowTypeError(_engine);
                                     }
                                     }
 
 
                                     return false;
                                     return false;
@@ -242,7 +242,7 @@ namespace Jint.Native.Array
 
 
                             if (throwOnError)
                             if (throwOnError)
                             {
                             {
-                                throw new JavaScriptException(_engine.TypeError);
+                                ExceptionHelper.ThrowTypeError(_engine);
                             }
                             }
 
 
                             return false;
                             return false;
@@ -263,7 +263,7 @@ namespace Jint.Native.Array
                 {
                 {
                     if (throwOnError)
                     if (throwOnError)
                     {
                     {
-                        throw new JavaScriptException(_engine.TypeError);
+                        ExceptionHelper.ThrowTypeError(_engine);
                     }
                     }
 
 
                     return false;
                     return false;
@@ -274,7 +274,7 @@ namespace Jint.Native.Array
                 {
                 {
                     if (throwOnError)
                     if (throwOnError)
                     {
                     {
-                        throw new JavaScriptException(_engine.TypeError);
+                        ExceptionHelper.ThrowTypeError(_engine);
                     }
                     }
 
 
                     return false;
                     return false;
@@ -295,7 +295,7 @@ namespace Jint.Native.Array
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public uint GetLength()
         public uint GetLength()
         {
         {
-            return (uint) ((JsNumber) _length.Value)._value;
+            return (uint) ((JsNumber) _length._value)._value;
         }
         }
 
 
         protected override void AddProperty(string propertyName, PropertyDescriptor descriptor)
         protected override void AddProperty(string propertyName, PropertyDescriptor descriptor)
@@ -394,7 +394,7 @@ namespace Jint.Native.Array
             {
             {
                 return index < GetLength()
                 return index < GetLength()
                        && (_sparse == null || _sparse.ContainsKey(index))
                        && (_sparse == null || _sparse.ContainsKey(index))
-                       && (_dense == null || (index < _dense.Length && _dense[index] != null));
+                       && (_dense == null || (index < (uint) _dense.Length && _dense[index] != null));
             }
             }
 
 
             if (p == PropertyNameLength)
             if (p == PropertyNameLength)
@@ -526,28 +526,16 @@ namespace Jint.Native.Array
         {
         {
             value = Undefined;
             value = Undefined;
 
 
-            if (!TryGetDescriptor(index, out var desc)
-                || desc == null
-                || desc == PropertyDescriptor.Undefined
-                || (ReferenceEquals(desc.Value, null) && ReferenceEquals(desc.Get, null)))
-            {
-                desc = GetProperty(TypeConverter.ToString(index));
-            }
-
-            if (desc != null && desc != PropertyDescriptor.Undefined)
-            {
-                bool success = desc.TryGetValue(this, out value);
-                return success;
-            }
-
-            return false;
+            TryGetDescriptor(index, out var desc);
+            desc = desc ?? GetProperty(TypeConverter.ToString(index)) ?? PropertyDescriptor.Undefined; 
+            return desc.TryGetValue(this, out value);
         }
         }
 
 
         internal bool DeleteAt(uint index)
         internal bool DeleteAt(uint index)
         {
         {
             if (_dense != null)
             if (_dense != null)
             {
             {
-                if (index < _dense.Length)
+                if (index < (uint) _dense.Length)
                 {
                 {
                     _dense[index] = null;
                     _dense[index] = null;
                     return true;
                     return true;
@@ -566,13 +554,12 @@ namespace Jint.Native.Array
         {
         {
             if (_dense != null)
             if (_dense != null)
             {
             {
-                if (index >= _dense.Length)
+                descriptor = null;
+                if (index < (uint) _dense.Length)
                 {
                 {
-                    descriptor = null;
-                    return false;
+                    descriptor = _dense[index];
                 }
                 }
 
 
-                descriptor = _dense[index];
                 return descriptor != null;
                 return descriptor != null;
             }
             }
 
 
@@ -583,7 +570,7 @@ namespace Jint.Native.Array
         internal void WriteArrayValue(uint index, PropertyDescriptor desc)
         internal void WriteArrayValue(uint index, PropertyDescriptor desc)
         {
         {
             // calculate eagerly so we know if we outgrow
             // calculate eagerly so we know if we outgrow
-            var newSize = _dense != null && index >= _dense.Length
+            var newSize = _dense != null && index >= (uint) _dense.Length
                 ? System.Math.Max(index, System.Math.Max(_dense.Length, 2)) * 2
                 ? System.Math.Max(index, System.Math.Max(_dense.Length, 2)) * 2
                 : 0;
                 : 0;
 
 
@@ -594,7 +581,8 @@ namespace Jint.Native.Array
 
 
             if (canUseDense)
             if (canUseDense)
             {
             {
-                if (index >= _dense.Length)
+                var temp = _dense;
+                if (index >= (uint) temp.Length)
                 {
                 {
                     EnsureCapacity((uint) newSize);
                     EnsureCapacity((uint) newSize);
                 }
                 }
@@ -615,7 +603,7 @@ namespace Jint.Native.Array
         {
         {
             _sparse = new Dictionary<uint, PropertyDescriptor>(_dense.Length <= 1024 ? _dense.Length : 0);
             _sparse = new Dictionary<uint, PropertyDescriptor>(_dense.Length <= 1024 ? _dense.Length : 0);
             // need to move data
             // need to move data
-            for (uint i = 0; i < _dense.Length; ++i)
+            for (uint i = 0; i < (uint) _dense.Length; ++i)
             {
             {
                 if (_dense[i] != null)
                 if (_dense[i] != null)
                 {
                 {
@@ -629,7 +617,7 @@ namespace Jint.Native.Array
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal void EnsureCapacity(uint capacity)
         internal void EnsureCapacity(uint capacity)
         {
         {
-            if (capacity > _dense.Length)
+            if (capacity > (uint) _dense.Length)
             {
             {
                 // need to grow
                 // need to grow
                 var newArray = new PropertyDescriptor[capacity];
                 var newArray = new PropertyDescriptor[capacity];
@@ -683,7 +671,7 @@ namespace Jint.Native.Array
                 }
                 }
                 else
                 else
                 {
                 {
-                    DefineOwnProperty(TypeConverter.ToString(n), desc, true);
+                    DefineOwnProperty(TypeConverter.ToString((uint) n), desc, true);
                 }
                 }
                 n++;
                 n++;
             }
             }
@@ -710,17 +698,17 @@ namespace Jint.Native.Array
 
 
             var callable = GetCallable(callbackfn);
             var callable = GetCallable(callbackfn);
             var a = Engine.Array.ConstructFast(len);
             var a = Engine.Array.ConstructFast(len);
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            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))
                 {
                 {
                     args[0] = kvalue;
                     args[0] = kvalue;
                     args[1] = k;
                     args[1] = k;
-                    args[2] = this;
                     var mappedValue = callable.Call(thisArg, args);
                     var mappedValue = callable.Call(thisArg, args);
                     var desc = new PropertyDescriptor(mappedValue, PropertyFlag.ConfigurableEnumerableWritable);
                     var desc = new PropertyDescriptor(mappedValue, PropertyFlag.ConfigurableEnumerableWritable);
-                    if (a._dense != null && k < a._dense.Length)
+                    if (a._dense != null && k < (uint) a._dense.Length)
                     {
                     {
                         a._dense[k] = desc;
                         a._dense[k] = desc;
                     }
                     }
@@ -731,7 +719,7 @@ namespace Jint.Native.Array
                 }
                 }
             }
             }
 
 
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
             return a;
             return a;
         }
         }
         
         
@@ -753,14 +741,14 @@ namespace Jint.Native.Array
             var thisArg = arguments.At(1);
             var thisArg = arguments.At(1);
             var callable = GetCallable(callbackfn);
             var callable = GetCallable(callbackfn);
 
 
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            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))
                 {
                 {
                     args[0] = kvalue;
                     args[0] = kvalue;
                     args[1] = k;
                     args[1] = k;
-                    args[2] = this;
                     var testResult = callable.Call(thisArg, args);
                     var testResult = callable.Call(thisArg, args);
                     if (TypeConverter.ToBoolean(testResult))
                     if (TypeConverter.ToBoolean(testResult))
                     {
                     {
@@ -771,7 +759,7 @@ namespace Jint.Native.Array
                 }
                 }
             }
             }
 
 
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
 
 
             index = 0;
             index = 0;
             value = Undefined;
             value = Undefined;

+ 32 - 31
Jint/Native/Array/ArrayPrototype.cs

@@ -116,7 +116,7 @@ namespace Jint.Native.Array
 
 
             if (len == 0 && arguments.Length < 2)
             if (len == 0 && arguments.Length < 2)
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var k = 0;
             var k = 0;
@@ -140,7 +140,7 @@ namespace Jint.Native.Array
 
 
                 if (kPresent == false)
                 if (kPresent == false)
                 {
                 {
-                    throw new JavaScriptException(Engine.TypeError);
+                    ExceptionHelper.ThrowTypeError(Engine);
                 }
                 }
             }
             }
 
 
@@ -176,14 +176,14 @@ namespace Jint.Native.Array
             var a = Engine.Array.ConstructFast(0);
             var a = Engine.Array.ConstructFast(0);
 
 
             uint to = 0;
             uint to = 0;
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o.Target;
             for (uint k = 0; k < len; k++)
             for (uint k = 0; k < len; k++)
             {
             {
                 if (o.TryGetValue(k, out var kvalue))
                 if (o.TryGetValue(k, out var kvalue))
                 {
                 {
                     args[0] = kvalue;
                     args[0] = kvalue;
                     args[1] = k;
                     args[1] = k;
-                    args[2] = o.Target;
                     var selected = callable.Call(thisArg, args);
                     var selected = callable.Call(thisArg, args);
                     if (TypeConverter.ToBoolean(selected))
                     if (TypeConverter.ToBoolean(selected))
                     {
                     {
@@ -194,7 +194,7 @@ namespace Jint.Native.Array
             }
             }
 
 
             a.SetLength(to);
             a.SetLength(to);
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
 
 
             return a;
             return a;
         }
         }
@@ -213,27 +213,20 @@ namespace Jint.Native.Array
             var thisArg = arguments.At(1);
             var thisArg = arguments.At(1);
             var callable = GetCallable(callbackfn);
             var callable = GetCallable(callbackfn);
 
 
-            var jsValues = Engine.JsValueArrayPool.RentArray(1);
-            jsValues[0] = len;
-            var a = Engine.Array.Construct(jsValues, len);
-            Engine.JsValueArrayPool.ReturnArray(jsValues);
-            
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var a = Engine.Array.ConstructFast(len);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o.Target;
             for (uint k = 0; k < len; k++)
             for (uint k = 0; k < len; k++)
             {
             {
                 if (o.TryGetValue(k, out var kvalue))
                 if (o.TryGetValue(k, out var kvalue))
                 {
                 {
                     args[0] = kvalue;
                     args[0] = kvalue;
                     args[1] = k;
                     args[1] = k;
-                    args[2] = o.Target;
                     var mappedValue = callable.Call(thisArg, args);
                     var mappedValue = callable.Call(thisArg, args);
                     a.SetIndexValue(k, mappedValue, updateLength: false);
                     a.SetIndexValue(k, mappedValue, updateLength: false);
                 }
                 }
             }
             }
-
-            a.SetLength(len);
-            Engine.JsValueArrayPool.ReturnArray(args);
-
+            _engine._jsValueArrayPool.ReturnArray(args);
             return a;
             return a;
         }
         }
 
 
@@ -247,18 +240,18 @@ namespace Jint.Native.Array
 
 
             var callable = GetCallable(callbackfn);
             var callable = GetCallable(callbackfn);
 
 
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o.Target;
             for (uint k = 0; k < len; k++)
             for (uint k = 0; k < len; k++)
             {
             {
                 if (o.TryGetValue(k, out var kvalue))
                 if (o.TryGetValue(k, out var kvalue))
                 {
                 {
                     args[0] = kvalue;
                     args[0] = kvalue;
                     args[1] = k;
                     args[1] = k;
-                    args[2] = o.Target;
                     callable.Call(thisArg, args);
                     callable.Call(thisArg, args);
                 }
                 }
             }
             }
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
 
 
             return Undefined;
             return Undefined;
         }
         }
@@ -279,14 +272,14 @@ namespace Jint.Native.Array
 
 
             var callable = GetCallable(callbackfn);
             var callable = GetCallable(callbackfn);
 
 
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o.Target;
             for (uint k = 0; k < len; k++)
             for (uint k = 0; k < len; k++)
             {
             {
                 if (o.TryGetValue(k, out var kvalue))
                 if (o.TryGetValue(k, out var kvalue))
                 {
                 {
                     args[0] = kvalue;
                     args[0] = kvalue;
                     args[1] = k;
                     args[1] = k;
-                    args[2] = o.Target;
                     var testResult = callable.Call(thisArg, args);
                     var testResult = callable.Call(thisArg, args);
                     if (false == TypeConverter.ToBoolean(testResult))
                     if (false == TypeConverter.ToBoolean(testResult))
                     {
                     {
@@ -294,7 +287,7 @@ namespace Jint.Native.Array
                     }
                     }
                 }
                 }
             }
             }
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
 
 
             return JsBoolean.True;
             return JsBoolean.True;
         }
         }
@@ -496,7 +489,7 @@ namespace Jint.Native.Array
         {
         {
             if (!thisObj.IsObject())
             if (!thisObj.IsObject())
             {
             {
-                throw new JavaScriptException(Engine.TypeError, "Array.prorotype.sort can only be applied on objects");
+                ExceptionHelper.ThrowTypeError(_engine, "Array.prorotype.sort can only be applied on objects");
             }
             }
 
 
             var obj = ArrayOperations.For(thisObj.AsObject());
             var obj = ArrayOperations.For(thisObj.AsObject());
@@ -511,7 +504,7 @@ namespace Jint.Native.Array
             ICallable compareFn = null;
             ICallable compareFn = null;
             if (!compareArg.IsUndefined())
             if (!compareArg.IsUndefined())
             {
             {
-                compareFn = compareArg.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "The sort argument must be a function"));
+                compareFn = compareArg.TryCast<ICallable>(x => ExceptionHelper.ThrowTypeError(_engine, "The sort argument must be a function"));
             }
             }
 
 
             int Comparer(JsValue x, JsValue y)
             int Comparer(JsValue x, JsValue y)
@@ -758,7 +751,7 @@ namespace Jint.Native.Array
             else
             else
             {
             {
                 var elementObj = TypeConverter.ToObject(Engine, firstElement);
                 var elementObj = TypeConverter.ToObject(Engine, firstElement);
-                var func = elementObj.Get("toLocaleString").TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError));
+                var func = elementObj.Get("toLocaleString") as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(_engine);
 
 
                 r = func.Call(elementObj, Arguments.Empty);
                 r = func.Call(elementObj, Arguments.Empty);
             }
             }
@@ -773,7 +766,7 @@ namespace Jint.Native.Array
                 else
                 else
                 {
                 {
                     var elementObj = TypeConverter.ToObject(Engine, nextElement);
                     var elementObj = TypeConverter.ToObject(Engine, nextElement);
-                    var func = elementObj.Get("toLocaleString").TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError));
+                    var func = elementObj.Get("toLocaleString") as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(_engine);
                     r = func.Call(elementObj, Arguments.Empty);
                     r = func.Call(elementObj, Arguments.Empty);
                 }
                 }
 
 
@@ -833,7 +826,10 @@ namespace Jint.Native.Array
         {
         {
             var array = TypeConverter.ToObject(Engine, thisObj);
             var array = TypeConverter.ToObject(Engine, thisObj);
             ICallable func;
             ICallable func;
-            func = array.Get("join").TryCast<ICallable>(x => { func = Engine.Object.PrototypeObject.Get("toString").TryCast<ICallable>(y => throw new ArgumentException()); });
+            func = array.Get("join").TryCast<ICallable>(x =>
+            {
+                func = Engine.Object.PrototypeObject.Get("toString").TryCast<ICallable>(y => ExceptionHelper.ThrowArgumentException());
+            });
 
 
             return func.Call(array, Arguments.Empty);
             return func.Call(array, Arguments.Empty);
         }
         }
@@ -851,7 +847,7 @@ namespace Jint.Native.Array
 
 
             if (len == 0 && arguments.Length < 2)
             if (len == 0 && arguments.Length < 2)
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             int k = (int) len - 1;
             int k = (int) len - 1;
@@ -877,10 +873,11 @@ namespace Jint.Native.Array
 
 
                 if (kPresent == false)
                 if (kPresent == false)
                 {
                 {
-                    throw new JavaScriptException(Engine.TypeError);
+                    ExceptionHelper.ThrowTypeError(Engine);
                 }
                 }
             }
             }
 
 
+            var jsValues = new JsValue[4];
             for (; k >= 0; k--)
             for (; k >= 0; k--)
             {
             {
                 var pk = TypeConverter.ToString(k);
                 var pk = TypeConverter.ToString(k);
@@ -888,7 +885,11 @@ namespace Jint.Native.Array
                 if (kPresent)
                 if (kPresent)
                 {
                 {
                     var kvalue = o.Get(pk);
                     var kvalue = o.Get(pk);
-                    accumulator = callable.Call(Undefined, new[] {accumulator, kvalue, k, o});
+                    jsValues[0] = accumulator;
+                    jsValues[1] = kvalue;
+                    jsValues[2] = k;
+                    jsValues[3] = o;
+                    accumulator = callable.Call(Undefined, jsValues);
                 }
                 }
             }
             }
 
 
@@ -1041,7 +1042,7 @@ namespace Jint.Native.Array
 
 
                 public override uint GetSmallestIndex() => _array.GetSmallestIndex();
                 public override uint GetSmallestIndex() => _array.GetSmallestIndex();
 
 
-                public override uint GetLength() => _array.GetLength();
+                public override uint GetLength() => (uint) ((JsNumber) _array._length._value)._value;
 
 
                 public override void SetLength(uint length) => _array.Put("length", length, true);
                 public override void SetLength(uint length) => _array.Put("length", length, true);
 
 

+ 11 - 4
Jint/Native/Boolean/BooleanConstructor.cs

@@ -57,10 +57,17 @@ namespace Jint.Native.Boolean
 
 
         public BooleanInstance Construct(bool value)
         public BooleanInstance Construct(bool value)
         {
         {
-            var instance = new BooleanInstance(Engine);
-            instance.Prototype = PrototypeObject;
-            instance.PrimitiveValue = value;
-            instance.Extensible = true;
+            return Construct(value ? JsBoolean.True : JsBoolean.False);
+        }
+        
+        public BooleanInstance Construct(JsBoolean value)
+        {
+            var instance = new BooleanInstance(Engine)
+            {
+                Prototype = PrototypeObject,
+                PrimitiveValue = value,
+                Extensible = true
+            };
 
 
             return instance;
             return instance;
         }
         }

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

@@ -45,7 +45,8 @@ namespace Jint.Native.Boolean
                 return o.PrimitiveValue;
                 return o.PrimitiveValue;
             }
             }
 
 
-            throw new JavaScriptException(Engine.TypeError);
+            ExceptionHelper.ThrowTypeError(Engine);
+            return null;
         }
         }
 
 
         private JsValue ToBooleanString(JsValue thisObj, JsValue[] arguments)
         private JsValue ToBooleanString(JsValue thisObj, JsValue[] arguments)

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

@@ -86,7 +86,7 @@ namespace Jint.Native.Date
             if (!DateTime.TryParseExact(date, DefaultFormats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result))
             if (!DateTime.TryParseExact(date, DefaultFormats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result))
             {
             {
                 if (!DateTime.TryParseExact(date, SecondaryFormats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result))
                 if (!DateTime.TryParseExact(date, SecondaryFormats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result))
-                {
+            {
                     if (!DateTime.TryParse(date, Engine.Options._Culture, DateTimeStyles.AdjustToUniversal, out result))
                     if (!DateTime.TryParse(date, Engine.Options._Culture, DateTimeStyles.AdjustToUniversal, out result))
                     {
                     {
                         if (!DateTime.TryParse(date, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result))
                         if (!DateTime.TryParse(date, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result))
@@ -147,7 +147,7 @@ namespace Jint.Native.Date
         {
         {
             if (arguments.Length < 2)
             if (arguments.Length < 2)
             {
             {
-                throw new ArgumentOutOfRangeException(nameof(arguments), "There must be at least two arguments.");
+                ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(arguments), "There must be at least two arguments.");
             }
             }
 
 
             var y = TypeConverter.ToNumber(arguments[0]);
             var y = TypeConverter.ToNumber(arguments[0]);

+ 2 - 1
Jint/Native/Date/DateInstance.cs

@@ -21,7 +21,8 @@ namespace Jint.Native.Date
         {
         {
             if (double.IsNaN(PrimitiveValue) || PrimitiveValue > Max || PrimitiveValue < Min)
             if (double.IsNaN(PrimitiveValue) || PrimitiveValue > Max || PrimitiveValue < Min)
             {
             {
-                throw new JavaScriptException(Engine.RangeError);
+                ExceptionHelper.ThrowRangeError(Engine);
+                return DateTime.MinValue;
             }
             }
             else
             else
             {
             {

+ 17 - 27
Jint/Native/Date/DatePrototype.cs

@@ -92,7 +92,7 @@ namespace Jint.Native.Date
         {
         {
             return thisObj.TryCast<DateInstance>(value =>
             return thisObj.TryCast<DateInstance>(value =>
                {
                {
-                   throw new JavaScriptException(Engine.TypeError, "Invalid Date");
+                   ExceptionHelper.ThrowTypeError(_engine, "Invalid Date");
                });
                });
         }
         }
 
 
@@ -523,9 +523,9 @@ namespace Jint.Native.Date
         private JsValue ToUtcString(JsValue thisObj, JsValue[] arguments)
         private JsValue ToUtcString(JsValue thisObj, JsValue[] arguments)
         {
         {
             return thisObj.TryCast<DateInstance>(x =>
             return thisObj.TryCast<DateInstance>(x =>
-            {
-                throw new JavaScriptException(Engine.TypeError);
-            } )
+                {
+                    ExceptionHelper.ThrowTypeError(_engine);
+                } )
             .ToDateTime().ToUniversalTime().ToString("ddd MMM dd yyyy HH:mm:ss 'GMT'", CultureInfo.InvariantCulture);
             .ToDateTime().ToUniversalTime().ToString("ddd MMM dd yyyy HH:mm:ss 'GMT'", CultureInfo.InvariantCulture);
         }
         }
 
 
@@ -533,12 +533,12 @@ namespace Jint.Native.Date
         {
         {
             var t = thisObj.TryCast<DateInstance>(x =>
             var t = thisObj.TryCast<DateInstance>(x =>
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(_engine);
             }).PrimitiveValue;
             }).PrimitiveValue;
 
 
             if (double.IsInfinity(t) || double.IsNaN(t))
             if (double.IsInfinity(t) || double.IsNaN(t))
             {
             {
-                throw new JavaScriptException(Engine.RangeError);
+                ExceptionHelper.ThrowRangeError(_engine);
             }
             }
             double h = HourFromTime(t);
             double h = HourFromTime(t);
             double m = MinFromTime(t);
             double m = MinFromTime(t);
@@ -548,14 +548,7 @@ namespace Jint.Native.Date
             if (m < 0) { m += MinutesPerHour; }
             if (m < 0) { m += MinutesPerHour; }
             if (s < 0) { s += SecondsPerMinute; }
             if (s < 0) { s += SecondsPerMinute; }
             if (ms < 0) { ms += MsPerSecond; }
             if (ms < 0) { ms += MsPerSecond; }
-            return string.Format("{0:0000}-{1:00}-{2:00}T{3:00}:{4:00}:{5:00}.{6:000}Z",
-                YearFromTime(t),
-                MonthFromTime(t)+1,
-                DateFromTime(t),
-                h,
-                m,
-                s,
-                ms);
+            return $"{YearFromTime(t):0000}-{MonthFromTime(t) + 1:00}-{DateFromTime(t):00}T{h:00}:{m:00}:{s:00}.{ms:000}Z";
         }
         }
 
 
         private JsValue ToJSON(JsValue thisObj, JsValue[] arguments)
         private JsValue ToJSON(JsValue thisObj, JsValue[] arguments)
@@ -570,7 +563,7 @@ namespace Jint.Native.Date
             var toIso = o.Get("toISOString");
             var toIso = o.Get("toISOString");
             if (!toIso.Is<ICallable>())
             if (!toIso.Is<ICallable>())
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             return toIso.TryCast<ICallable>().Call(o, Arguments.Empty);
             return toIso.TryCast<ICallable>().Call(o, Arguments.Empty);
@@ -717,7 +710,8 @@ namespace Jint.Native.Date
                 return 1;
                 return 1;
             }
             }
 
 
-            throw new ArgumentException();
+            ExceptionHelper.ThrowArgumentException();
+            return 0;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -788,7 +782,8 @@ namespace Jint.Native.Date
                 return 11;
                 return 11;
             }
             }
 
 
-            throw new InvalidOperationException();
+            ExceptionHelper.ThrowInvalidOperationException();
+            return 0;
         }
         }
 
 
         public static double DayWithinYear(double t)
         public static double DayWithinYear(double t)
@@ -861,7 +856,8 @@ namespace Jint.Native.Date
                 return dayWithinYear - 333 - InLeapYear(t);
                 return dayWithinYear - 333 - InLeapYear(t);
             }
             }
 
 
-            throw new InvalidOperationException();
+            ExceptionHelper.ThrowInvalidOperationException();
+            return 0;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -872,13 +868,7 @@ namespace Jint.Native.Date
             return (Day(t) + 4)%7;
             return (Day(t) + 4)%7;
         }
         }
 
 
-        public double LocalTza
-        {
-            get
-            {
-                return Engine.Options._LocalTimeZone.BaseUtcOffset.TotalMilliseconds;
-            }
-        }
+        public double LocalTza => Engine.Options._LocalTimeZone.BaseUtcOffset.TotalMilliseconds;
 
 
         public double DaylightSavingTa(double t)
         public double DaylightSavingTa(double t)
         {
         {
@@ -1025,8 +1015,8 @@ namespace Jint.Native.Date
                 case 1:
                 case 1:
                     return 28 + leap;
                     return 28 + leap;
                 default:
                 default:
-                    throw new ArgumentOutOfRangeException(nameof(month));
-
+                    ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(month), "invalid month");
+                    return 0;
             }
             }
         }
         }
 
 

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

@@ -5,7 +5,7 @@ using Jint.Runtime.Descriptors;
 
 
 namespace Jint.Native.Error
 namespace Jint.Native.Error
 {
 {
-    public class ErrorConstructor : FunctionInstance, IConstructor
+    public sealed class ErrorConstructor : FunctionInstance, IConstructor
     {
     {
         private string _name;
         private string _name;
 
 

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

@@ -44,7 +44,7 @@ namespace Jint.Native.Error
             var o = thisObject.TryCast<ObjectInstance>();
             var o = thisObject.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var name = TypeConverter.ToString(o.Get("name"));
             var name = TypeConverter.ToString(o.Get("name"));

+ 11 - 6
Jint/Native/Function/BindFunctionInstance.cs

@@ -4,7 +4,7 @@ using Jint.Runtime;
 
 
 namespace Jint.Native.Function
 namespace Jint.Native.Function
 {
 {
-    public class BindFunctionInstance : FunctionInstance, IConstructor
+    public sealed class BindFunctionInstance : FunctionInstance, IConstructor
     {
     {
         public BindFunctionInstance(Engine engine) : base(engine, System.Array.Empty<string>(), null, false)
         public BindFunctionInstance(Engine engine) : base(engine, System.Array.Empty<string>(), null, false)
         {
         {
@@ -20,30 +20,35 @@ namespace Jint.Native.Function
         {
         {
             var f = TargetFunction.TryCast<FunctionInstance>(x =>
             var f = TargetFunction.TryCast<FunctionInstance>(x =>
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             });
             });
 
 
-            return f.Call(BoundThis, BoundArgs.Union(arguments).ToArray());
+            return f.Call(BoundThis, CreateArguments(arguments));
         }
         }
 
 
         public ObjectInstance Construct(JsValue[] arguments)
         public ObjectInstance Construct(JsValue[] arguments)
         {
         {
             var target = TargetFunction.TryCast<IConstructor>(x =>
             var target = TargetFunction.TryCast<IConstructor>(x =>
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             });
             });
 
 
-            return target.Construct(BoundArgs.Union(arguments).ToArray());
+            return target.Construct(CreateArguments(arguments));
         }
         }
 
 
         public override bool HasInstance(JsValue v)
         public override bool HasInstance(JsValue v)
         {
         {
             var f = TargetFunction.TryCast<FunctionInstance>(x =>
             var f = TargetFunction.TryCast<FunctionInstance>(x =>
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             });
             });
 
 
             return f.HasInstance(v);
             return f.HasInstance(v);
         }
         }
+
+        private JsValue[] CreateArguments(JsValue[] arguments)
+        {
+            return Enumerable.Union(BoundArgs, arguments).ToArray();
+        }
     }
     }
 }
 }

+ 11 - 8
Jint/Native/Function/EvalFunctionInstance.cs

@@ -6,7 +6,7 @@ using Jint.Runtime.Environments;
 
 
 namespace Jint.Native.Function
 namespace Jint.Native.Function
 {
 {
-    public class EvalFunctionInstance: FunctionInstance
+    public sealed class EvalFunctionInstance : FunctionInstance
     {
     {
         private static readonly ParserOptions ParserOptions = new ParserOptions { AdaptRegexp = true, Tolerant = false };
         private static readonly ParserOptions ParserOptions = new ParserOptions { AdaptRegexp = true, Tolerant = false };
 
 
@@ -23,12 +23,13 @@ namespace Jint.Native.Function
 
 
         public JsValue Call(JsValue thisObject, JsValue[] arguments, bool directCall)
         public JsValue Call(JsValue thisObject, JsValue[] arguments, bool directCall)
         {
         {
-            if (arguments.At(0).Type != Types.String)
+            var arg = arguments.At(0);
+            if (arg.Type != Types.String)
             {
             {
-                return arguments.At(0);
+                return arg;
             }
             }
 
 
-            var code = TypeConverter.ToString(arguments.At(0));
+            var code = TypeConverter.ToString(arg);
 
 
             try
             try
             {
             {
@@ -47,9 +48,10 @@ 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;
                             if (StrictModeScope.IsStrictModeCode)
                             if (StrictModeScope.IsStrictModeCode)
                             {
                             {
-                                strictVarEnv = LexicalEnvironment.NewDeclarativeEnvironment(Engine, Engine.ExecutionContext.LexicalEnvironment);
+                                strictVarEnv = LexicalEnvironment.NewDeclarativeEnvironment(Engine, lexicalEnvironment);
                                 Engine.EnterExecutionContext(strictVarEnv, strictVarEnv, Engine.ExecutionContext.ThisBinding);
                                 Engine.EnterExecutionContext(strictVarEnv, strictVarEnv, Engine.ExecutionContext.ThisBinding);
                             }
                             }
 
 
@@ -65,7 +67,7 @@ namespace Jint.Native.Function
 
 
                             // we can safely release arguments if they don't escape the scope
                             // we can safely release arguments if they don't escape the scope
                             if (argumentInstanceRented
                             if (argumentInstanceRented
-                                && Engine.ExecutionContext.LexicalEnvironment?.Record is DeclarativeEnvironmentRecord der
+                                && lexicalEnvironment?._record is DeclarativeEnvironmentRecord der
                                 && !(result.Value is ArgumentsInstance))
                                 && !(result.Value is ArgumentsInstance))
                             {
                             {
                                 der.ReleaseArguments();
                                 der.ReleaseArguments();
@@ -100,10 +102,11 @@ namespace Jint.Native.Function
             {
             {
                 if (e.Description == Messages.InvalidLHSInAssignment)
                 if (e.Description == Messages.InvalidLHSInAssignment)
                 {
                 {
-                    throw new JavaScriptException(Engine.ReferenceError);
+                    ExceptionHelper.ThrowReferenceError(_engine);
                 }
                 }
 
 
-                throw new JavaScriptException(Engine.SyntaxError);
+                ExceptionHelper.ThrowSyntaxError(_engine);
+                return null;
             }
             }
         }
         }
     }
     }

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

@@ -1,5 +1,4 @@
-using System;
-using Esprima;
+using Esprima;
 using Esprima.Ast;
 using Esprima.Ast;
 using Jint.Native.Object;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime;
@@ -77,7 +76,8 @@ namespace Jint.Native.Function
             }
             }
             catch (ParserException)
             catch (ParserException)
             {
             {
-                throw new JavaScriptException(Engine.SyntaxError);
+                ExceptionHelper.ThrowSyntaxError(_engine);
+                return null;
             }
             }
 
 
             var functionObject = new ScriptFunctionInstance(
             var functionObject = new ScriptFunctionInstance(
@@ -129,7 +129,7 @@ namespace Jint.Native.Function
         {
         {
             if (arguments.Length != 2)
             if (arguments.Length != 2)
             {
             {
-                throw new ArgumentException("Apply has to be called with two arguments.");
+                ExceptionHelper.ThrowArgumentException("Apply has to be called with two arguments.");
             }
             }
 
 
             var func = thisObject.TryCast<ICallable>();
             var func = thisObject.TryCast<ICallable>();
@@ -138,7 +138,7 @@ namespace Jint.Native.Function
 
 
             if (func == null)
             if (func == null)
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             if (argArray.IsNull() || argArray.IsUndefined())
             if (argArray.IsNull() || argArray.IsUndefined())
@@ -149,7 +149,7 @@ namespace Jint.Native.Function
             var argArrayObj = argArray.TryCast<ObjectInstance>();
             var argArrayObj = argArray.TryCast<ObjectInstance>();
             if (ReferenceEquals(argArrayObj, null))
             if (ReferenceEquals(argArrayObj, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var len = argArrayObj.Get("length");
             var len = argArrayObj.Get("length");

+ 17 - 13
Jint/Native/Function/FunctionInstance.cs

@@ -10,12 +10,15 @@ namespace Jint.Native.Function
     {
     {
         private const string PropertyNamePrototype = "prototype";
         private const string PropertyNamePrototype = "prototype";
         private const int PropertyNamePrototypeLength = 9;
         private const int PropertyNamePrototypeLength = 9;
-        private PropertyDescriptor _prototype;
+        protected PropertyDescriptor _prototype;
 
 
         private const string PropertyNameLength = "length";
         private const string PropertyNameLength = "length";
         private const int PropertyNameLengthLength = 6;
         private const int PropertyNameLengthLength = 6;
-        private PropertyDescriptor _length;
-        
+        protected PropertyDescriptor _length;
+        protected readonly LexicalEnvironment _scope;
+        protected internal readonly string[] _formalParameters;
+        private readonly bool _strict;
+
         protected FunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict)
         protected FunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict)
             : this(engine, parameters, scope, strict, objectClass: "Function")
             : this(engine, parameters, scope, strict, objectClass: "Function")
         {
         {
@@ -29,9 +32,9 @@ namespace Jint.Native.Function
             in string objectClass)
             in string objectClass)
             : base(engine, objectClass)
             : base(engine, objectClass)
         {
         {
-            FormalParameters = parameters;
-            Scope = scope;
-            Strict = strict;
+            _formalParameters = parameters;
+            _scope = scope;
+            _strict = strict;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -42,10 +45,11 @@ namespace Jint.Native.Function
         /// <returns></returns>
         /// <returns></returns>
         public abstract JsValue Call(JsValue thisObject, JsValue[] arguments);
         public abstract JsValue Call(JsValue thisObject, JsValue[] arguments);
 
 
-        public LexicalEnvironment Scope { get; }
+        public LexicalEnvironment Scope => _scope;
+
+        public string[] FormalParameters => _formalParameters;
 
 
-        public string[] FormalParameters { get; }
-        public bool Strict { get; }
+        public bool Strict => _strict;
 
 
         public virtual bool HasInstance(JsValue v)
         public virtual bool HasInstance(JsValue v)
         {
         {
@@ -58,14 +62,14 @@ namespace Jint.Native.Function
             var po = Get("prototype");
             var po = Get("prototype");
             if (!po.IsObject())
             if (!po.IsObject())
             {
             {
-                throw new JavaScriptException(_engine.TypeError, $"Function has non-object prototype '{TypeConverter.ToString(po)}' in instanceof check");
+                ExceptionHelper.ThrowTypeError(_engine, $"Function has non-object prototype '{TypeConverter.ToString(po)}' in instanceof check");
             }
             }
 
 
             var o = po.AsObject();
             var o = po.AsObject();
 
 
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(_engine.TypeError);
+                ExceptionHelper.ThrowTypeError(_engine);
             }
             }
 
 
             while (true)
             while (true)
@@ -95,9 +99,9 @@ namespace Jint.Native.Function
 
 
             if (propertyName.Length == 6
             if (propertyName.Length == 6
                 && propertyName == "caller"
                 && propertyName == "caller"
-                && ((v.As<FunctionInstance>()?.Strict).GetValueOrDefault()))
+                && ((v.As<FunctionInstance>()?._strict).GetValueOrDefault()))
             {
             {
-                throw new JavaScriptException(_engine.TypeError);
+                ExceptionHelper.ThrowTypeError(_engine);
             }
             }
 
 
             return v;
             return v;

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

@@ -41,7 +41,7 @@ namespace Jint.Native.Function
         {
         {
             var target = thisObj.TryCast<ICallable>(x =>
             var target = thisObj.TryCast<ICallable>(x =>
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             });
             });
 
 
             var thisArg = arguments.At(0);
             var thisArg = arguments.At(0);
@@ -77,7 +77,7 @@ namespace Jint.Native.Function
 
 
             if (ReferenceEquals(func, null))
             if (ReferenceEquals(func, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError, "Function object expected.");
+                ExceptionHelper.ThrowTypeError(_engine, "Function object expected.");
             }
             }
 
 
             return "function() {{ ... }}";
             return "function() {{ ... }}";
@@ -91,7 +91,7 @@ namespace Jint.Native.Function
 
 
             if (func == null)
             if (func == null)
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             if (argArray.IsNull() || argArray.IsUndefined())
             if (argArray.IsNull() || argArray.IsUndefined())
@@ -102,16 +102,13 @@ namespace Jint.Native.Function
             var argArrayObj = argArray.TryCast<ObjectInstance>();
             var argArrayObj = argArray.TryCast<ObjectInstance>();
             if (ReferenceEquals(argArrayObj, null))
             if (ReferenceEquals(argArrayObj, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var len = ((JsNumber) argArrayObj.Get("length"))._value;
             var len = ((JsNumber) argArrayObj.Get("length"))._value;
             uint n = TypeConverter.ToUint32(len);
             uint n = TypeConverter.ToUint32(len);
 
 
-            var argList = n < 10 
-                ? Engine.JsValueArrayPool.RentArray((int) n)
-                : new JsValue[n];
-            
+            var argList = _engine._jsValueArrayPool.RentArray((int) n);
             for (int index = 0; index < n; index++)
             for (int index = 0; index < n; index++)
             {
             {
                 string indexName = TypeConverter.ToString(index);
                 string indexName = TypeConverter.ToString(index);
@@ -120,7 +117,7 @@ namespace Jint.Native.Function
             }
             }
 
 
             var result = func.Call(thisArg, argList);
             var result = func.Call(thisArg, argList);
-            Engine.JsValueArrayPool.ReturnArray(argList);
+            _engine._jsValueArrayPool.ReturnArray(argList);
             
             
             return result;
             return result;
         }
         }
@@ -130,7 +127,7 @@ namespace Jint.Native.Function
             var func = thisObject.TryCast<ICallable>();
             var func = thisObject.TryCast<ICallable>();
             if (func == null)
             if (func == null)
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(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));

+ 6 - 6
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -36,7 +36,7 @@ namespace Jint.Native.Function
             Extensible = true;
             Extensible = true;
             Prototype = _engine.Function.PrototypeObject;
             Prototype = _engine.Function.PrototypeObject;
 
 
-            DefineOwnProperty("length", new PropertyDescriptor(JsNumber.Create(FormalParameters.Length), PropertyFlag.AllForbidden), false);
+            _length = new PropertyDescriptor(JsNumber.Create(_formalParameters.Length), PropertyFlag.AllForbidden);
 
 
             var proto = new ObjectInstanceWithConstructor(engine, this)
             var proto = new ObjectInstanceWithConstructor(engine, this)
             {
             {
@@ -44,7 +44,7 @@ namespace Jint.Native.Function
                 Prototype = _engine.Object.PrototypeObject
                 Prototype = _engine.Object.PrototypeObject
             };
             };
 
 
-            SetOwnProperty("prototype", new PropertyDescriptor(proto, PropertyFlag.OnlyWritable));
+            _prototype = new PropertyDescriptor(proto, PropertyFlag.OnlyWritable);
 
 
             if (_functionDeclaration.Id != null)
             if (_functionDeclaration.Id != null)
             {
             {
@@ -150,11 +150,11 @@ namespace Jint.Native.Function
                 {
                 {
                     thisBinding = thisArg;
                     thisBinding = thisArg;
                 }
                 }
-                else if (thisArg.IsUndefined() || thisArg.IsNull())
+                else if (thisArg._type == Types.Undefined || thisArg._type == Types.Null)
                 {
                 {
                     thisBinding = _engine.Global;
                     thisBinding = _engine.Global;
                 }
                 }
-                else if (!thisArg.IsObject())
+                else if (thisArg._type != Types.Object)
                 {
                 {
                     thisBinding = TypeConverter.ToObject(_engine, thisArg);
                     thisBinding = TypeConverter.ToObject(_engine, thisArg);
                 }
                 }
@@ -163,7 +163,7 @@ namespace Jint.Native.Function
                     thisBinding = thisArg;
                     thisBinding = thisArg;
                 }
                 }
 
 
-                var localEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, Scope);
+                var localEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _scope);
 
 
                 _engine.EnterExecutionContext(localEnv, localEnv, thisBinding);
                 _engine.EnterExecutionContext(localEnv, localEnv, thisBinding);
 
 
@@ -182,7 +182,7 @@ namespace Jint.Native.Function
                     
                     
                     // we can safely release arguments if they don't escape the scope
                     // we can safely release arguments if they don't escape the scope
                     if (argumentInstanceRented
                     if (argumentInstanceRented
-                        && _engine.ExecutionContext.LexicalEnvironment?.Record is DeclarativeEnvironmentRecord der
+                        && _engine.ExecutionContext.LexicalEnvironment?._record is DeclarativeEnvironmentRecord der
                         && !(result.Value is ArgumentsInstance))
                         && !(result.Value is ArgumentsInstance))
                     {
                     {
                         der.ReleaseArguments();
                         der.ReleaseArguments();

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

@@ -13,7 +13,8 @@ namespace Jint.Native.Function
 
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
         {
-            throw new JavaScriptException(_engine.TypeError);
+            ExceptionHelper.ThrowTypeError(_engine);
+            return null;
         }
         }
     }
     }
 }
 }

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

@@ -414,7 +414,7 @@ namespace Jint.Native.Global
                 {
                 {
                     if (c >= 0xDC00 && c <= 0xDBFF)
                     if (c >= 0xDC00 && c <= 0xDBFF)
                     {
                     {
-                        throw new JavaScriptException(Engine.UriError);
+                        ExceptionHelper.ThrowUriError(_engine);
                     }
                     }
 
 
                     int v;
                     int v;
@@ -427,19 +427,19 @@ namespace Jint.Native.Global
                         k++;
                         k++;
                         if (k == strLen)
                         if (k == strLen)
                         {
                         {
-                            throw new JavaScriptException(Engine.UriError);
+                            ExceptionHelper.ThrowUriError(_engine);
                         }
                         }
 
 
                         var kChar = (int)uriString[k];
                         var kChar = (int)uriString[k];
                         if (kChar < 0xDC00 || kChar > 0xDFFF)
                         if (kChar < 0xDC00 || kChar > 0xDFFF)
                         {
                         {
-                            throw new JavaScriptException(Engine.UriError);
+                            ExceptionHelper.ThrowUriError(_engine);
                         }
                         }
 
 
                         v = (c - 0xD800) * 0x400 + (kChar - 0xDC00) + 0x10000;
                         v = (c - 0xD800) * 0x400 + (kChar - 0xDC00) + 0x10000;
                     }
                     }
 
 
-                    byte[] octets;
+                    byte[] octets = System.Array.Empty<byte>();
 
 
                     if (v >= 0 && v <= 0x007F)
                     if (v >= 0 && v <= 0x007F)
                     {
                     {
@@ -467,7 +467,7 @@ namespace Jint.Native.Global
                     }
                     }
                     else if (v <= 0xDFFF)
                     else if (v <= 0xDFFF)
                     {
                     {
-                        throw new JavaScriptException(Engine.UriError);
+                        ExceptionHelper.ThrowUriError(_engine);
                     }
                     }
                     else if (v <= 0xFFFF)
                     else if (v <= 0xFFFF)
                     {
                     {
@@ -536,12 +536,12 @@ namespace Jint.Native.Global
                     var start = k;
                     var start = k;
                     if (k + 2 >= strLen)
                     if (k + 2 >= strLen)
                     {
                     {
-                        throw new JavaScriptException(Engine.UriError);
+                        ExceptionHelper.ThrowUriError(_engine);
                     }
                     }
 
 
                     if (!IsValidHexaChar(uriString[k + 1]) || !IsValidHexaChar(uriString[k + 2]))
                     if (!IsValidHexaChar(uriString[k + 1]) || !IsValidHexaChar(uriString[k + 2]))
                     {
                     {
-                        throw new JavaScriptException(Engine.UriError);
+                        ExceptionHelper.ThrowUriError(_engine);
                     }
                     }
 
 
                     var B = Convert.ToByte(uriString[k + 1].ToString() + uriString[k + 2], 16);
                     var B = Convert.ToByte(uriString[k + 1].ToString() + uriString[k + 2], 16);
@@ -566,7 +566,7 @@ namespace Jint.Native.Global
 
 
                         if (n == 1 || n > 4)
                         if (n == 1 || n > 4)
                         {
                         {
-                            throw new JavaScriptException(Engine.UriError);
+                            ExceptionHelper.ThrowUriError(_engine);
                         }
                         }
 
 
                         var Octets = new byte[n];
                         var Octets = new byte[n];
@@ -574,7 +574,7 @@ namespace Jint.Native.Global
 
 
                         if (k + (3 * (n - 1)) >= strLen)
                         if (k + (3 * (n - 1)) >= strLen)
                         {
                         {
-                            throw new JavaScriptException(Engine.UriError);
+                            ExceptionHelper.ThrowUriError(_engine);
                         }
                         }
 
 
                         for (var j = 1; j < n; j++)
                         for (var j = 1; j < n; j++)
@@ -582,12 +582,12 @@ namespace Jint.Native.Global
                             k++;
                             k++;
                             if (uriString[k] != '%')
                             if (uriString[k] != '%')
                             {
                             {
-                                throw new JavaScriptException(Engine.UriError);
+                                ExceptionHelper.ThrowUriError(_engine);
                             }
                             }
 
 
                             if (!IsValidHexaChar(uriString[k + 1]) || !IsValidHexaChar(uriString[k + 2]))
                             if (!IsValidHexaChar(uriString[k + 1]) || !IsValidHexaChar(uriString[k + 2]))
                             {
                             {
-                                throw new JavaScriptException(Engine.UriError);
+                                ExceptionHelper.ThrowUriError(_engine);
                             }
                             }
 
 
                             B = Convert.ToByte(uriString[k + 1].ToString() + uriString[k + 2], 16);
                             B = Convert.ToByte(uriString[k + 1].ToString() + uriString[k + 2], 16);
@@ -595,7 +595,7 @@ namespace Jint.Native.Global
                             // B & 11000000 != 10000000
                             // B & 11000000 != 10000000
                             if ((B & 0xC0) != 0x80)
                             if ((B & 0xC0) != 0x80)
                             {
                             {
-                                throw new JavaScriptException(Engine.UriError);
+                                ExceptionHelper.ThrowUriError(_engine);
                             }
                             }
 
 
                             k += 2;
                             k += 2;
@@ -633,11 +633,11 @@ namespace Jint.Native.Global
                 }
                 }
                 else if (c < 256)
                 else if (c < 256)
                 {
                 {
-                    _stringBuilder.Append(string.Format("%{0}", ((int)c).ToString("X2")));
+                    _stringBuilder.Append($"%{((int) c):X2}");
                 }
                 }
                 else
                 else
                 {
                 {
-                    _stringBuilder.Append(string.Format("%u{0}", ((int)c).ToString("X4")));
+                    _stringBuilder.Append($"%u{((int) c):X4}");
                 }
                 }
             }
             }
 
 

+ 2 - 2
Jint/Native/JsBoolean.cs

@@ -5,8 +5,8 @@ namespace Jint.Native
 {
 {
     public sealed class JsBoolean : JsValue, IEquatable<JsBoolean>
     public sealed class JsBoolean : JsValue, IEquatable<JsBoolean>
     {
     {
-        public static readonly JsValue False = new JsBoolean(false);
-        public static readonly JsValue True = new JsBoolean(true);
+        public static readonly JsBoolean False = new JsBoolean(false);
+        public static readonly JsBoolean True = new JsBoolean(true);
 
 
         internal static readonly object BoxedTrue = true;
         internal static readonly object BoxedTrue = true;
         internal static readonly object BoxedFalse = false;
         internal static readonly object BoxedFalse = false;

+ 5 - 4
Jint/Native/JsNumber.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Globalization;
 using Jint.Runtime;
 using Jint.Runtime;
 
 
 namespace Jint.Native
 namespace Jint.Native
@@ -88,7 +89,7 @@ namespace Jint.Native
 
 
         internal static JsNumber Create(int value)
         internal static JsNumber Create(int value)
         {
         {
-            if (value >= 0 && value < _intToJsValue.Length)
+            if ((uint) value < (uint) _intToJsValue.Length)
             {
             {
                 return _intToJsValue[value];
                 return _intToJsValue[value];
             }
             }
@@ -103,7 +104,7 @@ namespace Jint.Native
 
 
         internal static JsNumber Create(uint value)
         internal static JsNumber Create(uint value)
         {
         {
-            if (value >= 0 && value < _intToJsValue.Length)
+            if (value < (uint) _intToJsValue.Length)
             {
             {
                 return _intToJsValue[value];
                 return _intToJsValue[value];
             }
             }
@@ -113,7 +114,7 @@ namespace Jint.Native
 
 
         internal static JsNumber Create(ulong value)
         internal static JsNumber Create(ulong value)
         {
         {
-            if (value >= 0 && value < (ulong) _intToJsValue.Length)
+            if (value < (ulong) _intToJsValue.Length)
             {
             {
                 return _intToJsValue[value];
                 return _intToJsValue[value];
             }
             }
@@ -123,7 +124,7 @@ namespace Jint.Native
 
 
         public override string ToString()
         public override string ToString()
         {
         {
-            return _value.ToString();
+            return _value.ToString(CultureInfo.InvariantCulture);
         }
         }
 
 
         public override bool Equals(JsValue obj)
         public override bool Equals(JsValue obj)

+ 25 - 15
Jint/Native/JsString.cs

@@ -10,7 +10,7 @@ namespace Jint.Native
         private static readonly JsString[] _charToJsValue;
         private static readonly JsString[] _charToJsValue;
         private static readonly JsString[] _charToStringJsValue;
         private static readonly JsString[] _charToStringJsValue;
 
 
-        private static readonly JsString Empty = new JsString("");
+        public static readonly JsString Empty = new JsString("");
         private static readonly JsString NullString = new JsString("null");
         private static readonly JsString NullString = new JsString("null");
 
 
         internal string _value;
         internal string _value;
@@ -57,24 +57,33 @@ namespace Jint.Native
             return string.IsNullOrEmpty(_value);
             return string.IsNullOrEmpty(_value);
         }
         }
 
 
+        public virtual int Length => _value.Length;
+
         internal static JsString Create(string value)
         internal static JsString Create(string value)
         {
         {
-            switch (value.Length)
+            if (value.Length == 0)
+            {
+                return Empty;
+            }
+            if (value.Length == 1)
+            {
+                var i = (uint) value[0];
+                if (i < (uint) _charToStringJsValue.Length)
+                {
+                    return _charToStringJsValue[i];
+                }
+            }
+            else if (value.Length == 4 && value == Native.Null.Text)
             {
             {
-                case 0:
-                    return Empty;
-                case 1 when value[0] >= 0 && value[0] <= AsciiMax:
-                    return _charToStringJsValue[value[0]];
-                case 4 when value == Native.Null.Text:
-                    return NullString;
-                default:
-                    return new JsString(value);
+                return NullString;
             }
             }
+
+            return new JsString(value);
         }
         }
 
 
         internal static JsString Create(char value)
         internal static JsString Create(char value)
         {
         {
-            if (value >= 0 && value <= AsciiMax)
+            if (value < (uint) _charToJsValue.Length)
             {
             {
                 return _charToJsValue[value];
                 return _charToJsValue[value];
             }
             }
@@ -171,6 +180,8 @@ namespace Jint.Native
                     || _stringBuilder != null && _stringBuilder.Length == 0;
                     || _stringBuilder != null && _stringBuilder.Length == 0;
             }
             }
 
 
+            public override int Length => _stringBuilder?.Length ?? _value?.Length ?? 0;
+
             public override object ToObject()
             public override object ToObject()
             {
             {
                 return _stringBuilder.ToString();
                 return _stringBuilder.ToString();
@@ -183,15 +194,14 @@ namespace Jint.Native
                     return _stringBuilder.Equals(cs._stringBuilder);
                     return _stringBuilder.Equals(cs._stringBuilder);
                 }
                 }
 
 
-                if (other.Type == Types.String)
+                if (other is JsString jsString)
                 {
                 {
-                    var otherString = other.AsStringWithoutTypeCheck();
-                    if (otherString.Length != _stringBuilder.Length)
+                    if (jsString._value.Length != Length)
                     {
                     {
                         return false;
                         return false;
                     }
                     }
 
 
-                    return ToString().Equals(otherString);
+                    return ToString() == jsString._value;
                 }
                 }
 
 
                 return base.Equals(other);
                 return base.Equals(other);

+ 9 - 20
Jint/Native/JsValue.cs

@@ -116,7 +116,7 @@ namespace Jint.Native
         {
         {
             if (!IsObject())
             if (!IsObject())
             {
             {
-                ThrowArgumentException("The value is not an object");
+                ExceptionHelper.ThrowArgumentException("The value is not an object");
             }
             }
             return this as ObjectInstance;
             return this as ObjectInstance;
         }
         }
@@ -127,7 +127,7 @@ namespace Jint.Native
         {
         {
             if (!IsObject())
             if (!IsObject())
             {
             {
-                ThrowArgumentException("The value is not an object");
+                ExceptionHelper.ThrowArgumentException("The value is not an object");
             }
             }
             return this as TInstance;
             return this as TInstance;
         }
         }
@@ -138,7 +138,7 @@ namespace Jint.Native
         {
         {
             if (!IsArray())
             if (!IsArray())
             {
             {
-                ThrowArgumentException("The value is not an array");
+                ExceptionHelper.ThrowArgumentException("The value is not an array");
             }
             }
             return this as ArrayInstance;
             return this as ArrayInstance;
         }
         }
@@ -149,7 +149,7 @@ namespace Jint.Native
         {
         {
             if (!IsDate())
             if (!IsDate())
             {
             {
-                ThrowArgumentException("The value is not a date");
+                ExceptionHelper.ThrowArgumentException("The value is not a date");
             }
             }
             return this as DateInstance;
             return this as DateInstance;
         }
         }
@@ -159,7 +159,7 @@ namespace Jint.Native
         {
         {
             if (!IsRegExp())
             if (!IsRegExp())
             {
             {
-                ThrowArgumentException("The value is not a regex");
+                ExceptionHelper.ThrowArgumentException("The value is not a regex");
             }
             }
 
 
             return this as RegExpInstance;
             return this as RegExpInstance;
@@ -170,7 +170,7 @@ namespace Jint.Native
         {
         {
             if (_type != Types.Completion)
             if (_type != Types.Completion)
             {
             {
-                ThrowArgumentException("The value is not a completion record");
+                ExceptionHelper.ThrowArgumentException("The value is not a completion record");
             }
             }
 
 
             // TODO not implemented
             // TODO not implemented
@@ -181,7 +181,7 @@ namespace Jint.Native
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public T TryCast<T>(Action<JsValue> fail = null) where T : class
         public T TryCast<T>(Action<JsValue> fail = null) where T : class
         {
         {
-            if (IsObject())
+            if (_type == Types.Object)
             {
             {
                 if (this is T o)
                 if (this is T o)
                 {
                 {
@@ -280,7 +280,7 @@ namespace Jint.Native
                 return new DelegateWrapper(engine, d);
                 return new DelegateWrapper(engine, d);
             }
             }
 
 
-            if (value.GetType().IsEnum())
+            if (value.GetType().IsEnum)
             {
             {
                 return JsNumber.Create((int) value);
                 return JsNumber.Create((int) value);
             }
             }
@@ -333,13 +333,7 @@ namespace Jint.Native
         /// <returns>The value returned by the function call.</returns>
         /// <returns>The value returned by the function call.</returns>
         public JsValue Invoke(JsValue thisObj, JsValue[] arguments)
         public JsValue Invoke(JsValue thisObj, JsValue[] arguments)
         {
         {
-            var callable = TryCast<ICallable>();
-
-            if (callable == null)
-            {
-                throw new ArgumentException("Can only invoke functions");
-            }
-
+            var callable = this as ICallable ?? ExceptionHelper.ThrowArgumentException<ICallable>("Can only invoke functions");
             return callable.Call(thisObj, arguments);
             return callable.Call(thisObj, arguments);
         }
         }
 
 
@@ -361,11 +355,6 @@ namespace Jint.Native
             return false;
             return false;
         }
         }
 
 
-        private static void ThrowArgumentException(string message)
-        {
-            throw new ArgumentException(message);
-        }
-
         public override string ToString()
         public override string ToString()
         {
         {
             return "None";
             return "None";

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

@@ -98,7 +98,7 @@ namespace Jint.Native.Json
                 }
                 }
                 else
                 else
                 {
                 {
-                    throw new JavaScriptException(_engine.SyntaxError, string.Format("Expected hexadecimal digit:{0}", _source));
+                    ExceptionHelper.ThrowSyntaxError(_engine, $"Expected hexadecimal digit:{_source}");
                 }
                 }
             }
             }
             return (char)code;
             return (char)code;
@@ -152,7 +152,9 @@ namespace Jint.Native.Json
                             Range = new[] {start, _index}
                             Range = new[] {start, _index}
                         };
                         };
             }
             }
-            throw new JavaScriptException(_engine.SyntaxError, string.Format(Messages.UnexpectedToken, code));
+
+            ExceptionHelper.ThrowSyntaxError(_engine, string.Format(Messages.UnexpectedToken, code));
+            return null;
         }
         }
 
 
         private Token ScanNumericLiteral()
         private Token ScanNumericLiteral()
@@ -181,7 +183,7 @@ namespace Jint.Native.Json
                     // decimal number starts with '0' such as '09' is illegal.
                     // decimal number starts with '0' such as '09' is illegal.
                     if (ch > 0 && IsDecimalDigit(ch))
                     if (ch > 0 && IsDecimalDigit(ch))
                     {
                     {
-                        throw new Exception(Messages.UnexpectedToken);
+                        ExceptionHelper.ThrowArgumentException(Messages.UnexpectedToken);
                     }
                     }
                 }
                 }
 
 
@@ -220,7 +222,7 @@ namespace Jint.Native.Json
                 }
                 }
                 else
                 else
                 {
                 {
-                    throw new Exception(Messages.UnexpectedToken);
+                    ExceptionHelper.ThrowArgumentException(Messages.UnexpectedToken);
                 }
                 }
             }
             }
 
 
@@ -255,10 +257,9 @@ namespace Jint.Native.Json
                     Range = new[] { start, _index }
                     Range = new[] { start, _index }
                 };
                 };
             }
             }
-            else
-            {
-                throw new JavaScriptException(_engine.SyntaxError, string.Format(Messages.UnexpectedToken, s));
-            }
+
+            ExceptionHelper.ThrowSyntaxError(_engine, string.Format(Messages.UnexpectedToken, s));
+            return null;
         }
         }
 
 
         private Token ScanNullLiteral()
         private Token ScanNullLiteral()
@@ -282,10 +283,9 @@ namespace Jint.Native.Json
                     Range = new[] { start, _index }
                     Range = new[] { start, _index }
                 };
                 };
             }
             }
-            else
-            {
-                throw new JavaScriptException(_engine.SyntaxError, string.Format(Messages.UnexpectedToken, s));
-            }
+
+            ExceptionHelper.ThrowSyntaxError(_engine, string.Format(Messages.UnexpectedToken, s));
+            return null;
         }
         }
 
 
         private Token ScanStringLiteral()
         private Token ScanStringLiteral()
@@ -309,7 +309,7 @@ namespace Jint.Native.Json
 
 
                 if (ch <= 31)
                 if (ch <= 31)
                 {
                 {
-                    throw new JavaScriptException(_engine.SyntaxError, string.Format("Invalid character '{0}', position:{1}, string:{2}", ch, _index, _source));
+                    ExceptionHelper.ThrowSyntaxError(_engine, $"Invalid character '{ch}', position:{_index}, string:{_source}");
                 }
                 }
 
 
                 if (ch == '\\')
                 if (ch == '\\')
@@ -401,7 +401,7 @@ namespace Jint.Native.Json
 
 
             if (quote != 0)
             if (quote != 0)
             {
             {
-                throw new JavaScriptException(_engine.SyntaxError, string.Format(Messages.UnexpectedToken, _source));
+                ExceptionHelper.ThrowSyntaxError(_engine, string.Format(Messages.UnexpectedToken, _source));
             }
             }
 
 
             return new Token
             return new Token
@@ -738,7 +738,7 @@ namespace Jint.Native.Json
                 var name = Lex().Value.ToString();
                 var name = Lex().Value.ToString();
                 if (PropertyNameContainsInvalidChar0To31(name))
                 if (PropertyNameContainsInvalidChar0To31(name))
                 {
                 {
-                    throw new JavaScriptException(_engine.SyntaxError, string.Format("Invalid character in property name '{0}'", name));
+                    ExceptionHelper.ThrowSyntaxError(_engine, $"Invalid character in property name '{name}'");
                 }
                 }
 
 
                 Expect(":");
                 Expect(":");
@@ -864,7 +864,7 @@ namespace Jint.Native.Json
                 object value = _lookahead.Value;
                 object value = _lookahead.Value;
                 if(_lookahead.Type != Tokens.EOF)
                 if(_lookahead.Type != Tokens.EOF)
                 {
                 {
-                    throw new JavaScriptException(_engine.SyntaxError, string.Format("Unexpected {0} {1}", _lookahead.Type, _lookahead.Value));
+                    ExceptionHelper.ThrowSyntaxError(_engine, $"Unexpected {_lookahead.Type} {_lookahead.Value}");
                 }
                 }
                 return jsv;
                 return jsv;
             }
             }

+ 4 - 5
Jint/Native/Json/JsonSerializer.cs

@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using Jint.Native.Array;
 using Jint.Native.Array;
 using Jint.Native.Global;
 using Jint.Native.Global;
@@ -50,7 +49,7 @@ namespace Jint.Native.Json
 
 
                     foreach (var property in replacerObj.GetOwnProperties().Select(x => x.Value))
                     foreach (var property in replacerObj.GetOwnProperties().Select(x => x.Value))
                     {
                     {
-                        JsValue v = _engine.GetValue(property);
+                        JsValue v = _engine.GetValue(property, false);
                         string item = null;
                         string item = null;
                         if (v.IsString())
                         if (v.IsString())
                         {
                         {
@@ -297,12 +296,12 @@ namespace Jint.Native.Json
         {
         {
             if (value == null)
             if (value == null)
             {
             {
-                throw new ArgumentNullException("value");
+                ExceptionHelper.ThrowArgumentNullException(nameof(value));
             }
             }
 
 
             if (_stack.Contains(value))
             if (_stack.Contains(value))
             {
             {
-                throw new JavaScriptException(_engine.TypeError, "Cyclic reference detected.");
+                ExceptionHelper.ThrowTypeError(_engine, "Cyclic reference detected.");
             }
             }
         }
         }
 
 

+ 5 - 5
Jint/Native/Number/Dtoa/DiyFp.cs

@@ -38,7 +38,7 @@ namespace Jint.Native.Number.Dtoa
 // have the most significant bit of the significand set.
 // have the most significant bit of the significand set.
 // Multiplication and Subtraction do not normalize their results.
 // Multiplication and Subtraction do not normalize their results.
 // DiyFp are not designed to contain special doubles (NaN and Infinity).
 // DiyFp are not designed to contain special doubles (NaN and Infinity).
-    internal struct DiyFp
+    internal readonly struct DiyFp
     {
     {
         internal const int KSignificandSize = 64;
         internal const int KSignificandSize = 64;
         private const ulong KUint64MSB = 0x8000000000000000L;
         private const ulong KUint64MSB = 0x8000000000000000L;
@@ -49,8 +49,8 @@ namespace Jint.Native.Number.Dtoa
             E = e;
             E = e;
         }
         }
 
 
-        public long F { get; }
-        public int E { get; }
+        public readonly long F;
+        public readonly int E;
 
 
         private static bool Uint64Gte(long a, long b)
         private static bool Uint64Gte(long a, long b)
         {
         {
@@ -61,7 +61,7 @@ namespace Jint.Native.Number.Dtoa
         // Returns a - b.
         // Returns a - b.
         // The exponents of both numbers must be the same and this must be bigger
         // The exponents of both numbers must be the same and this must be bigger
         // than other. The result will not be normalized.
         // than other. The result will not be normalized.
-        internal static DiyFp Minus(DiyFp a, DiyFp b)
+        internal static DiyFp Minus(in DiyFp a, in DiyFp b)
         {
         {
             Debug.Assert(a.E == b.E);
             Debug.Assert(a.E == b.E);
             Debug.Assert(Uint64Gte(a.F, b.F));
             Debug.Assert(Uint64Gte(a.F, b.F));
@@ -72,7 +72,7 @@ namespace Jint.Native.Number.Dtoa
         // this = this * other.
         // this = this * other.
 
 
         // returns a * b;
         // returns a * b;
-        internal static DiyFp Times(DiyFp a, DiyFp b)
+        internal static DiyFp Times(in DiyFp a, in DiyFp b)
         {
         {
             DiyFp result = new DiyFp(a.F, a.E);
             DiyFp result = new DiyFp(a.F, a.E);
             // Simply "emulates" a 128 bit multiplication.
             // Simply "emulates" a 128 bit multiplication.

+ 4 - 3
Jint/Native/Number/Dtoa/FastDtoa.cs

@@ -349,9 +349,10 @@ namespace Jint.Native.Number.Dtoa
         // represent 'w' we can stop. Everything inside the interval low - high
         // represent 'w' we can stop. Everything inside the interval low - high
         // represents w. However we have to pay attention to low, high and w's
         // represents w. However we have to pay attention to low, high and w's
         // imprecision.
         // imprecision.
-        private static bool DigitGen(DiyFp low,
-            DiyFp w,
-            DiyFp high,
+        private static bool DigitGen(
+            in DiyFp low,
+            in DiyFp w,
+            in DiyFp high,
             FastDtoaBuilder buffer,
             FastDtoaBuilder buffer,
             int mk)
             int mk)
         {
         {

+ 11 - 4
Jint/Native/Number/NumberConstructor.cs

@@ -63,10 +63,17 @@ namespace Jint.Native.Number
 
 
         public NumberInstance Construct(double value)
         public NumberInstance Construct(double value)
         {
         {
-            var instance = new NumberInstance(Engine);
-            instance.Prototype = PrototypeObject;
-            instance.NumberData = value;
-            instance.Extensible = true;
+            return Construct(JsNumber.Create(value));
+        }
+
+        public NumberInstance Construct(JsNumber value)
+        {
+            var instance = new NumberInstance(Engine)
+            {
+                Prototype = PrototypeObject,
+                NumberData = value,
+                Extensible = true
+            };
 
 
             return instance;
             return instance;
         }
         }

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

@@ -18,7 +18,7 @@ namespace Jint.Native.Number
 
 
         JsValue IPrimitiveInstance.PrimitiveValue => NumberData;
         JsValue IPrimitiveInstance.PrimitiveValue => NumberData;
 
 
-        public JsValue NumberData { get; set; }
+        public JsNumber NumberData { get; set; }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static bool IsNegativeZero(double x)
         public static bool IsNegativeZero(double x)

+ 19 - 17
Jint/Native/Number/NumberPrototype.cs

@@ -21,10 +21,12 @@ namespace Jint.Native.Number
 
 
         public static NumberPrototype CreatePrototypeObject(Engine engine, NumberConstructor numberConstructor)
         public static NumberPrototype CreatePrototypeObject(Engine engine, NumberConstructor numberConstructor)
         {
         {
-            var obj = new NumberPrototype(engine);
-            obj.Prototype = engine.Object.PrototypeObject;
-            obj.NumberData = 0;
-            obj.Extensible = true;
+            var obj = new NumberPrototype(engine)
+            {
+                Prototype = engine.Object.PrototypeObject,
+                NumberData = JsNumber.Create(0),
+                Extensible = true
+            };
 
 
             obj.FastAddProperty("constructor", numberConstructor, true, false, true);
             obj.FastAddProperty("constructor", numberConstructor, true, false, true);
 
 
@@ -45,7 +47,7 @@ namespace Jint.Native.Number
         {
         {
             if (!thisObject.IsNumber() && ReferenceEquals(thisObject.TryCast<NumberInstance>(), null))
             if (!thisObject.IsNumber() && ReferenceEquals(thisObject.TryCast<NumberInstance>(), null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var m = TypeConverter.ToNumber(thisObject);
             var m = TypeConverter.ToNumber(thisObject);
@@ -55,7 +57,7 @@ namespace Jint.Native.Number
                 return "NaN";
                 return "NaN";
             }
             }
 
 
-            if (m.Equals(0))
+            if (m == 0)
             {
             {
                 return "0";
                 return "0";
             }
             }
@@ -83,7 +85,7 @@ namespace Jint.Native.Number
             var number = thisObj.TryCast<NumberInstance>();
             var number = thisObj.TryCast<NumberInstance>();
             if (ReferenceEquals(number, null))
             if (ReferenceEquals(number, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             return number.NumberData;
             return number.NumberData;
@@ -96,7 +98,7 @@ namespace Jint.Native.Number
             var f = (int)TypeConverter.ToInteger(arguments.At(0, 0));
             var f = (int)TypeConverter.ToInteger(arguments.At(0, 0));
             if (f < 0 || f > 20)
             if (f < 0 || f > 20)
             {
             {
-                throw new JavaScriptException(Engine.RangeError, "fractionDigits argument must be between 0 and 20");
+                ExceptionHelper.ThrowRangeError(_engine, "fractionDigits argument must be between 0 and 20");
             }
             }
 
 
             var x = TypeConverter.ToNumber(thisObj);
             var x = TypeConverter.ToNumber(thisObj);
@@ -119,7 +121,7 @@ namespace Jint.Native.Number
             var f = (int)TypeConverter.ToInteger(arguments.At(0, 16));
             var f = (int)TypeConverter.ToInteger(arguments.At(0, 16));
             if (f < 0 || f > 20)
             if (f < 0 || f > 20)
             {
             {
-                throw new JavaScriptException(Engine.RangeError, "fractionDigits argument must be between 0 and 20");
+                ExceptionHelper.ThrowRangeError(_engine, "fractionDigits argument must be between 0 and 20");
             }
             }
 
 
             var x = TypeConverter.ToNumber(thisObj);
             var x = TypeConverter.ToNumber(thisObj);
@@ -129,7 +131,7 @@ namespace Jint.Native.Number
                 return "NaN";
                 return "NaN";
             }
             }
 
 
-            string format = System.String.Concat("#.", new System.String('0', f), "e+0");
+            string format = string.Concat("#.", new string('0', f), "e+0");
             return x.ToString(format, CultureInfo.InvariantCulture);
             return x.ToString(format, CultureInfo.InvariantCulture);
         }
         }
 
 
@@ -151,7 +153,7 @@ namespace Jint.Native.Number
 
 
             if (p < 1 || p > 21)
             if (p < 1 || p > 21)
             {
             {
-                throw new JavaScriptException(Engine.RangeError, "precision must be between 1 and 21");
+                ExceptionHelper.ThrowRangeError(_engine, "precision must be between 1 and 21");
             }
             }
 
 
             // Get the number of decimals
             // Get the number of decimals
@@ -169,7 +171,7 @@ namespace Jint.Native.Number
         {
         {
             if (!thisObject.IsNumber() && (ReferenceEquals(thisObject.TryCast<NumberInstance>(), null)))
             if (!thisObject.IsNumber() && (ReferenceEquals(thisObject.TryCast<NumberInstance>(), null)))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(_engine);
             }
             }
 
 
             var radix = arguments.At(0).IsUndefined() 
             var radix = arguments.At(0).IsUndefined() 
@@ -178,7 +180,7 @@ namespace Jint.Native.Number
 
 
             if (radix < 2 || radix > 36)
             if (radix < 2 || radix > 36)
             {
             {
-                throw new JavaScriptException(Engine.RangeError, "radix must be between 2 and 36");
+                ExceptionHelper.ThrowRangeError(_engine, "radix must be between 2 and 36");
             }
             }
 
 
             var x = TypeConverter.ToNumber(thisObject);
             var x = TypeConverter.ToNumber(thisObject);
@@ -188,7 +190,7 @@ namespace Jint.Native.Number
                 return "NaN";
                 return "NaN";
             }
             }
 
 
-            if (x.Equals(0))
+            if (x == 0)
             {
             {
                 return "0";
                 return "0";
             }
             }
@@ -212,7 +214,7 @@ namespace Jint.Native.Number
             var fraction = x -  integer;
             var fraction = x -  integer;
 
 
             string result = ToBase(integer, radix);
             string result = ToBase(integer, radix);
-            if (!fraction.Equals(0))
+            if (fraction != 0)
             {
             {
                 result += "." + ToFractionBase(fraction, radix);
                 result += "." + ToFractionBase(fraction, radix);
             }
             }
@@ -245,7 +247,7 @@ namespace Jint.Native.Number
             // http://www.mathpath.org/concepts/Num/frac.htm
             // http://www.mathpath.org/concepts/Num/frac.htm
 
 
             const string digits = "0123456789abcdefghijklmnopqrstuvwxyz";
             const string digits = "0123456789abcdefghijklmnopqrstuvwxyz";
-            if (n.Equals(0))
+            if (n == 0)
             {
             {
                 return "0";
                 return "0";
             }
             }
@@ -270,7 +272,7 @@ namespace Jint.Native.Number
                 return "NaN";
                 return "NaN";
             }
             }
 
 
-            if (m.Equals(0))
+            if (m == 0)
             {
             {
                 return "0";
                 return "0";
             }
             }

+ 33 - 22
Jint/Native/Object/ObjectConstructor.cs

@@ -100,6 +100,18 @@ namespace Jint.Native.Object
 
 
             return obj;
             return obj;
         }
         }
+        
+        internal ObjectInstance Construct(int propertyCount)
+        {
+            var obj = new ObjectInstance(_engine)
+            {
+                Extensible = true,
+                Prototype = Engine.Object.PrototypeObject,
+                _properties =  propertyCount > 0 ? new Dictionary<string, PropertyDescriptor>(propertyCount) : null
+            };
+
+            return obj;
+        }
 
 
         public JsValue GetPrototypeOf(JsValue thisObject, JsValue[] arguments)
         public JsValue GetPrototypeOf(JsValue thisObject, JsValue[] arguments)
         {
         {
@@ -107,7 +119,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             return o.Prototype ?? Null;
             return o.Prototype ?? Null;
@@ -119,7 +131,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var p = arguments.At(1);
             var p = arguments.At(1);
@@ -135,7 +147,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             uint n = 0;
             uint n = 0;
@@ -144,8 +156,8 @@ namespace Jint.Native.Object
             var ownProperties = o.GetOwnProperties().ToList();
             var ownProperties = o.GetOwnProperties().ToList();
             if (o is StringInstance s)
             if (o is StringInstance s)
             {
             {
-                var length = s.PrimitiveValue.AsStringWithoutTypeCheck().Length;
-                array = Engine.Array.Construct(ownProperties.Count + length);
+                var length = s.PrimitiveValue.Length;
+                array = Engine.Array.ConstructFast((uint) (ownProperties.Count + length));
                 for (var i = 0; i < length; i++)
                 for (var i = 0; i < length; i++)
                 {
                 {
                     array.SetIndexValue(n, TypeConverter.ToString(i), updateLength: false);
                     array.SetIndexValue(n, TypeConverter.ToString(i), updateLength: false);
@@ -172,7 +184,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null) && !oArg.IsNull())
             if (ReferenceEquals(o, null) && !oArg.IsNull())
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var obj = Engine.Object.Construct(Arguments.Empty);
             var obj = Engine.Object.Construct(Arguments.Empty);
@@ -181,7 +193,11 @@ namespace Jint.Native.Object
             var properties = arguments.At(1);
             var properties = arguments.At(1);
             if (!properties.IsUndefined())
             if (!properties.IsUndefined())
             {
             {
-                DefineProperties(thisObject, new [] {obj, properties});
+                var jsValues = _engine._jsValueArrayPool.RentArray(2);
+                jsValues[0] = obj;
+                jsValues[1] = properties;
+                DefineProperties(thisObject, jsValues);
+                _engine._jsValueArrayPool.ReturnArray(jsValues);
             }
             }
 
 
             return obj;
             return obj;
@@ -193,7 +209,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var p = arguments.At(1);
             var p = arguments.At(1);
@@ -212,7 +228,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var properties = arguments.At(1);
             var properties = arguments.At(1);
@@ -243,7 +259,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var properties = new List<KeyValuePair<string, PropertyDescriptor>>(o.GetOwnProperties());
             var properties = new List<KeyValuePair<string, PropertyDescriptor>>(o.GetOwnProperties());
@@ -270,7 +286,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var properties = new List<KeyValuePair<string, PropertyDescriptor>>(o.GetOwnProperties());
             var properties = new List<KeyValuePair<string, PropertyDescriptor>>(o.GetOwnProperties());
@@ -306,7 +322,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             o.Extensible = false;
             o.Extensible = false;
@@ -320,7 +336,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             foreach (var prop in o.GetOwnProperties())
             foreach (var prop in o.GetOwnProperties())
@@ -345,7 +361,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             foreach (var pair in o.GetOwnProperties())
             foreach (var pair in o.GetOwnProperties())
@@ -378,7 +394,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             return o.Extensible;
             return o.Extensible;
@@ -390,7 +406,7 @@ namespace Jint.Native.Object
             var o = oArg.TryCast<ObjectInstance>();
             var o = oArg.TryCast<ObjectInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var enumerableProperties = o.GetOwnProperties()
             var enumerableProperties = o.GetOwnProperties()
@@ -398,11 +414,7 @@ namespace Jint.Native.Object
                 .ToArray();
                 .ToArray();
             var n = enumerableProperties.Length;
             var n = enumerableProperties.Length;
 
 
-            var args = _engine.JsValueArrayPool.RentArray(1);
-            args[0] = n;
-            var array = Engine.Array.Construct(args, (uint) n);
-            _engine.JsValueArrayPool.ReturnArray(args);
-
+            var array = Engine.Array.ConstructFast((uint) n);
             uint index = 0;
             uint index = 0;
             foreach (var prop in enumerableProperties)
             foreach (var prop in enumerableProperties)
             {
             {
@@ -410,7 +422,6 @@ namespace Jint.Native.Object
                 array.SetIndexValue(index, p, updateLength: false);
                 array.SetIndexValue(index, p, updateLength: false);
                 index++;
                 index++;
             }
             }
-            array.SetLength(index);
             return array;
             return array;
         }
         }
     }
     }

+ 57 - 68
Jint/Native/Object/ObjectInstance.cs

@@ -18,18 +18,17 @@ namespace Jint.Native.Object
 {
 {
     public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     {
     {
-        private MruPropertyCache2<PropertyDescriptor> _intrinsicProperties;
-        private MruPropertyCache2<PropertyDescriptor> _properties;
+        protected Dictionary<string, PropertyDescriptor> _intrinsicProperties;
+        protected internal Dictionary<string, PropertyDescriptor> _properties;
         
         
         private readonly string _class;
         private readonly string _class;
         protected readonly Engine _engine;
         protected readonly Engine _engine;
         
         
         public ObjectInstance(Engine engine) : this(engine, "Object")
         public ObjectInstance(Engine engine) : this(engine, "Object")
         {
         {
-            _engine = engine;
         }
         }
         
         
-        protected ObjectInstance(Engine engine, in string objectClass) : base(Types.Object)
+        protected ObjectInstance(Engine engine, string objectClass) : base(Types.Object)
         {
         {
             _engine = engine;
             _engine = engine;
             _class = objectClass;
             _class = objectClass;
@@ -63,7 +62,7 @@ namespace Jint.Native.Object
         {
         {
             if (_intrinsicProperties == null)
             if (_intrinsicProperties == null)
             {
             {
-                _intrinsicProperties = new MruPropertyCache2<PropertyDescriptor>();
+                _intrinsicProperties = new Dictionary<string, PropertyDescriptor>();
             }
             }
 
 
             _intrinsicProperties[symbol.AsSymbol()] = new PropertyDescriptor(value, writable, enumerable, configurable);
             _intrinsicProperties[symbol.AsSymbol()] = new PropertyDescriptor(value, writable, enumerable, configurable);
@@ -84,7 +83,7 @@ namespace Jint.Native.Object
         /// A String value indicating a specification defined
         /// A String value indicating a specification defined
         /// classification of objects.
         /// classification of objects.
         /// </summary>
         /// </summary>
-        public ref readonly string Class => ref _class;
+        public string Class => _class;
 
 
         public virtual IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
         public virtual IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
         {
         {
@@ -92,7 +91,7 @@ namespace Jint.Native.Object
 
 
             if (_properties != null)
             if (_properties != null)
             {
             {
-                foreach (var pair in _properties.GetEnumerator())
+                foreach (var pair in _properties)
                 {
                 {
                     yield return pair;
                     yield return pair;
                 }
                 }
@@ -103,7 +102,7 @@ namespace Jint.Native.Object
         {
         {
             if (_properties == null)
             if (_properties == null)
             {
             {
-                _properties = new MruPropertyCache2<PropertyDescriptor>();
+                _properties = new Dictionary<string, PropertyDescriptor>();
             }
             }
 
 
             _properties.Add(propertyName, descriptor);
             _properties.Add(propertyName, descriptor);
@@ -124,7 +123,7 @@ namespace Jint.Native.Object
         {
         {
             EnsureInitialized();
             EnsureInitialized();
 
 
-            return _properties?.ContainsKey(propertyName) ?? false;
+            return _properties?.ContainsKey(propertyName) == true;
         }
         }
 
 
         public virtual void RemoveOwnProperty(string propertyName)
         public virtual void RemoveOwnProperty(string propertyName)
@@ -148,16 +147,27 @@ namespace Jint.Native.Object
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal JsValue UnwrapJsValue(PropertyDescriptor desc)
         internal JsValue UnwrapJsValue(PropertyDescriptor desc)
+        {
+            return UnwrapJsValue(desc, this);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static JsValue UnwrapJsValue(PropertyDescriptor desc, JsValue thisObject)
         {
         {
             if (desc == PropertyDescriptor.Undefined)
             if (desc == PropertyDescriptor.Undefined)
             {
             {
                 return Undefined;
                 return Undefined;
             }
             }
 
 
-            if (desc.IsDataDescriptor())
+            var value = (desc._flags & PropertyFlag.CustomJsValue) != 0
+                ? desc.CustomValue
+                : desc._value;
+
+            // IsDataDescriptor inlined
+            if ((desc._flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != 0
+                || !ReferenceEquals(value, null))
             {
             {
-                var val = desc.Value;
-                return val ?? Undefined;
+                return value ?? Undefined;
             }
             }
 
 
             var getter = desc.Get ?? Undefined;
             var getter = desc.Get ?? Undefined;
@@ -168,7 +178,7 @@ namespace Jint.Native.Object
 
 
             // if getter is not undefined it must be ICallable
             // if getter is not undefined it must be ICallable
             var callable = getter.TryCast<ICallable>();
             var callable = getter.TryCast<ICallable>();
-            return callable.Call(this, Arguments.Empty);
+            return callable.Call(thisObject, Arguments.Empty);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -183,12 +193,9 @@ namespace Jint.Native.Object
         {
         {
             EnsureInitialized();
             EnsureInitialized();
 
 
-            if (_properties != null && _properties.TryGetValue(propertyName, out var x))
-            {
-                return x;
-            }
-
-            return PropertyDescriptor.Undefined;
+            PropertyDescriptor descriptor = null;
+            _properties?.TryGetValue(propertyName, out descriptor);
+            return descriptor ?? PropertyDescriptor.Undefined;
         }
         }
 
 
         protected internal virtual void SetOwnProperty(string propertyName, PropertyDescriptor desc)
         protected internal virtual void SetOwnProperty(string propertyName, PropertyDescriptor desc)
@@ -197,7 +204,7 @@ namespace Jint.Native.Object
 
 
             if (_properties == null)
             if (_properties == null)
             {
             {
-                _properties = new MruPropertyCache2<PropertyDescriptor>();
+                _properties = new Dictionary<string, PropertyDescriptor>();
             }
             }
 
 
             _properties[propertyName] = desc;
             _properties[propertyName] = desc;
@@ -218,12 +225,7 @@ namespace Jint.Native.Object
                 return prop;
                 return prop;
             }
             }
 
 
-            if (ReferenceEquals(Prototype, null))
-            {
-                return PropertyDescriptor.Undefined;
-            }
-
-            return Prototype.GetProperty(propertyName);
+            return Prototype?.GetProperty(propertyName) ?? PropertyDescriptor.Undefined;
         }
         }
 
 
         public bool TryGetValue(string propertyName, out JsValue value)
         public bool TryGetValue(string propertyName, out JsValue value)
@@ -279,7 +281,7 @@ namespace Jint.Native.Object
             {
             {
                 if (throwOnError)
                 if (throwOnError)
                 {
                 {
-                    throw new JavaScriptException(Engine.TypeError);
+                    ExceptionHelper.ThrowTypeError(Engine);
                 }
                 }
 
 
                 return;
                 return;
@@ -407,15 +409,13 @@ namespace Jint.Native.Object
                 RemoveOwnProperty(propertyName);
                 RemoveOwnProperty(propertyName);
                 return true;
                 return true;
             }
             }
-            else
-            {
-                if (throwOnError)
-                {
-                    throw new JavaScriptException(Engine.TypeError);
-                }
 
 
-                return false;
+            if (throwOnError)
+            {
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
+
+            return false;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -430,8 +430,7 @@ namespace Jint.Native.Object
 
 
             if (hint == Types.String || (hint == Types.None && Class == "Date"))
             if (hint == Types.String || (hint == Types.None && Class == "Date"))
             {
             {
-                var toString = Get("toString").TryCast<ICallable>();
-                if (toString != null)
+                if (Get("toString") is ICallable toString)
                 {
                 {
                     var str = toString.Call(this, Arguments.Empty);
                     var str = toString.Call(this, Arguments.Empty);
                     if (str.IsPrimitive())
                     if (str.IsPrimitive())
@@ -440,8 +439,7 @@ namespace Jint.Native.Object
                     }
                     }
                 }
                 }
 
 
-                var valueOf = Get("valueOf").TryCast<ICallable>();
-                if (valueOf != null)
+                if (Get("valueOf") is ICallable valueOf)
                 {
                 {
                     var val = valueOf.Call(this, Arguments.Empty);
                     var val = valueOf.Call(this, Arguments.Empty);
                     if (val.IsPrimitive())
                     if (val.IsPrimitive())
@@ -450,13 +448,12 @@ namespace Jint.Native.Object
                     }
                     }
                 }
                 }
 
 
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             if (hint == Types.Number || hint == Types.None)
             if (hint == Types.Number || hint == Types.None)
             {
             {
-                var valueOf = Get("valueOf").TryCast<ICallable>();
-                if (valueOf != null)
+                if (Get("valueOf") is ICallable valueOf)
                 {
                 {
                     var val = valueOf.Call(this, Arguments.Empty);
                     var val = valueOf.Call(this, Arguments.Empty);
                     if (val.IsPrimitive())
                     if (val.IsPrimitive())
@@ -465,8 +462,7 @@ namespace Jint.Native.Object
                     }
                     }
                 }
                 }
 
 
-                var toString = Get("toString").TryCast<ICallable>();
-                if (toString != null)
+                if (Get("toString") is ICallable toString)
                 {
                 {
                     var str = toString.Call(this, Arguments.Empty);
                     var str = toString.Call(this, Arguments.Empty);
                     if (str.IsPrimitive())
                     if (str.IsPrimitive())
@@ -475,7 +471,7 @@ namespace Jint.Native.Object
                     }
                     }
                 }
                 }
 
 
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             return ToString();
             return ToString();
@@ -506,7 +502,7 @@ namespace Jint.Native.Object
                 {
                 {
                     if (throwOnError)
                     if (throwOnError)
                     {
                     {
-                        throw new JavaScriptException(Engine.TypeError);
+                        ExceptionHelper.ThrowTypeError(Engine);
                     }
                     }
 
 
                     return false;
                     return false;
@@ -516,11 +512,11 @@ namespace Jint.Native.Object
                     if (desc.IsGenericDescriptor() || desc.IsDataDescriptor())
                     if (desc.IsGenericDescriptor() || desc.IsDataDescriptor())
                     {
                     {
                         PropertyDescriptor propertyDescriptor;
                         PropertyDescriptor propertyDescriptor;
-                        if (desc.Configurable && desc.Enumerable && desc.Writable)
+                        if ((desc._flags & PropertyFlag.ConfigurableEnumerableWritable) == PropertyFlag.ConfigurableEnumerableWritable)
                         {
                         {
                             propertyDescriptor = new PropertyDescriptor(descValue ?? Undefined, PropertyFlag.ConfigurableEnumerableWritable);
                             propertyDescriptor = new PropertyDescriptor(descValue ?? Undefined, PropertyFlag.ConfigurableEnumerableWritable);
                         }
                         }
-                        else if (!desc.Configurable && !desc.Enumerable && !desc.Writable)
+                        else if ((desc._flags & PropertyFlag.ConfigurableEnumerableWritable) == 0)
                         {
                         {
                             propertyDescriptor = new PropertyDescriptor(descValue ?? Undefined, PropertyFlag.AllForbidden);
                             propertyDescriptor = new PropertyDescriptor(descValue ?? Undefined, PropertyFlag.AllForbidden);
                         }
                         }
@@ -548,9 +544,7 @@ namespace Jint.Native.Object
             var currentSet = current.Set;
             var currentSet = current.Set;
             var currentValue = current.Value;
             var currentValue = current.Value;
             
             
-            if (!current.ConfigurableSet &&
-                !current.EnumerableSet &&
-                !current.WritableSet &&
+            if ((current._flags & PropertyFlag.ConfigurableSet | PropertyFlag.EnumerableSet | PropertyFlag.WritableSet) == 0 &&
                 ReferenceEquals(currentGet, null) &&
                 ReferenceEquals(currentGet, null) &&
                 ReferenceEquals(currentSet, null) &&
                 ReferenceEquals(currentSet, null) &&
                 ReferenceEquals(currentValue, null))
                 ReferenceEquals(currentValue, null))
@@ -579,7 +573,7 @@ namespace Jint.Native.Object
                 {
                 {
                     if (throwOnError)
                     if (throwOnError)
                     {
                     {
-                        throw new JavaScriptException(Engine.TypeError);
+                        ExceptionHelper.ThrowTypeError(Engine);
                     }
                     }
 
 
                     return false;
                     return false;
@@ -589,7 +583,7 @@ namespace Jint.Native.Object
                 {
                 {
                     if (throwOnError)
                     if (throwOnError)
                     {
                     {
-                        throw new JavaScriptException(Engine.TypeError);
+                        ExceptionHelper.ThrowTypeError(Engine);
                     }
                     }
 
 
                     return false;
                     return false;
@@ -604,7 +598,7 @@ namespace Jint.Native.Object
                     {
                     {
                         if (throwOnError)
                         if (throwOnError)
                         {
                         {
-                            throw new JavaScriptException(Engine.TypeError);
+                            ExceptionHelper.ThrowTypeError(Engine);
                         }
                         }
 
 
                         return false;
                         return false;
@@ -636,7 +630,7 @@ namespace Jint.Native.Object
                         {
                         {
                             if (throwOnError)
                             if (throwOnError)
                             {
                             {
-                                throw new JavaScriptException(Engine.TypeError);
+                                ExceptionHelper.ThrowTypeError(Engine);
                             }
                             }
 
 
                             return false;
                             return false;
@@ -648,7 +642,7 @@ namespace Jint.Native.Object
                             {
                             {
                                 if (throwOnError)
                                 if (throwOnError)
                                 {
                                 {
-                                    throw new JavaScriptException(Engine.TypeError);
+                                    ExceptionHelper.ThrowTypeError(Engine);
                                 }
                                 }
 
 
                                 return false;
                                 return false;
@@ -666,7 +660,7 @@ namespace Jint.Native.Object
                         {
                         {
                             if (throwOnError)
                             if (throwOnError)
                             {
                             {
-                                throw new JavaScriptException(Engine.TypeError);
+                                ExceptionHelper.ThrowTypeError(Engine);
                             }
                             }
 
 
                             return false;
                             return false;
@@ -786,7 +780,7 @@ namespace Jint.Native.Object
                 case "String":
                 case "String":
                     if (this is StringInstance stringInstance)
                     if (this is StringInstance stringInstance)
                     {
                     {
-                        return stringInstance.PrimitiveValue.AsStringWithoutTypeCheck();
+                        return stringInstance.PrimitiveValue.ToString();
                     }
                     }
 
 
                     break;
                     break;
@@ -820,7 +814,7 @@ namespace Jint.Native.Object
                 case "Number":
                 case "Number":
                     if (this is NumberInstance numberInstance)
                     if (this is NumberInstance numberInstance)
                     {
                     {
-                        return ((JsNumber) numberInstance.NumberData)._value;
+                        return numberInstance.NumberData._value;
                     }
                     }
 
 
                     break;
                     break;
@@ -905,14 +899,14 @@ namespace Jint.Native.Object
             var thisArg = arguments.At(1);
             var thisArg = arguments.At(1);
             var callable = GetCallable(callbackfn);
             var callable = GetCallable(callbackfn);
 
 
-            var args = Engine.JsValueArrayPool.RentArray(3);
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            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))
                 {
                 {
                     args[0] = kvalue;
                     args[0] = kvalue;
                     args[1] = k;
                     args[1] = k;
-                    args[2] = this;
                     var testResult = callable.Call(thisArg, args);
                     var testResult = callable.Call(thisArg, args);
                     if (TypeConverter.ToBoolean(testResult))
                     if (TypeConverter.ToBoolean(testResult))
                     {
                     {
@@ -923,7 +917,7 @@ namespace Jint.Native.Object
                 }
                 }
             }
             }
 
 
-            Engine.JsValueArrayPool.ReturnArray(args);
+            _engine._jsValueArrayPool.ReturnArray(args);
 
 
             index = 0;
             index = 0;
             value = Undefined;
             value = Undefined;
@@ -937,7 +931,8 @@ namespace Jint.Native.Object
                 return callable;
                 return callable;
             }
             }
 
 
-            throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
+            ExceptionHelper.ThrowTypeError(_engine, "Argument must be callable");
+            return null;
         }
         }
 
 
         public override bool Equals(JsValue obj)
         public override bool Equals(JsValue obj)
@@ -969,11 +964,5 @@ namespace Jint.Native.Object
 
 
             return false;
             return false;
         }
         }
-
-        internal void Clear()
-        {
-            _intrinsicProperties?.Clear();
-            _properties?.Clear();
-        }
     }
     }
 }
 }

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

@@ -80,7 +80,7 @@ namespace Jint.Native.Object
             var o = TypeConverter.ToObject(Engine, thisObject);
             var o = TypeConverter.ToObject(Engine, thisObject);
             var toString = o.Get("toString").TryCast<ICallable>(x =>
             var toString = o.Get("toString").TryCast<ICallable>(x =>
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             });
             });
 
 
             return toString.Call(o, Arguments.Empty);
             return toString.Call(o, Arguments.Empty);

+ 3 - 4
Jint/Native/RegExp/RegExpConstructor.cs

@@ -73,7 +73,7 @@ namespace Jint.Native.RegExp
 
 
             if (!flags.IsUndefined() && !ReferenceEquals(r, null))
             if (!flags.IsUndefined() && !ReferenceEquals(r, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             if (pattern.IsUndefined())
             if (pattern.IsUndefined())
@@ -98,11 +98,10 @@ namespace Jint.Native.RegExp
             }
             }
             catch (Exception e)
             catch (Exception e)
             {
             {
-                throw new JavaScriptException(Engine.SyntaxError, e.Message);
+                ExceptionHelper.ThrowSyntaxError(_engine, e.Message);
             }
             }
 
 
-            string s;
-            s = p;
+            var s = p;
 
 
             if (string.IsNullOrEmpty(s))
             if (string.IsNullOrEmpty(s))
             {
             {

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

@@ -52,7 +52,7 @@ namespace Jint.Native.RegExp
             var r = TypeConverter.ToObject(Engine, thisObj);
             var r = TypeConverter.ToObject(Engine, thisObj);
             if (r.Class != "RegExp")
             if (r.Class != "RegExp")
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var match = Exec(r, arguments);
             var match = Exec(r, arguments);
@@ -64,7 +64,7 @@ namespace Jint.Native.RegExp
             var R = TypeConverter.ToObject(Engine, thisObj) as RegExpInstance;
             var R = TypeConverter.ToObject(Engine, thisObj) as RegExpInstance;
             if (ReferenceEquals(R, null))
             if (ReferenceEquals(R, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             var s = TypeConverter.ToString(arguments.At(0));
             var s = TypeConverter.ToString(arguments.At(0));

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

@@ -70,12 +70,18 @@ namespace Jint.Native.String
 
 
         public StringInstance Construct(string value)
         public StringInstance Construct(string value)
         {
         {
-            var instance = new StringInstance(Engine);
-            instance.Prototype = PrototypeObject;
-            instance.PrimitiveValue = value;
-            instance.Extensible = true;
+            return Construct(JsString.Create(value));
+        }
 
 
-            instance.SetOwnProperty("length", new PropertyDescriptor(value.Length, PropertyFlag.AllForbidden));
+        public StringInstance Construct(JsString value)
+        {
+            var instance = new StringInstance(Engine)
+            {
+                Prototype = PrototypeObject,
+                PrimitiveValue = value,
+                Extensible = true,
+                _length = new PropertyDescriptor(value.Length, PropertyFlag.AllForbidden)
+            };
 
 
             return instance;
             return instance;
         }
         }

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

@@ -10,7 +10,7 @@ namespace Jint.Native.String
         private const string PropertyNameLength = "length";
         private const string PropertyNameLength = "length";
         private const int PropertyNameLengthLength = 6;
         private const int PropertyNameLengthLength = 6;
 
 
-        private PropertyDescriptor _length;
+        internal PropertyDescriptor _length;
 
 
         public StringInstance(Engine engine)
         public StringInstance(Engine engine)
             : base(engine, objectClass: "String")
             : base(engine, objectClass: "String")
@@ -21,7 +21,7 @@ namespace Jint.Native.String
 
 
         JsValue IPrimitiveInstance.PrimitiveValue => PrimitiveValue;
         JsValue IPrimitiveInstance.PrimitiveValue => PrimitiveValue;
 
 
-        public JsValue PrimitiveValue { get; set; }
+        public JsString PrimitiveValue { get; set; }
 
 
         private static bool IsInt(double d)
         private static bool IsInt(double d)
         {
         {

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

@@ -25,7 +25,7 @@ namespace Jint.Native.String
         {
         {
             var obj = new StringPrototype(engine);
             var obj = new StringPrototype(engine);
             obj.Prototype = engine.Object.PrototypeObject;
             obj.Prototype = engine.Object.PrototypeObject;
-            obj.PrimitiveValue = "";
+            obj.PrimitiveValue = JsString.Empty;
             obj.Extensible = true;
             obj.Extensible = true;
             obj.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden));
             obj.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden));
             obj.SetOwnProperty("constructor", new PropertyDescriptor(stringConstructor, PropertyFlag.NonEnumerable));
             obj.SetOwnProperty("constructor", new PropertyDescriptor(stringConstructor, PropertyFlag.NonEnumerable));
@@ -65,7 +65,7 @@ namespace Jint.Native.String
             var s = TypeConverter.ToObject(Engine, thisObj) as StringInstance;
             var s = TypeConverter.ToObject(Engine, thisObj) as StringInstance;
             if (ReferenceEquals(s, null))
             if (ReferenceEquals(s, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             return s.PrimitiveValue;
             return s.PrimitiveValue;
@@ -278,10 +278,10 @@ namespace Jint.Native.String
             }
             }
             else if (separator.IsUndefined())
             else if (separator.IsUndefined())
             {
             {
-                var jsValues = Engine.JsValueArrayPool.RentArray(1);
+                var jsValues = _engine._jsValueArrayPool.RentArray(1);
                 jsValues[0] = s;
                 jsValues[0] = s;
                 var arrayInstance = (ArrayInstance)Engine.Array.Construct(jsValues);
                 var arrayInstance = (ArrayInstance)Engine.Array.Construct(jsValues);
-                Engine.JsValueArrayPool.ReturnArray(jsValues);
+                _engine._jsValueArrayPool.ReturnArray(jsValues);
                 return arrayInstance;
                 return arrayInstance;
             }
             }
             else
             else
@@ -395,18 +395,18 @@ namespace Jint.Native.String
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
 
 
             var start = TypeConverter.ToNumber(arguments.At(0));
             var start = TypeConverter.ToNumber(arguments.At(0));
-            if (double.NegativeInfinity.Equals(start))
+            if (double.IsNegativeInfinity(start))
             {
             {
                 start = 0;
                 start = 0;
             }
             }
-            if (double.PositiveInfinity.Equals(start))
+            if (double.IsPositiveInfinity(start))
             {
             {
                 return string.Empty;
                 return string.Empty;
             }
             }
 
 
             var s = TypeConverter.ToString(thisObj);
             var s = TypeConverter.ToString(thisObj);
             var end = TypeConverter.ToNumber(arguments.At(1));
             var end = TypeConverter.ToNumber(arguments.At(1));
-            if (double.PositiveInfinity.Equals(end))
+            if (double.IsPositiveInfinity(end))
             {
             {
                 end = s.Length;
                 end = s.Length;
             }
             }
@@ -597,14 +597,14 @@ namespace Jint.Native.String
                     return thisString;
                     return thisString;
                 int end = start + substr.Length;
                 int end = start + substr.Length;
 
 
-                var args = Engine.JsValueArrayPool.RentArray(3);
+                var args = _engine._jsValueArrayPool.RentArray(3);
                 args[0] = substr;
                 args[0] = substr;
                 args[1] = start;
                 args[1] = start;
                 args[2] = thisString;
                 args[2] = thisString;
 
 
                 var replaceString = TypeConverter.ToString(replaceFunction.Call(Undefined, args));
                 var replaceString = TypeConverter.ToString(replaceFunction.Call(Undefined, args));
                 
                 
-                Engine.JsValueArrayPool.ReturnArray(args);
+                _engine._jsValueArrayPool.ReturnArray(args);
 
 
                 // Replace only the first match.
                 // Replace only the first match.
                 var result = StringExecutionContext.Current.GetStringBuilder(thisString.Length + (substr.Length - substr.Length));
                 var result = StringExecutionContext.Current.GetStringBuilder(thisString.Length + (substr.Length - substr.Length));
@@ -818,7 +818,7 @@ namespace Jint.Native.String
             var s = thisObj.TryCast<StringInstance>();
             var s = thisObj.TryCast<StringInstance>();
             if (ReferenceEquals(s, null))
             if (ReferenceEquals(s, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             return s.PrimitiveValue;
             return s.PrimitiveValue;
@@ -894,7 +894,7 @@ namespace Jint.Native.String
             {
             {
                 if (searchString.IsRegExp())
                 if (searchString.IsRegExp())
                 {
                 {
-                    throw new JavaScriptException(Engine.TypeError);
+                    ExceptionHelper.ThrowTypeError(Engine);
                 }
                 }
             }
             }
 
 

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

@@ -66,7 +66,7 @@ namespace Jint.Native.Symbol
 
 
             var value = new JsSymbol(description.AsString());
             var value = new JsSymbol(description.AsString());
 
 
-            if (JsValue.ReturnOnAbruptCompletion(ref descString))
+            if (ReturnOnAbruptCompletion(ref descString))
             {
             {
                 return descString;
                 return descString;
             }
             }
@@ -95,7 +95,7 @@ namespace Jint.Native.Symbol
 
 
             if (!sym.IsSymbol())
             if (!sym.IsSymbol())
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             JsSymbol symbol;
             JsSymbol symbol;
@@ -109,15 +109,23 @@ namespace Jint.Native.Symbol
 
 
         public ObjectInstance Construct(JsValue[] arguments)
         public ObjectInstance Construct(JsValue[] arguments)
         {
         {
-            throw new JavaScriptException(Engine.TypeError);
+            ExceptionHelper.ThrowTypeError(Engine);
+            return null;
         }
         }
 
 
         public SymbolInstance Construct(string description)
         public SymbolInstance Construct(string description)
         {
         {
-            var instance = new SymbolInstance(Engine);
-            instance.Prototype = PrototypeObject;
-            instance.SymbolData = new JsSymbol(description);
-            instance.Extensible = true;
+            return Construct(new JsSymbol(description));
+        }
+
+        public SymbolInstance Construct(JsSymbol symbol)
+        {
+            var instance = new SymbolInstance(Engine)
+            {
+                Prototype = PrototypeObject,
+                SymbolData = symbol, 
+                Extensible = true
+            };
 
 
             return instance;
             return instance;
         }
         }

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

@@ -45,7 +45,7 @@ namespace Jint.Native.Symbol
         {
         {
             if (!thisObject.IsSymbol())
             if (!thisObject.IsSymbol())
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             return SymbolDescriptiveString((JsSymbol)thisObject);
             return SymbolDescriptiveString((JsSymbol)thisObject);
@@ -56,7 +56,7 @@ namespace Jint.Native.Symbol
             var sym = thisObject.TryCast<SymbolInstance>();
             var sym = thisObject.TryCast<SymbolInstance>();
             if (ReferenceEquals(sym, null))
             if (ReferenceEquals(sym, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             return sym.SymbolData;
             return sym.SymbolData;
@@ -73,7 +73,7 @@ namespace Jint.Native.Symbol
             var o = thisObject.AsInstance<SymbolInstance>();
             var o = thisObject.AsInstance<SymbolInstance>();
             if (ReferenceEquals(o, null))
             if (ReferenceEquals(o, null))
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
             }
             }
 
 
             return o.SymbolData;
             return o.SymbolData;

+ 2 - 7
Jint/Options.cs

@@ -3,13 +3,12 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
-using System.Runtime.CompilerServices;
 using Jint.Native;
 using Jint.Native;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop;
 
 
 namespace Jint
 namespace Jint
 {
 {
-    public class Options
+    public sealed class Options
     {
     {
         private bool _discardGlobal;
         private bool _discardGlobal;
         private bool _strict;
         private bool _strict;
@@ -177,11 +176,7 @@ namespace Jint
 
 
         internal long _MemoryLimit => _memoryLimit;
         internal long _MemoryLimit => _memoryLimit;
 
 
-        internal int _MaxStatements
-        {
-            [MethodImpl(MethodImplOptions.AggressiveInlining)]
-            get { return _maxStatements; }
-        }
+        internal int _MaxStatements => _maxStatements;
 
 
         internal int MaxRecursionDepth => _maxRecursionDepth;
         internal int MaxRecursionDepth => _maxRecursionDepth;
 
 

+ 5 - 8
Jint/Pooling/ArgumentsInstancePool.cs

@@ -23,7 +23,11 @@ namespace Jint.Pooling
 
 
         private ArgumentsInstance Factory()
         private ArgumentsInstance Factory()
         {
         {
-            return new ArgumentsInstance(_engine);
+            return new ArgumentsInstance(_engine)
+            {
+                Prototype = _engine.Object.PrototypeObject,
+                Extensible = true
+            };
         }
         }
 
 
         public ArgumentsInstance Rent(
         public ArgumentsInstance Rent(
@@ -35,13 +39,6 @@ namespace Jint.Pooling
         {
         {
             var obj = _pool.Allocate();
             var obj = _pool.Allocate();
             obj.Prepare(func, names, args, env, strict);
             obj.Prepare(func, names, args, env, strict);
-
-            // These properties are pre-initialized as their don't trigger
-            // the EnsureInitialized() event and are cheap
-            obj.Prototype = _engine.Object.PrototypeObject;
-            obj.Extensible = true;
-            obj.Strict = strict;
-
             return obj;
             return obj;
         }
         }
 
 

+ 5 - 5
Jint/Pooling/ObjectPool.cs

@@ -134,7 +134,7 @@ namespace Jint.Pooling
             // We will interlock only when we have a candidate. in a worst case we may miss some
             // We will interlock only when we have a candidate. in a worst case we may miss some
             // recently returned objects. Not a big deal.
             // recently returned objects. Not a big deal.
             T inst = _firstItem;
             T inst = _firstItem;
-            if (inst != null)
+            if (!ReferenceEquals(inst, null))
             {
             {
                 _firstItem = null;
                 _firstItem = null;
                 return inst;
                 return inst;
@@ -161,7 +161,7 @@ namespace Jint.Pooling
             for (int i = 0; i < items.Length; i++)
             for (int i = 0; i < items.Length; i++)
             {
             {
                 T inst = items[i].Value;
                 T inst = items[i].Value;
-                if (inst != null)
+                if (!ReferenceEquals(inst, null))
                 {
                 {
                     items[i].Value = null;
                     items[i].Value = null;
                     return inst;
                     return inst;
@@ -184,7 +184,7 @@ namespace Jint.Pooling
             Validate(obj);
             Validate(obj);
             ForgetTrackedObject(obj);
             ForgetTrackedObject(obj);
  
  
-            if (_firstItem == null)
+            if (ReferenceEquals(_firstItem, null))
             {
             {
                 // Intentionally not using interlocked here. 
                 // Intentionally not using interlocked here. 
                 // In a worst case scenario two objects may be stored into same slot.
                 // In a worst case scenario two objects may be stored into same slot.
@@ -202,7 +202,7 @@ namespace Jint.Pooling
             var items = _items;
             var items = _items;
             for (int i = 0; i < items.Length; i++)
             for (int i = 0; i < items.Length; i++)
             {
             {
-                if (items[i].Value == null)
+                if (ReferenceEquals(items[i].Value, null))
                 {
                 {
                     // Intentionally not using interlocked here. 
                     // Intentionally not using interlocked here. 
                     // In a worst case scenario two objects may be stored into same slot.
                     // In a worst case scenario two objects may be stored into same slot.
@@ -265,7 +265,7 @@ namespace Jint.Pooling
             for (int i = 0; i < items.Length; i++)
             for (int i = 0; i < items.Length; i++)
             {
             {
                 var value = items[i].Value;
                 var value = items[i].Value;
-                if (value == null)
+                if (ReferenceEquals(value, null))
                 {
                 {
                     return;
                     return;
                 }
                 }

+ 0 - 65
Jint/ReflectionExtensions.cs

@@ -1,65 +0,0 @@
-#if NETSTANDARD1_3
-using System;
-using System.Linq;
-using System.Reflection;
-
-namespace Jint
-{
-    internal static class ReflectionExtensions
-    {
-        internal static bool IsEnum(this Type type)
-        {
-            return type.GetTypeInfo().IsEnum;
-        }
-
-        internal static bool IsGenericType(this Type type)
-        {
-            return type.GetTypeInfo().IsGenericType;
-        }
-
-        internal static bool IsValueType(this Type type)
-        {
-            return type.GetTypeInfo().IsValueType;
-        }
-
-        internal static bool HasAttribute<T>(this ParameterInfo member) where T : Attribute
-        {
-            return member.GetCustomAttributes<T>().Any();
-        }
-    }
-}
-#else
-using System;
-using System.Reflection;
-
-namespace Jint
-{
-    internal static class ReflectionExtensions
-    {
-        internal static bool IsEnum(this Type type)
-        {
-            return type.IsEnum;
-        }
-
-        internal static bool IsGenericType(this Type type)
-        {
-            return type.IsGenericType;
-        }
-
-        internal static bool IsValueType(this Type type)
-        {
-            return type.IsValueType;
-        }
-
-        internal static bool HasAttribute<T>(this ParameterInfo member) where T : Attribute
-        {
-            return Attribute.IsDefined(member, typeof(T));
-        }
-
-        internal static MethodInfo GetMethodInfo(this Delegate d)
-        {
-            return d.Method;
-        }
-    }
-}
-#endif

+ 1 - 1
Jint/Runtime/Arguments.cs

@@ -23,7 +23,7 @@ namespace Jint.Runtime
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static JsValue At(this JsValue[] args, int index, JsValue undefinedValue)
         public static JsValue At(this JsValue[] args, int index, JsValue undefinedValue)
         {
         {
-            return args.Length > index ? args[index] : undefinedValue;
+            return index < args.Length ? args[index] : undefinedValue;
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]

+ 7 - 7
Jint/Runtime/CallStack/CallStackElement.cs

@@ -1,11 +1,11 @@
-namespace Jint.Runtime
-{
-    using Esprima.Ast;
-    using Jint.Native;
+using Esprima.Ast;
+using Jint.Native;
 
 
+namespace Jint.Runtime
+{
     public class CallStackElement
     public class CallStackElement
     {
     {
-        private string _shortDescription;
+        private readonly string _shortDescription;
 
 
         public CallStackElement(CallExpression callExpression, JsValue function, string shortDescription)
         public CallStackElement(CallExpression callExpression, JsValue function, string shortDescription)
         {
         {
@@ -14,9 +14,9 @@
             Function = function;
             Function = function;
         }
         }
 
 
-        public CallExpression CallExpression { get; private set; }
+        public CallExpression CallExpression { get; }
 
 
-        public JsValue Function { get; private set; }
+        public JsValue Function { get; }
 
 
         public override string ToString()
         public override string ToString()
         {
         {

+ 4 - 4
Jint/Runtime/CallStack/JintCallStack.cs

@@ -1,15 +1,15 @@
 using System.Collections;
 using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
 
 
 namespace Jint.Runtime.CallStack
 namespace Jint.Runtime.CallStack
 {
 {
-    using System.Collections.Generic;
-    using System.Linq;
 
 
     public class JintCallStack : IEnumerable<CallStackElement>
     public class JintCallStack : IEnumerable<CallStackElement>
     {
     {
-        private Stack<CallStackElement> _stack = new Stack<CallStackElement>();
+        private readonly Stack<CallStackElement> _stack = new Stack<CallStackElement>();
 
 
-        private Dictionary<CallStackElement, int> _statistics =
+        private readonly Dictionary<CallStackElement, int> _statistics =
             new Dictionary<CallStackElement, int>(new CallStackElementComparer());
             new Dictionary<CallStackElement, int>(new CallStackElementComparer());
 
 
         public int Push(CallStackElement item)
         public int Push(CallStackElement item)

+ 5 - 5
Jint/Runtime/Debugger/BreakPoint.cs

@@ -1,11 +1,7 @@
 namespace Jint.Runtime.Debugger
 namespace Jint.Runtime.Debugger
 {
 {
-    public class BreakPoint
+    public sealed class BreakPoint
     {
     {
-        public int Line { get; set; }
-        public int Char { get; set; }
-        public string Condition { get; set; }
-
         public BreakPoint(int line, int character)
         public BreakPoint(int line, int character)
         {
         {
             Line = line;
             Line = line;
@@ -17,5 +13,9 @@
         {
         {
             Condition = condition;
             Condition = condition;
         }
         }
+
+        public int Line { get; }
+        public int Char { get; }
+        public string Condition { get; }
     }
     }
 }
 }

+ 35 - 24
Jint/Runtime/Descriptors/PropertyDescriptor.cs

@@ -1,5 +1,4 @@
-using System;
-using System.Runtime.CompilerServices;
+using System.Runtime.CompilerServices;
 using Jint.Native;
 using Jint.Native;
 using Jint.Native.Object;
 using Jint.Native.Object;
 using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Descriptors.Specialized;
@@ -10,23 +9,30 @@ namespace Jint.Runtime.Descriptors
     {
     {
         public static readonly PropertyDescriptor Undefined = new PropertyDescriptor(PropertyFlag.None);
         public static readonly PropertyDescriptor Undefined = new PropertyDescriptor(PropertyFlag.None);
 
 
-        private PropertyFlag _flags;
-        private JsValue _value;
+        internal PropertyFlag _flags;
+        internal JsValue _value;
 
 
         protected PropertyDescriptor(PropertyFlag flags)
         protected PropertyDescriptor(PropertyFlag flags)
         {
         {
             _flags = flags;
             _flags = flags;
         }
         }
         
         
-        
         protected internal PropertyDescriptor(JsValue value, PropertyFlag flags) : this(flags)
         protected internal PropertyDescriptor(JsValue value, PropertyFlag flags) : this(flags)
         {
         {
-            Value = value;
+            if ((_flags & PropertyFlag.CustomJsValue) != 0)
+            {
+                CustomValue = value;
+            }
+            _value = value;
         }
         }
 
 
         public PropertyDescriptor(JsValue value, bool? writable, bool? enumerable, bool? configurable)
         public PropertyDescriptor(JsValue value, bool? writable, bool? enumerable, bool? configurable)
         {
         {
-            Value = value;
+            if ((_flags & PropertyFlag.CustomJsValue) != 0)
+            {
+                CustomValue = value;
+            }
+            _value = value;  
 
 
             if (writable != null)
             if (writable != null)
             {
             {
@@ -198,10 +204,10 @@ namespace Jint.Runtime.Descriptors
             }
             }
         }
         }
 
 
-        protected virtual JsValue CustomValue
+        protected internal virtual JsValue CustomValue
         {
         {
             get => null;
             get => null;
-            set => throw new NotImplementedException();
+            set => ExceptionHelper.ThrowNotImplementedException();
         }
         }
 
 
         internal PropertyFlag Flags
         internal PropertyFlag Flags
@@ -215,7 +221,7 @@ namespace Jint.Runtime.Descriptors
             var obj = o.TryCast<ObjectInstance>();
             var obj = o.TryCast<ObjectInstance>();
             if (ReferenceEquals(obj, null))
             if (ReferenceEquals(obj, null))
             {
             {
-                throw new JavaScriptException(engine.TypeError);
+                ExceptionHelper.ThrowTypeError(engine);
             }
             }
 
 
             var getProperty = obj.GetProperty("get");
             var getProperty = obj.GetProperty("get");
@@ -226,7 +232,7 @@ namespace Jint.Runtime.Descriptors
             if ((obj.HasProperty("value") || obj.HasProperty("writable")) &&
             if ((obj.HasProperty("value") || obj.HasProperty("writable")) &&
                 (hasGetProperty || hasSetProperty))
                 (hasGetProperty || hasSetProperty))
             {
             {
-                throw new JavaScriptException(engine.TypeError);
+                ExceptionHelper.ThrowTypeError(engine);
             }
             }
 
 
             var desc = hasGetProperty || hasSetProperty
             var desc = hasGetProperty || hasSetProperty
@@ -265,7 +271,7 @@ namespace Jint.Runtime.Descriptors
                 var getter = obj.UnwrapJsValue(getProperty);
                 var getter = obj.UnwrapJsValue(getProperty);
                 if (!getter.IsUndefined() && getter.TryCast<ICallable>() == null)
                 if (!getter.IsUndefined() && getter.TryCast<ICallable>() == null)
                 {
                 {
-                    throw new JavaScriptException(engine.TypeError);
+                    ExceptionHelper.ThrowTypeError(engine);
                 }
                 }
 
 
                 ((GetSetPropertyDescriptor) desc).SetGet(getter);
                 ((GetSetPropertyDescriptor) desc).SetGet(getter);
@@ -276,7 +282,7 @@ namespace Jint.Runtime.Descriptors
                 var setter = obj.UnwrapJsValue(setProperty);
                 var setter = obj.UnwrapJsValue(setProperty);
                 if (!setter.IsUndefined() && setter.TryCast<ICallable>() == null)
                 if (!setter.IsUndefined() && setter.TryCast<ICallable>() == null)
                 {
                 {
-                    throw new JavaScriptException(engine.TypeError);
+                    ExceptionHelper.ThrowTypeError(engine);
                 }
                 }
 
 
                 ((GetSetPropertyDescriptor) desc).SetSet(setter);
                 ((GetSetPropertyDescriptor) desc).SetSet(setter);
@@ -286,7 +292,7 @@ namespace Jint.Runtime.Descriptors
             {
             {
                 if (!ReferenceEquals(desc.Value, null) || desc.WritableSet)
                 if (!ReferenceEquals(desc.Value, null) || desc.WritableSet)
                 {
                 {
-                    throw new JavaScriptException(engine.TypeError);
+                    ExceptionHelper.ThrowTypeError(engine);
                 }
                 }
             }
             }
 
 
@@ -328,7 +334,9 @@ namespace Jint.Runtime.Descriptors
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsDataDescriptor()
         public bool IsDataDescriptor()
         {
         {
-            return WritableSet || !ReferenceEquals(Value, null);
+            return (_flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != 0 
+                   || (_flags & PropertyFlag.CustomJsValue) != 0 && !ReferenceEquals(CustomValue, null)
+                   || !ReferenceEquals(_value, null);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -346,22 +354,25 @@ namespace Jint.Runtime.Descriptors
         {
         {
             value = JsValue.Undefined;
             value = JsValue.Undefined;
 
 
-            if (this == Undefined)
-            {
-                value = JsValue.Undefined;
-                return false;
-            }
-
-            if (IsDataDescriptor())
+            // IsDataDescriptor logic inlined
+            if ((_flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != 0)
             {
             {
-                var val = Value;
+                var val = (_flags & PropertyFlag.CustomJsValue) != 0
+                    ? CustomValue
+                    : _value;
+                
                 if (!ReferenceEquals(val, null))
                 if (!ReferenceEquals(val, null))
                 {
                 {
                     value = val;
                     value = val;
                     return true;
                     return true;
                 }
                 }
             }
             }
-
+            
+            if (this == Undefined)
+            {
+                return false;
+            }
+            
             var getter = Get;
             var getter = Get;
             if (!ReferenceEquals(getter, null) && !getter.IsUndefined())
             if (!ReferenceEquals(getter, null) && !getter.IsUndefined())
             {
             {

+ 1 - 1
Jint/Runtime/Descriptors/Specialized/FieldInfoDescriptor.cs

@@ -19,7 +19,7 @@ namespace Jint.Runtime.Descriptors.Specialized
             Writable = !fieldInfo.Attributes.HasFlag(FieldAttributes.InitOnly); // don't write to fields marked as readonly
             Writable = !fieldInfo.Attributes.HasFlag(FieldAttributes.InitOnly); // don't write to fields marked as readonly
         }
         }
 
 
-        protected override JsValue CustomValue
+        protected internal override JsValue CustomValue
         {
         {
             get => JsValue.FromObject(_engine, _fieldInfo.GetValue(_item));
             get => JsValue.FromObject(_engine, _fieldInfo.GetValue(_item));
             set
             set

+ 14 - 7
Jint/Runtime/Descriptors/Specialized/IndexDescriptor.cs

@@ -20,20 +20,27 @@ namespace Jint.Runtime.Descriptors.Specialized
 
 
             // get all instance indexers with exactly 1 argument
             // get all instance indexers with exactly 1 argument
             var indexers = targetType.GetProperties();
             var indexers = targetType.GetProperties();
+            var paramTypeArray = new Type[1];
 
 
             // try to find first indexer having either public getter or setter with matching argument type
             // try to find first indexer having either public getter or setter with matching argument type
             foreach (var indexer in indexers)
             foreach (var indexer in indexers)
             {
             {
-                if (indexer.GetIndexParameters().Length != 1) continue;
+                var indexParameters = indexer.GetIndexParameters();
+                if (indexParameters.Length != 1)
+                {
+                    continue;
+                }
+
                 if (indexer.GetGetMethod() != null || indexer.GetSetMethod() != null)
                 if (indexer.GetGetMethod() != null || indexer.GetSetMethod() != null)
                 {
                 {
-                    var paramType = indexer.GetIndexParameters()[0].ParameterType;
+                    var paramType = indexParameters[0].ParameterType;
 
 
                     if (_engine.ClrTypeConverter.TryConvert(key, paramType, CultureInfo.InvariantCulture, out _key))
                     if (_engine.ClrTypeConverter.TryConvert(key, paramType, CultureInfo.InvariantCulture, out _key))
                     {
                     {
                         _indexer = indexer;
                         _indexer = indexer;
                         // get contains key method to avoid index exception being thrown in dictionaries
                         // get contains key method to avoid index exception being thrown in dictionaries
-                        _containsKey = targetType.GetMethod("ContainsKey", new Type[] {paramType});
+                        paramTypeArray[0] = paramType;
+                        _containsKey = targetType.GetMethod("ContainsKey", paramTypeArray);
                         break;
                         break;
                     }
                     }
                 }
                 }
@@ -42,7 +49,7 @@ namespace Jint.Runtime.Descriptors.Specialized
             // throw if no indexer found
             // throw if no indexer found
             if (_indexer == null)
             if (_indexer == null)
             {
             {
-                throw new InvalidOperationException("No matching indexer found.");
+                ExceptionHelper.ThrowInvalidOperationException("No matching indexer found.");
             }
             }
 
 
             Writable = true;
             Writable = true;
@@ -53,7 +60,7 @@ namespace Jint.Runtime.Descriptors.Specialized
         {
         {
         }
         }
 
 
-        protected override JsValue CustomValue
+        protected internal override JsValue CustomValue
         {
         {
             get
             get
             {
             {
@@ -61,7 +68,7 @@ namespace Jint.Runtime.Descriptors.Specialized
 
 
                 if (getter == null)
                 if (getter == null)
                 {
                 {
-                    throw new InvalidOperationException("Indexer has no public getter.");
+                    ExceptionHelper.ThrowInvalidOperationException("Indexer has no public getter.");
                 }
                 }
 
 
                 object[] parameters = {_key};
                 object[] parameters = {_key};
@@ -89,7 +96,7 @@ namespace Jint.Runtime.Descriptors.Specialized
                 var setter = _indexer.GetSetMethod();
                 var setter = _indexer.GetSetMethod();
                 if (setter == null)
                 if (setter == null)
                 {
                 {
-                    throw new InvalidOperationException("Indexer has no public setter.");
+                    ExceptionHelper.ThrowInvalidOperationException("Indexer has no public setter.");
                 }
                 }
 
 
                 object[] parameters = {_key, value?.ToObject()};
                 object[] parameters = {_key, value?.ToObject()};

+ 1 - 1
Jint/Runtime/Descriptors/Specialized/PropertyInfoDescriptor.cs

@@ -19,7 +19,7 @@ namespace Jint.Runtime.Descriptors.Specialized
             Writable = propertyInfo.CanWrite;
             Writable = propertyInfo.CanWrite;
         }
         }
 
 
-        protected override JsValue CustomValue
+        protected internal override JsValue CustomValue
         {
         {
             get => JsValue.FromObject(_engine, _propertyInfo.GetValue(_item, null));
             get => JsValue.FromObject(_engine, _propertyInfo.GetValue(_item, null));
             set
             set

+ 10 - 3
Jint/Runtime/Environments/Binding.cs

@@ -2,10 +2,17 @@
 
 
 namespace Jint.Runtime.Environments
 namespace Jint.Runtime.Environments
 {
 {
-    public class Binding
+    public struct Binding
     {
     {
+        public Binding(JsValue value, bool canBeDeleted, bool mutable)
+        {
+            Value = value;
+            CanBeDeleted = canBeDeleted;
+            Mutable = mutable;
+        }
+
         public JsValue Value;
         public JsValue Value;
-        public bool CanBeDeleted;
-        public bool Mutable;
+        public readonly bool CanBeDeleted;
+        public readonly bool Mutable;
     }
     }
 }
 }

+ 199 - 72
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -1,6 +1,9 @@
-using System;
+using System;
+using System.Collections.Generic;
+using Esprima.Ast;
 using Jint.Native;
 using Jint.Native;
 using Jint.Native.Argument;
 using Jint.Native.Argument;
+using Jint.Native.Function;
 
 
 namespace Jint.Runtime.Environments
 namespace Jint.Runtime.Environments
 {
 {
@@ -10,56 +13,113 @@ namespace Jint.Runtime.Environments
     /// </summary>
     /// </summary>
     public sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
     public sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
     {
     {
+        private StructDictionary<Binding> _dictionary;
+        private bool _set;
+        private string _key;
+        private Binding _value;
+
         private const string BindingNameArguments = "arguments";
         private const string BindingNameArguments = "arguments";
         private Binding _argumentsBinding;
         private Binding _argumentsBinding;
 
 
-        private MruPropertyCache2<Binding> _bindings;
-
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         {
         {
         }
         }
 
 
-        public override bool HasBinding(string name)
+        private void SetItem(string key, in Binding value)
         {
         {
-            if (_argumentsBinding != null && name.Length == 9 && name == BindingNameArguments)
+            if (_set && _key != key)
             {
             {
-                return true;
+                if (_dictionary == null)
+                {
+                    _dictionary = new StructDictionary<Binding>();
+                }
+
+                _dictionary.TryInsert(_key, _value, InsertionBehavior.OverwriteExisting);
             }
             }
 
 
-            return _bindings?.ContainsKey(name) == true;
+            _set = true;
+            _key = key;
+            _value = value;
+
+            _dictionary?.TryInsert(key, value, InsertionBehavior.OverwriteExisting);
         }
         }
 
 
-        public override void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false)
+        private ref Binding GetExistingItem(string key)
+        {
+            if (_set && _key == key)
+            {
+                return ref _value;
+            }
+
+            if (key.Length == 9 && key == BindingNameArguments)
+            {
+                return ref _argumentsBinding;
+            }
+
+            return ref _dictionary.GetItem(key);
+        }
+
+        private bool ContainsKey(string key)
         {
         {
-            var binding = new Binding
+            if (key.Length == 9 && key == BindingNameArguments)
             {
             {
-                Value = value,
-                CanBeDeleted =  canBeDeleted,
-                Mutable = true
-            };
+                return !ReferenceEquals(_argumentsBinding.Value, null);
+            }
+
+            if (_set && key == _key)
+            {
+                return true;
+            }
+
+            return _dictionary?.ContainsKey(key) == true;
+        }
 
 
-            if (name == BindingNameArguments)
+        private void Remove(string key)
+        {
+            if (_set && key == _key)
+            {
+                _set = false;
+                _key = null;
+                _value = default;
+            }
+            
+            if (key == BindingNameArguments)
             {
             {
-                _argumentsBinding = binding;
+                _argumentsBinding.Value = null;
             }
             }
             else
             else
             {
             {
-                if (_bindings == null)
-                {
-                    _bindings = new MruPropertyCache2<Binding>();
-                }
-                _bindings.Add(name, binding);
+                _dictionary?.Remove(key);
             }
             }
+
         }
         }
 
 
-        public override void SetMutableBinding(string name, JsValue value, bool strict)
+        private bool TryGetValue(string key, out Binding value)
         {
         {
-            if (_bindings == null)
+            value = default;
+            if (_set && _key == key)
             {
             {
-                _bindings = new MruPropertyCache2<Binding>();
+                value = _value;
+                return true;
             }
             }
 
 
-            var binding = name == BindingNameArguments ? _argumentsBinding : _bindings[name];
+            return _dictionary?.TryGetValue(key, out value) == true;
+        }
+
+        public override bool HasBinding(string name)
+        {
+            return ContainsKey(name);
+        }
+
+        public override void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false)
+        {
+            SetItem(name, new Binding(value, canBeDeleted, mutable: true));
+        }
+
+        public override void SetMutableBinding(string name, JsValue value, bool strict)
+        {
+            ref var binding = ref GetExistingItem(name);
+
             if (binding.Mutable)
             if (binding.Mutable)
             {
             {
                 binding.Value = value;
                 binding.Value = value;
@@ -68,20 +128,20 @@ namespace Jint.Runtime.Environments
             {
             {
                 if (strict)
                 if (strict)
                 {
                 {
-                    throw new JavaScriptException(_engine.TypeError, "Can't update the value of an immutable binding.");
+                    ExceptionHelper.ThrowTypeError(_engine, "Can't update the value of an immutable binding.");
                 }
                 }
             }
             }
         }
         }
 
 
         public override JsValue GetBindingValue(string name, bool strict)
         public override JsValue GetBindingValue(string name, bool strict)
         {
         {
-            var binding = name == BindingNameArguments ? _argumentsBinding : _bindings[name];
+            ref var binding = ref GetExistingItem(name);
 
 
-            if (!binding.Mutable && binding.Value.IsUndefined())
+            if (!binding.Mutable && binding.Value._type == Types.Undefined)
             {
             {
                 if (strict)
                 if (strict)
                 {
                 {
-                    throw new JavaScriptException(_engine.ReferenceError, "Can't access anm uninitiazed immutable binding.");
+                    ExceptionHelper.ThrowReferenceError(_engine, "Can't access an uninitialized immutable binding.");
                 }
                 }
 
 
                 return Undefined;
                 return Undefined;
@@ -92,17 +152,9 @@ namespace Jint.Runtime.Environments
 
 
         public override bool DeleteBinding(string name)
         public override bool DeleteBinding(string name)
         {
         {
-            Binding binding = null;
-            if (name == BindingNameArguments)
-            {
-                binding = _argumentsBinding;
-            }
-            else
-            {
-                _bindings?.TryGetValue(name, out binding);
-            }
+            ref Binding binding = ref GetExistingItem(name);
 
 
-            if (binding == null)
+            if (ReferenceEquals(binding.Value, null))
             {
             {
                 return true;
                 return true;
             }
             }
@@ -112,14 +164,7 @@ namespace Jint.Runtime.Environments
                 return false;
                 return false;
             }
             }
 
 
-            if (name == BindingNameArguments)
-            {
-                _argumentsBinding = null;
-            }
-            else
-            {
-                _bindings.Remove(name);
-            }
+            Remove(name);
 
 
             return true;
             return true;
         }
         }
@@ -129,50 +174,132 @@ namespace Jint.Runtime.Environments
             return Undefined;
             return Undefined;
         }
         }
 
 
-        /// <summary>
-        /// Creates a new immutable binding in an environment record.
-        /// </summary>
-        /// <param name="name">The identifier of the binding.</param>
-        /// <param name="value">The value  of the binding.</param>
-        public void CreateImmutableBinding(string name, JsValue value)
+        /// <inheritdoc />
+        public override string[] GetAllBindingNames()
         {
         {
-            var binding = new Binding
+            int size = _set ? 1 : 0;
+            if (!ReferenceEquals(_argumentsBinding.Value, null))
+            {
+                size += 1;
+            }
+
+            if (_dictionary != null)
             {
             {
-                Value = value,
-                Mutable = false,
-                CanBeDeleted = false
-            };
+                size += _dictionary.Count;
+            }
 
 
-            if (name == BindingNameArguments)
+            var keys = size > 0 ? new string[size] : Array.Empty<string>();
+            int n = 0;
+            if (_set)
             {
             {
-                _argumentsBinding = binding;
+                keys[n++] = _key;
             }
             }
-            else
+
+            if (!ReferenceEquals(_argumentsBinding.Value, null))
+            {
+                keys[n++] = BindingNameArguments;
+            }
+
+            if (_dictionary != null)
             {
             {
-                if (_bindings == null)
+                foreach (var key in _dictionary.Keys)
                 {
                 {
-                    _bindings = new MruPropertyCache2<Binding>();
+                    keys[n++] = key;
                 }
                 }
-                _bindings.Add(name, binding);
             }
             }
+
+            return keys;
+        }
+
+        internal void ReleaseArguments()
+        {
+            _engine._argumentsInstancePool.Return(_argumentsBinding.Value as ArgumentsInstance);
+            _argumentsBinding = default;
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Returns an array of all the defined binding names
+        /// Optimized version for function calls.
         /// </summary>
         /// </summary>
-        /// <returns>The array of all defined bindings</returns>
-        public override string[] GetAllBindingNames()
+        internal void AddFunctionParameters(
+            FunctionInstance functionInstance,
+            JsValue[] arguments,
+            ArgumentsInstance argumentsInstance)
         {
         {
-            return _bindings?.GetKeys() ?? Array.Empty<string>();
+            var parameters = functionInstance._formalParameters;
+            bool empty = _dictionary == null && !_set;
+            if (empty && parameters.Length == 1 && parameters[0].Length != BindingNameArguments.Length)
+            {
+                var jsValue = arguments.Length == 0 ? Undefined : arguments[0];
+                var binding = new Binding(jsValue, false, true);
+                _set = true;
+                _key = parameters[0];
+                _value = binding;
+            }
+            else
+            {
+                AddMultipleParameters(arguments, parameters);
+            }
+
+            if (ReferenceEquals(_argumentsBinding.Value, null))
+            {
+                _argumentsBinding = new Binding(argumentsInstance, canBeDeleted: false, mutable: true);
+            }
         }
         }
 
 
-        internal void ReleaseArguments()
+        private void AddMultipleParameters(JsValue[] arguments, string[] parameters)
+        {
+            bool empty = _dictionary == null && !_set;
+            for (var i = 0; i < parameters.Length; i++)
+            {
+                var argName = parameters[i];
+                var jsValue = i + 1 > arguments.Length ? Undefined : arguments[i];
+
+                if (empty || !TryGetValue(argName, out var existing))
+                {
+                    var binding = new Binding(jsValue, false, true);
+                    if (argName.Length == 9 && argName == BindingNameArguments)
+                    {
+                        _argumentsBinding = binding;
+                    }
+                    else
+                    {
+                        SetItem(argName, binding);
+                    }
+                }
+                else
+                {
+                    if (existing.Mutable)
+                    {
+                        ref var b = ref GetExistingItem(argName);
+                        b.Value = jsValue;
+                    }
+                    else
+                    {
+                        ExceptionHelper.ThrowTypeError(_engine, "Can't update the value of an immutable binding.");
+                    }
+                }
+            }
+        }
+
+        internal void AddVariableDeclarations(List<VariableDeclaration> variableDeclarations)
         {
         {
-            if (_argumentsBinding?.Value is ArgumentsInstance instance)
+            var variableDeclarationsCount = variableDeclarations.Count;
+            for (var i = 0; i < variableDeclarationsCount; i++)
             {
             {
-                _engine.ArgumentsInstancePool.Return(instance);
-                _argumentsBinding = null;
+                var variableDeclaration = variableDeclarations[i];
+                var declarationsCount = variableDeclaration.Declarations.Count;
+                for (var j = 0; j < declarationsCount; j++)
+                {
+                    var d = variableDeclaration.Declarations[j];
+                    var dn = ((Identifier) d.Id).Name;
+
+                    if (!ContainsKey(dn))
+                    {
+                        var binding = new Binding(Undefined, canBeDeleted: false, mutable: true);
+                        SetItem(dn, binding);
+                    }
+                }
             }
             }
         }
         }
     }
     }
-}
+}

+ 19 - 3
Jint/Runtime/Environments/EnvironmentRecord.cs

@@ -1,5 +1,4 @@
 using Jint.Native;
 using Jint.Native;
-using Jint.Native.Object;
 
 
 namespace Jint.Runtime.Environments
 namespace Jint.Runtime.Environments
 {
 {
@@ -7,10 +6,13 @@ namespace Jint.Runtime.Environments
     /// Base implementation of an Environment Record
     /// Base implementation of an Environment Record
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1
     /// </summary>
     /// </summary>
-    public abstract class EnvironmentRecord : ObjectInstance
+    public abstract class EnvironmentRecord : JsValue
     {
     {
-        protected EnvironmentRecord(Engine engine) : base(engine)
+        protected readonly Engine _engine;
+
+        protected EnvironmentRecord(Engine engine) : base(Types.Object)
         {
         {
+            _engine = engine;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -62,5 +64,19 @@ namespace Jint.Runtime.Environments
         /// </summary>
         /// </summary>
         /// <returns>The array of all defined bindings</returns>
         /// <returns>The array of all defined bindings</returns>
         public abstract string[] GetAllBindingNames();
         public abstract string[] GetAllBindingNames();
+        
+        public override object ToObject()
+        {
+            ExceptionHelper.ThrowNotSupportedException();
+            return null;
+        }
+
+        public override bool Equals(JsValue other)
+        {
+            ExceptionHelper.ThrowNotSupportedException();
+            return false;
+        }
+
     }
     }
 }
 }
+

+ 5 - 5
Jint/Runtime/Environments/LexicalEnvironment.cs

@@ -13,7 +13,7 @@ namespace Jint.Runtime.Environments
     public sealed class LexicalEnvironment
     public sealed class LexicalEnvironment
     {
     {
         private readonly Engine _engine;
         private readonly Engine _engine;
-        private readonly EnvironmentRecord _record;
+        internal readonly EnvironmentRecord _record;
         private readonly LexicalEnvironment _outer;
         private readonly LexicalEnvironment _outer;
 
 
         public LexicalEnvironment(Engine engine, EnvironmentRecord record, LexicalEnvironment outer)
         public LexicalEnvironment(Engine engine, EnvironmentRecord record, LexicalEnvironment outer)
@@ -37,7 +37,7 @@ namespace Jint.Runtime.Environments
             // optimize for common case where result is in one of the nearest scopes
             // optimize for common case where result is in one of the nearest scopes
             if (lex._record.HasBinding(name))
             if (lex._record.HasBinding(name))
             {
             {
-                return lex._engine.ReferencePool.Rent(lex._record, name, strict);
+                return lex._engine._referencePool.Rent(lex._record, name, strict);
             }
             }
 
 
             if (lex._outer == null)
             if (lex._outer == null)
@@ -52,14 +52,14 @@ namespace Jint.Runtime.Environments
         {
         {
             while (true)
             while (true)
             {
             {
-                if (lex.Record.HasBinding(name))
+                if (lex._record.HasBinding(name))
                 {
                 {
-                    return lex._engine.ReferencePool.Rent(lex._record, name, strict);
+                    return lex._engine._referencePool.Rent(lex._record, name, strict);
                 }
                 }
 
 
                 if (lex._outer == null)
                 if (lex._outer == null)
                 {
                 {
-                    return lex._engine.ReferencePool.Rent(Undefined.Instance, name, strict);
+                    return lex._engine._referencePool.Rent(Undefined.Instance, name, strict);
                 }
                 }
 
 
                 lex = lex._outer;
                 lex = lex._outer;

+ 2 - 2
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -48,10 +48,10 @@ namespace Jint.Runtime.Environments
             var desc = _bindingObject.GetProperty(name);
             var desc = _bindingObject.GetProperty(name);
             if (strict && desc == PropertyDescriptor.Undefined)
             if (strict && desc == PropertyDescriptor.Undefined)
             {
             {
-                throw new JavaScriptException(_engine.ReferenceError);
+                ExceptionHelper.ThrowReferenceError(_engine);
             }
             }
 
 
-            return UnwrapJsValue(desc);
+            return ObjectInstance.UnwrapJsValue(desc, this);
         }
         }
 
 
         public override bool DeleteBinding(string name)
         public override bool DeleteBinding(string name)

+ 120 - 0
Jint/Runtime/ExceptionHelper.cs

@@ -0,0 +1,120 @@
+using System;
+using Jint.Runtime.CallStack;
+
+namespace Jint.Runtime
+{
+    internal static class ExceptionHelper
+    {
+        public static void ThrowSyntaxError(Engine engine, string message = null)
+        {
+            throw new JavaScriptException(engine.SyntaxError, message);
+        }
+
+        public static T ThrowArgumentException<T>(string message = null)
+        {
+            ThrowArgumentException(message);
+            return default;
+        }
+
+        public static void ThrowArgumentException(string message = null)
+        {
+            ThrowArgumentException(message, null);
+        }
+
+        public static void ThrowArgumentException(string message, string paramName)
+        {
+            throw new ArgumentException(message, paramName);
+        }
+
+        public static void ThrowReferenceError(Engine engine, string message = null)
+        {
+            throw new JavaScriptException(engine.ReferenceError, message);
+        }
+
+        public static T ThrowTypeError<T>(Engine engine, string message = null, Exception exception = null)
+        {
+            ThrowTypeError(engine, message, exception);
+            return default;
+        }
+
+        public static void ThrowTypeError(Engine engine, string message = null, Exception exception = null)
+        {
+            throw new JavaScriptException(engine.TypeError, message, exception);
+        }
+
+        public static void ThrowRangeError(Engine engine, string message = null)
+        {
+            throw new JavaScriptException(engine.RangeError, message);
+        }
+
+        public static void ThrowUriError(Engine engine)
+        {
+            throw new JavaScriptException(engine.UriError);
+        }
+
+        public static void ThrowNotImplementedException()
+        {
+            throw new NotImplementedException();
+        }
+
+        public static void ThrowArgumentOutOfRangeException(string paramName, string message)
+        {
+            throw new ArgumentOutOfRangeException(paramName, message);
+        }
+
+        public static void ThrowTimeoutException()
+        {
+            throw new TimeoutException();
+        }
+
+        public static void ThrowStatementsCountOverflowException()
+        {
+            throw new StatementsCountOverflowException();
+        }
+
+        public static void ThrowArgumentOutOfRangeException()
+        {
+            throw new ArgumentOutOfRangeException();
+        }
+
+        public static void ThrowNotSupportedException(string message = null)
+        {
+            throw new NotSupportedException(message);
+        }
+
+        public static void ThrowInvalidOperationException(string message = null)
+        {
+            throw new InvalidOperationException(message);
+        }
+
+        public static void ThrowJavaScriptException(string message)
+        {
+            throw new JavaScriptException("TypeError");
+        }
+
+        public static void ThrowRecursionDepthOverflowException(JintCallStack currentStack, string currentExpressionReference)
+        {
+            throw new RecursionDepthOverflowException(currentStack, currentExpressionReference);
+        }
+
+        public static void ThrowArgumentNullException(string paramName)
+        {
+            throw new ArgumentNullException(paramName);
+        }
+
+        public static void ThrowError(Engine engine, string message)
+        {
+            throw new JavaScriptException(engine.Error, message);
+        }
+
+        public static void ThrowPlatformNotSupportedException(string message)
+        {
+            throw new PlatformNotSupportedException(message);
+        }
+
+        public static void ThrowMemoryLimitExceededException(string message)
+        {
+            throw new MemoryLimitExceededException(message);
+        }
+    }
+}

+ 184 - 266
Jint/Runtime/ExpressionIntepreter.cs

@@ -13,7 +13,7 @@ using Jint.Runtime.References;
 
 
 namespace Jint.Runtime
 namespace Jint.Runtime
 {
 {
-    public class ExpressionInterpreter
+    public sealed class ExpressionInterpreter
     {
     {
         private readonly Engine _engine;
         private readonly Engine _engine;
         private readonly bool _isDebugMode;
         private readonly bool _isDebugMode;
@@ -52,30 +52,24 @@ namespace Jint.Runtime
 
 
         public JsValue EvaluateAssignmentExpression(AssignmentExpression assignmentExpression)
         public JsValue EvaluateAssignmentExpression(AssignmentExpression assignmentExpression)
         {
         {
-            var lref = EvaluateExpression((Expression) assignmentExpression.Left) as Reference;
-            JsValue rval = _engine.GetValue(EvaluateExpression(assignmentExpression.Right), true);
+            var lref = _engine.EvaluateExpression((Expression) assignmentExpression.Left) as Reference;
+            JsValue rval = _engine.GetValue(_engine.EvaluateExpression(assignmentExpression.Right), true);
 
 
             if (lref == null)
             if (lref == null)
             {
             {
-                throw new JavaScriptException(_engine.ReferenceError);
+                ExceptionHelper.ThrowReferenceError(_engine);
             }
             }
 
 
             if (assignmentExpression.Operator == AssignmentOperator.Assign) // "="
             if (assignmentExpression.Operator == AssignmentOperator.Assign) // "="
             {
             {
-
-                if(lref.IsStrict()
-                   && lref.GetBase() is EnvironmentRecord
-                   && (lref.GetReferencedName() == "eval" || lref.GetReferencedName() == "arguments"))
-                {
-                    throw new JavaScriptException(_engine.SyntaxError);
-                }
+                lref.AssertValid(_engine);
 
 
                 _engine.PutValue(lref, rval);
                 _engine.PutValue(lref, rval);
-                _engine.ReferencePool.Return(lref);
+                _engine._referencePool.Return(lref);
                 return rval;
                 return rval;
             }
             }
 
 
-            JsValue lval = _engine.GetValue(lref);
+            JsValue lval = _engine.GetValue(lref, false);
 
 
             switch (assignmentExpression.Operator)
             switch (assignmentExpression.Operator)
             {
             {
@@ -151,13 +145,13 @@ namespace Jint.Runtime
                     break;
                     break;
 
 
                 default:
                 default:
-                    throw new NotImplementedException();
-
+                    ExceptionHelper.ThrowNotImplementedException();
+                    return null;
             }
             }
 
 
             _engine.PutValue(lref, lval);
             _engine.PutValue(lref, lval);
 
 
-            _engine.ReferencePool.Return(lref);
+            _engine._referencePool.Return(lref);
             return lval;
             return lval;
         }
         }
 
 
@@ -182,7 +176,7 @@ namespace Jint.Runtime
                     return double.NaN;
                     return double.NaN;
                 }
                 }
 
 
-                if (double.IsInfinity(lN) && rN.Equals(0))
+                if (double.IsInfinity(lN) && rN == 0)
                 {
                 {
                     if (NumberInstance.IsNegativeZero(rN))
                     if (NumberInstance.IsNegativeZero(rN))
                     {
                     {
@@ -192,12 +186,12 @@ namespace Jint.Runtime
                     return lN;
                     return lN;
                 }
                 }
 
 
-                if (lN.Equals(0) && rN.Equals(0))
+                if (lN == 0 && rN == 0)
                 {
                 {
                     return double.NaN;
                     return double.NaN;
                 }
                 }
 
 
-                if (rN.Equals(0))
+                if (rN == 0)
                 {
                 {
                     if (NumberInstance.IsNegativeZero(rN))
                     if (NumberInstance.IsNegativeZero(rN))
                     {
                     {
@@ -220,7 +214,7 @@ namespace Jint.Runtime
             }
             }
             else
             else
             {
             {
-                left = _engine.GetValue(EvaluateExpression(expression.Left), true);
+                left = _engine.GetValue(_engine.EvaluateExpression(expression.Left), true);
             }
             }
 
 
             JsValue right;
             JsValue right;
@@ -230,7 +224,7 @@ namespace Jint.Runtime
             }
             }
             else
             else
             {
             {
-                right = _engine.GetValue(EvaluateExpression(expression.Right), true);
+                right = _engine.GetValue(_engine.EvaluateExpression(expression.Right), true);
             }
             }
 
 
             JsValue value;
             JsValue value;
@@ -281,11 +275,11 @@ namespace Jint.Runtime
                     break;
                     break;
 
 
                 case BinaryOperator.Equal:
                 case BinaryOperator.Equal:
-                    value = Equal(left, right);
+                    value = Equal(left, right) ? JsBoolean.True : JsBoolean.False;
                     break;
                     break;
 
 
                 case BinaryOperator.NotEqual:
                 case BinaryOperator.NotEqual:
-                    value = !Equal(left, right);
+                    value = Equal(left, right) ? JsBoolean.False : JsBoolean.True;
                     break;
                     break;
 
 
                 case BinaryOperator.Greater:
                 case BinaryOperator.Greater:
@@ -329,10 +323,10 @@ namespace Jint.Runtime
                     break;
                     break;
 
 
                 case BinaryOperator.StrictlyEqual:
                 case BinaryOperator.StrictlyEqual:
-                    return StrictlyEqual(left, right);
+                    return StrictlyEqual(left, right) ? JsBoolean.True : JsBoolean.False;
 
 
                 case BinaryOperator.StricltyNotEqual:
                 case BinaryOperator.StricltyNotEqual:
-                    return !StrictlyEqual(left, right);
+                    return StrictlyEqual(left, right)? JsBoolean.False : JsBoolean.True;
 
 
                 case BinaryOperator.BitwiseAnd:
                 case BinaryOperator.BitwiseAnd:
                     return TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right);
                     return TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right);
@@ -356,7 +350,7 @@ namespace Jint.Runtime
                     var f = right.TryCast<FunctionInstance>();
                     var f = right.TryCast<FunctionInstance>();
                     if (ReferenceEquals(f, null))
                     if (ReferenceEquals(f, null))
                     {
                     {
-                        throw new JavaScriptException(_engine.TypeError, "instanceof can only be used with a function object");
+                        ExceptionHelper.ThrowTypeError(_engine, "instanceof can only be used with a function object");
                     }
                     }
                     value = f.HasInstance(left);
                     value = f.HasInstance(left);
                     break;
                     break;
@@ -364,14 +358,15 @@ namespace Jint.Runtime
                 case BinaryOperator.In:
                 case BinaryOperator.In:
                     if (!right.IsObject())
                     if (!right.IsObject())
                     {
                     {
-                        throw new JavaScriptException(_engine.TypeError, "in can only be used with an object");
+                        ExceptionHelper.ThrowTypeError(_engine, "in can only be used with an object");
                     }
                     }
 
 
                     value = right.AsObject().HasProperty(TypeConverter.ToString(left));
                     value = right.AsObject().HasProperty(TypeConverter.ToString(left));
                     break;
                     break;
 
 
                 default:
                 default:
-                    throw new NotImplementedException();
+                    ExceptionHelper.ThrowNotImplementedException();
+                    return null;
             }
             }
 
 
             return value;
             return value;
@@ -379,18 +374,17 @@ namespace Jint.Runtime
 
 
         public JsValue EvaluateLogicalExpression(BinaryExpression binaryExpression)
         public JsValue EvaluateLogicalExpression(BinaryExpression binaryExpression)
         {
         {
-            var left = _engine.GetValue(EvaluateExpression(binaryExpression.Left), true);
+            var left = _engine.GetValue(_engine.EvaluateExpression(binaryExpression.Left), true);
 
 
             switch (binaryExpression.Operator)
             switch (binaryExpression.Operator)
             {
             {
-
                 case BinaryOperator.LogicalAnd:
                 case BinaryOperator.LogicalAnd:
                     if (!TypeConverter.ToBoolean(left))
                     if (!TypeConverter.ToBoolean(left))
                     {
                     {
                         return left;
                         return left;
                     }
                     }
 
 
-                    return _engine.GetValue(EvaluateExpression(binaryExpression.Right), true);
+                    return _engine.GetValue(_engine.EvaluateExpression(binaryExpression.Right), true);
 
 
                 case BinaryOperator.LogicalOr:
                 case BinaryOperator.LogicalOr:
                     if (TypeConverter.ToBoolean(left))
                     if (TypeConverter.ToBoolean(left))
@@ -398,59 +392,57 @@ namespace Jint.Runtime
                         return left;
                         return left;
                     }
                     }
 
 
-                    return _engine.GetValue(EvaluateExpression(binaryExpression.Right), true);
+                    return _engine.GetValue(_engine.EvaluateExpression(binaryExpression.Right), true);
 
 
                 default:
                 default:
-                    throw new NotImplementedException();
+                    ExceptionHelper.ThrowNotImplementedException();
+                    return null;
             }
             }
         }
         }
 
 
         private static bool Equal(JsValue x, JsValue y)
         private static bool Equal(JsValue x, JsValue y)
         {
         {
-            var typex = x.Type;
-            var typey = y.Type;
-
-            if (typex == typey)
+            if (x._type == y._type)
             {
             {
 				return StrictlyEqual(x, y);
 				return StrictlyEqual(x, y);
             }
             }
 
 
-            if (x.IsNull() && y.IsUndefined())
+            if (x._type == Types.Null && y._type == Types.Undefined)
             {
             {
                 return true;
                 return true;
             }
             }
 
 
-            if (x.IsUndefined() && y.IsNull())
+            if (x._type == Types.Undefined && y._type == Types.Null)
             {
             {
                 return true;
                 return true;
             }
             }
 
 
-            if (typex == Types.Number && typey == Types.String)
+            if (x._type == Types.Number && y._type == Types.String)
             {
             {
                 return Equal(x, TypeConverter.ToNumber(y));
                 return Equal(x, TypeConverter.ToNumber(y));
             }
             }
 
 
-            if (typex == Types.String && typey == Types.Number)
+            if (x._type == Types.String && y._type == Types.Number)
             {
             {
                 return Equal(TypeConverter.ToNumber(x), y);
                 return Equal(TypeConverter.ToNumber(x), y);
             }
             }
 
 
-            if (typex == Types.Boolean)
+            if (x._type == Types.Boolean)
             {
             {
                 return Equal(TypeConverter.ToNumber(x), y);
                 return Equal(TypeConverter.ToNumber(x), y);
             }
             }
 
 
-            if (typey == Types.Boolean)
+            if (y._type == Types.Boolean)
             {
             {
                 return Equal(x, TypeConverter.ToNumber(y));
                 return Equal(x, TypeConverter.ToNumber(y));
             }
             }
 
 
-            if (typey == Types.Object && (typex == Types.String || typex == Types.Number))
+            if (y._type == Types.Object && (x._type == Types.String || x._type == Types.Number))
             {
             {
                 return Equal(x, TypeConverter.ToPrimitive(y));
                 return Equal(x, TypeConverter.ToPrimitive(y));
             }
             }
 
 
-            if (typex == Types.Object && (typey == Types.String || typey == Types.Number))
+            if (x._type == Types.Object && (y._type == Types.String || y._type == Types.Number))
             {
             {
                 return Equal(TypeConverter.ToPrimitive(x), y);
                 return Equal(TypeConverter.ToPrimitive(x), y);
             }
             }
@@ -460,62 +452,37 @@ namespace Jint.Runtime
 
 
         public static bool StrictlyEqual(JsValue x, JsValue y)
         public static bool StrictlyEqual(JsValue x, JsValue y)
         {
         {
-            var typea = x.Type;
-            var typeb = y.Type;
-
-            if (typea != typeb)
+            if (x._type != y._type)
             {
             {
                 return false;
                 return false;
             }
             }
 
 
-            if (typea == Types.Undefined || typea == Types.Null)
+            if (x._type == Types.Boolean || x._type == Types.String)
             {
             {
-                return true;
+                return x.Equals(y);
             }
             }
 
 
-            if (typea == Types.Number)
+                        
+            if (x._type >= Types.None && x._type <= Types.Null)
             {
             {
-                var nx = ((JsNumber) x)._value;
-                var ny = ((JsNumber) y)._value;
-
-                if (double.IsNaN(nx) || double.IsNaN(ny))
-                {
-                    return false;
-                }
-
-                if (nx.Equals(ny))
-                {
-                    return true;
-                }
-
-                return false;
-            }
-
-            if (typea == Types.String)
-            {
-                return x.AsStringWithoutTypeCheck() == y.AsStringWithoutTypeCheck();
+                return true;
             }
             }
 
 
-            if (typea == Types.Boolean)
+            if (x is JsNumber jsNumber)
             {
             {
-                return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
+                var nx = jsNumber._value;
+                var ny = ((JsNumber) y)._value;
+                return !double.IsNaN(nx) && !double.IsNaN(ny) && nx == ny;
             }
             }
 
 
-            if (typea == Types.Object)
+            if (x is IObjectWrapper xw)
             {
             {
-                if (x.AsObject() is IObjectWrapper xw)
+                if (!(y is IObjectWrapper yw))
                 {
                 {
-                    var yw = y.AsObject() as IObjectWrapper;
-                    if (yw == null)
-                        return false;
-
-                    return Equals(xw.Target, yw.Target);
+                    return false;
                 }
                 }
-            }
 
 
-            if (typea == Types.None)
-            {
-                return true;
+                return Equals(xw.Target, yw.Target);
             }
             }
 
 
             return x == y;
             return x == y;
@@ -531,12 +498,11 @@ namespace Jint.Runtime
                 return false;
                 return false;
             }
             }
 
 
-            if (typea == Types.None)
-            {
-                return true;
-            }
-            if (typea == Types.Number)
+            switch (typea)
             {
             {
+                case Types.None:
+                    return true;
+                case Types.Number:
                 var nx = TypeConverter.ToNumber(x);
                 var nx = TypeConverter.ToNumber(x);
                 var ny = TypeConverter.ToNumber(y);
                 var ny = TypeConverter.ToNumber(y);
 
 
@@ -545,9 +511,9 @@ namespace Jint.Runtime
                     return true;
                     return true;
                 }
                 }
 
 
-                if (nx.Equals(ny))
+                if (nx == ny)
                 {
                 {
-                    if (nx.Equals(0))
+                    if (nx == 0)
                     {
                     {
                         // +0 !== -0
                         // +0 !== -0
                         return NumberInstance.IsNegativeZero(nx) == NumberInstance.IsNegativeZero(ny);
                         return NumberInstance.IsNegativeZero(nx) == NumberInstance.IsNegativeZero(ny);
@@ -557,16 +523,14 @@ namespace Jint.Runtime
                 }
                 }
 
 
                 return false;
                 return false;
+                case Types.String:
+                    return TypeConverter.ToString(x) == TypeConverter.ToString(y);
+                case Types.Boolean:
+                    return TypeConverter.ToBoolean(x) == TypeConverter.ToBoolean(y);
+                default:
+                    return x == y;
             }
             }
-            if (typea == Types.String)
-            {
-                return TypeConverter.ToString(x) == TypeConverter.ToString(y);
-            }
-            if (typea == Types.Boolean)
-            {
-                return TypeConverter.ToBoolean(x) == TypeConverter.ToBoolean(y);
-            }
-            return x == y;
+
         }
         }
 
 
         public static JsValue Compare(JsValue x, JsValue y, bool leftFirst = true)
         public static JsValue Compare(JsValue x, JsValue y, bool leftFirst = true)
@@ -596,7 +560,7 @@ namespace Jint.Runtime
                     return Undefined.Instance;
                     return Undefined.Instance;
                 }
                 }
 
 
-                if (nx.Equals(ny))
+                if (nx == ny)
                 {
                 {
                     return false;
                     return false;
                 }
                 }
@@ -643,7 +607,7 @@ namespace Jint.Runtime
             {
             {
                 case TokenType.BooleanLiteral:
                 case TokenType.BooleanLiteral:
                     // bool is fast enough
                     // bool is fast enough
-                    return (JsValue) (literal.CachedValue = literal.CachedValue ?? (literal.NumericValue != 0.0 ? JsBoolean.True : JsBoolean.False));
+                    return literal.NumericValue > 0.0 ? JsBoolean.True : JsBoolean.False;
                 
                 
                 case TokenType.NullLiteral:
                 case TokenType.NullLiteral:
                     // and so is null
                     // and so is null
@@ -653,7 +617,7 @@ namespace Jint.Runtime
                     return (JsValue) (literal.CachedValue = literal.CachedValue ?? JsNumber.Create(literal.NumericValue));
                     return (JsValue) (literal.CachedValue = literal.CachedValue ?? JsNumber.Create(literal.NumericValue));
                 
                 
                 case TokenType.StringLiteral:
                 case TokenType.StringLiteral:
-                    return (JsValue) (literal.CachedValue = literal.CachedValue ?? (literal.CachedValue = JsString.Create((string) literal.Value)));
+                    return (JsValue) (literal.CachedValue = literal.CachedValue ?? JsString.Create((string) literal.Value));
                 
                 
                 case TokenType.RegularExpression:
                 case TokenType.RegularExpression:
                     // should not cache
                     // should not cache
@@ -668,107 +632,93 @@ namespace Jint.Runtime
         public JsValue EvaluateObjectExpression(ObjectExpression objectExpression)
         public JsValue EvaluateObjectExpression(ObjectExpression objectExpression)
         {
         {
             // http://www.ecma-international.org/ecma-262/5.1/#sec-11.1.5
             // http://www.ecma-international.org/ecma-262/5.1/#sec-11.1.5
-
-            var obj = _engine.Object.Construct(Arguments.Empty);
             var propertiesCount = objectExpression.Properties.Count;
             var propertiesCount = objectExpression.Properties.Count;
+            var obj = _engine.Object.Construct(propertiesCount);
             for (var i = 0; i < propertiesCount; i++)
             for (var i = 0; i < propertiesCount; i++)
             {
             {
                 var property = objectExpression.Properties[i];
                 var property = objectExpression.Properties[i];
                 var propName = property.Key.GetKey();
                 var propName = property.Key.GetKey();
-                var previous = obj.GetOwnProperty(propName);
+                if (!obj._properties.TryGetValue(propName, out var previous))
+                {
+                    previous = PropertyDescriptor.Undefined;
+                }
+                
                 PropertyDescriptor propDesc;
                 PropertyDescriptor propDesc;
 
 
-                const PropertyFlag enumerableConfigurable = PropertyFlag.Enumerable | PropertyFlag.Configurable;
-                
-                switch (property.Kind)
+                if (property.Kind == PropertyKind.Init || property.Kind == PropertyKind.Data)
                 {
                 {
-                    case PropertyKind.Init:
-                    case PropertyKind.Data:
-                        var exprValue = _engine.EvaluateExpression(property.Value);
-                        var propValue = _engine.GetValue(exprValue, true);
-                        propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
-                        break;
-
-                    case PropertyKind.Get:
-                        var getter = property.Value as IFunction;
-
-                        if (getter == null)
-                        {
-                            throw new JavaScriptException(_engine.SyntaxError);
-                        }
-
-                        ScriptFunctionInstance get;
-                        using (new StrictModeScope(getter.Strict))
-                        {
-                            get = new ScriptFunctionInstance(
-                                _engine,
-                                getter,
-                                _engine.ExecutionContext.LexicalEnvironment,
-                                StrictModeScope.IsStrictModeCode
-                            );
-                        }
-
-                        propDesc = new GetSetPropertyDescriptor(get: get, set: null, enumerableConfigurable);
-                        break;
-
-                    case PropertyKind.Set:
-                        var setter = property.Value as IFunction;
-
-                        if (setter == null)
-                        {
-                            throw new JavaScriptException(_engine.SyntaxError);
-                        }
+                    var exprValue = _engine.EvaluateExpression(property.Value);
+                    var propValue = _engine.GetValue(exprValue, true);
+                    propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
+                }
+                else if (property.Kind == PropertyKind.Get || property.Kind == PropertyKind.Set)
+                {
+                    var function = property.Value as IFunction;
 
 
-                        ScriptFunctionInstance set;
-                        using (new StrictModeScope(setter.Strict))
-                        {
-                            set = new ScriptFunctionInstance(
-                                _engine,
-                                setter,
-                                _engine.ExecutionContext.LexicalEnvironment,
-                                StrictModeScope.IsStrictModeCode
-                            );
-                        }
+                    if (function == null)
+                    {
+                        ExceptionHelper.ThrowSyntaxError(_engine);
+                    }
 
 
-                        propDesc = new GetSetPropertyDescriptor(get: null, set: set, enumerableConfigurable);
-                        break;
+                    ScriptFunctionInstance functionInstance;
+                    using (new StrictModeScope(function.Strict))
+                    {
+                        functionInstance = new ScriptFunctionInstance(
+                            _engine,
+                            function,
+                            _engine.ExecutionContext.LexicalEnvironment,
+                            StrictModeScope.IsStrictModeCode
+                        );
+                    }
 
 
-                    default:
-                        throw new ArgumentOutOfRangeException();
+                    propDesc = new GetSetPropertyDescriptor(
+                        get: property.Kind == PropertyKind.Get ? functionInstance : null,
+                        set: property.Kind == PropertyKind.Set ? functionInstance : null,
+                        PropertyFlag.Enumerable | PropertyFlag.Configurable);
+                }
+                else
+                {
+                    ExceptionHelper.ThrowArgumentOutOfRangeException();
+                    return null;
                 }
                 }
 
 
                 if (previous != PropertyDescriptor.Undefined)
                 if (previous != PropertyDescriptor.Undefined)
                 {
                 {
                     if (StrictModeScope.IsStrictModeCode && previous.IsDataDescriptor() && propDesc.IsDataDescriptor())
                     if (StrictModeScope.IsStrictModeCode && previous.IsDataDescriptor() && propDesc.IsDataDescriptor())
                     {
                     {
-                        throw new JavaScriptException(_engine.SyntaxError);
+                        ExceptionHelper.ThrowSyntaxError(_engine);
                     }
                     }
 
 
                     if (previous.IsDataDescriptor() && propDesc.IsAccessorDescriptor())
                     if (previous.IsDataDescriptor() && propDesc.IsAccessorDescriptor())
                     {
                     {
-                        throw new JavaScriptException(_engine.SyntaxError);
+                        ExceptionHelper.ThrowSyntaxError(_engine);
                     }
                     }
 
 
                     if (previous.IsAccessorDescriptor() && propDesc.IsDataDescriptor())
                     if (previous.IsAccessorDescriptor() && propDesc.IsDataDescriptor())
                     {
                     {
-                        throw new JavaScriptException(_engine.SyntaxError);
+                        ExceptionHelper.ThrowSyntaxError(_engine);
                     }
                     }
 
 
                     if (previous.IsAccessorDescriptor() && propDesc.IsAccessorDescriptor())
                     if (previous.IsAccessorDescriptor() && propDesc.IsAccessorDescriptor())
                     {
                     {
                         if (!ReferenceEquals(propDesc.Set, null) && !ReferenceEquals(previous.Set, null))
                         if (!ReferenceEquals(propDesc.Set, null) && !ReferenceEquals(previous.Set, null))
                         {
                         {
-                            throw new JavaScriptException(_engine.SyntaxError);
+                            ExceptionHelper.ThrowSyntaxError(_engine);
                         }
                         }
 
 
                         if (!ReferenceEquals(propDesc.Get, null) && !ReferenceEquals(previous.Get, null))
                         if (!ReferenceEquals(propDesc.Get, null) && !ReferenceEquals(previous.Get, null))
                         {
                         {
-                            throw new JavaScriptException(_engine.SyntaxError);
+                            ExceptionHelper.ThrowSyntaxError(_engine);
                         }
                         }
                     }
                     }
-                }
 
 
-                obj.DefineOwnProperty(propName, propDesc, false);
+                    obj.DefineOwnProperty(propName, propDesc, false);
+                }
+                else
+                {
+                    // do faster direct set
+                    obj._properties[propName] = propDesc;
+                }
             }
             }
 
 
             return obj;
             return obj;
@@ -781,8 +731,8 @@ namespace Jint.Runtime
         /// <returns></returns>
         /// <returns></returns>
         public Reference EvaluateMemberExpression(MemberExpression memberExpression)
         public Reference EvaluateMemberExpression(MemberExpression memberExpression)
         {
         {
-            var baseReference = EvaluateExpression(memberExpression.Object);
-            var baseValue = _engine.GetValue(baseReference);
+            var baseReference = _engine.EvaluateExpression(memberExpression.Object);
+            var baseValue = _engine.GetValue(baseReference, false);
 
 
             string propertyNameString;
             string propertyNameString;
             if (!memberExpression.Computed) // index accessor ?
             if (!memberExpression.Computed) // index accessor ?
@@ -792,7 +742,7 @@ namespace Jint.Runtime
             }
             }
             else
             else
             {
             {
-                var propertyNameReference = 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.ToString(propertyNameValue);
             }
             }
@@ -801,15 +751,15 @@ namespace Jint.Runtime
 
 
             if (baseReference is Reference r)
             if (baseReference is Reference r)
             {
             {
-                _engine.ReferencePool.Return(r);
+                _engine._referencePool.Return(r);
             }
             }
-            return _engine.ReferencePool.Rent(baseValue, propertyNameString, StrictModeScope.IsStrictModeCode);
+            return _engine._referencePool.Rent(baseValue, propertyNameString, StrictModeScope.IsStrictModeCode);
         }
         }
 
 
         public JsValue EvaluateFunctionExpression(IFunction functionExpression)
         public JsValue EvaluateFunctionExpression(IFunction functionExpression)
         {
         {
             var funcEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
             var funcEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
-            var envRec = (DeclarativeEnvironmentRecord)funcEnv.Record;
+            var envRec = (DeclarativeEnvironmentRecord) funcEnv._record;
 
 
             var closure = new ScriptFunctionInstance(
             var closure = new ScriptFunctionInstance(
                 _engine,
                 _engine,
@@ -828,14 +778,13 @@ namespace Jint.Runtime
 
 
         public JsValue EvaluateCallExpression(CallExpression callExpression)
         public JsValue EvaluateCallExpression(CallExpression callExpression)
         {
         {
-            var callee = EvaluateExpression(callExpression.Callee);
+            var callee = _engine.EvaluateExpression(callExpression.Callee);
             
             
             if (_isDebugMode)
             if (_isDebugMode)
             {
             {
                 _engine.DebugHandler.AddToDebugCallStack(callExpression);
                 _engine.DebugHandler.AddToDebugCallStack(callExpression);
             }
             }
 
 
-            JsValue thisObject;
             // todo: implement as in http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.4
             // todo: implement as in http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.4
 
 
             var arguments = Array.Empty<JsValue>();
             var arguments = Array.Empty<JsValue>();
@@ -848,7 +797,7 @@ namespace Jint.Runtime
                 var allLiteral = true;
                 var allLiteral = true;
                 if (callExpression.Arguments.Count > 0)
                 if (callExpression.Arguments.Count > 0)
                 {
                 {
-                    arguments = _engine.JsValueArrayPool.RentArray(callExpression.Arguments.Count);
+                    arguments = _engine._jsValueArrayPool.RentArray(callExpression.Arguments.Count);
                     BuildArguments(callExpression.Arguments, arguments, out allLiteral);
                     BuildArguments(callExpression.Arguments, arguments, out allLiteral);
                 }
                 }
 
 
@@ -867,66 +816,62 @@ namespace Jint.Runtime
                 }
                 }
             }
             }
 
 
-            var func = _engine.GetValue(callee);
+            var func = _engine.GetValue(callee, false);
 
 
             var r = callee as Reference;
             var r = callee as Reference;
-
             if (_maxRecursionDepth >= 0)
             if (_maxRecursionDepth >= 0)
             {
             {
-                var stackItem = new CallStackElement(callExpression, func, r != null ? r.GetReferencedName() : "anonymous function");
+                var stackItem = new CallStackElement(callExpression, func, r?._name ?? "anonymous function");
 
 
                 var recursionDepth = _engine.CallStack.Push(stackItem);
                 var recursionDepth = _engine.CallStack.Push(stackItem);
 
 
                 if (recursionDepth > _maxRecursionDepth)
                 if (recursionDepth > _maxRecursionDepth)
                 {
                 {
                     _engine.CallStack.Pop();
                     _engine.CallStack.Pop();
-                    throw new RecursionDepthOverflowException(_engine.CallStack, stackItem.ToString());
+                    ExceptionHelper.ThrowRecursionDepthOverflowException(_engine.CallStack, stackItem.ToString());
                 }
                 }
             }
             }
 
 
-            if (func.IsUndefined())
+            if (func._type == Types.Undefined)
             {
             {
-                throw new JavaScriptException(_engine.TypeError, r == null ? "" : string.Format("Object has no method '{0}'", r.GetReferencedName()));
+                ExceptionHelper.ThrowTypeError(_engine, r == null ? "" : $"Object has no method '{r.GetReferencedName()}'");
             }
             }
 
 
-            if (!func.IsObject())
+            if (func._type != Types.Object)
             {
             {
                 if (_referenceResolver == null || !_referenceResolver.TryGetCallable(_engine, callee, out func))
                 if (_referenceResolver == null || !_referenceResolver.TryGetCallable(_engine, callee, out func))
                 {
                 {
-                    throw new JavaScriptException(_engine.TypeError,
+                    ExceptionHelper.ThrowTypeError(_engine,
                         r == null ? "" : $"Property '{r.GetReferencedName()}' of object is not a function");
                         r == null ? "" : $"Property '{r.GetReferencedName()}' of object is not a function");
                 }
                 }
             }
             }
 
 
-            var callable = func.TryCast<ICallable>();
+            var callable = func as ICallable;
             if (callable == null)
             if (callable == null)
             {
             {
-                throw new JavaScriptException(_engine.TypeError);
+                ExceptionHelper.ThrowTypeError(_engine);
             }
             }
 
 
+            var thisObject = Undefined.Instance;
             if (r != null)
             if (r != null)
             {
             {
                 if (r.IsPropertyReference())
                 if (r.IsPropertyReference())
                 {
                 {
-                    thisObject = r.GetBase();
+                    thisObject = r._baseValue;
                 }
                 }
                 else
                 else
                 {
                 {
-                    var env = r.GetBase().TryCast<EnvironmentRecord>();
+                    var env = (EnvironmentRecord) r._baseValue;
                     thisObject = env.ImplicitThisValue();
                     thisObject = env.ImplicitThisValue();
                 }
                 }
-            }
-            else
-            {
-                thisObject = Undefined.Instance;
-            }
-
-            // is it a direct call to eval ? http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.1.1
-            if (r != null && r.GetReferencedName() == "eval" && callable is EvalFunctionInstance)
-            {
-                var value = ((EvalFunctionInstance) callable).Call(thisObject, arguments, true);
-                _engine.ReferencePool.Return(r);
-                return value;
+                
+                // is it a direct call to eval ? http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.1.1
+                if (r._name == "eval" && callable is EvalFunctionInstance instance)
+                {
+                    var value = instance.Call(thisObject, arguments, true);
+                    _engine._referencePool.Return(r);
+                    return value;
+                }
             }
             }
 
 
             var result = callable.Call(thisObject, arguments);
             var result = callable.Call(thisObject, arguments);
@@ -943,10 +888,10 @@ namespace Jint.Runtime
 
 
             if (!callExpression.Cached && arguments.Length > 0)
             if (!callExpression.Cached && arguments.Length > 0)
             {
             {
-                _engine.JsValueArrayPool.ReturnArray(arguments);
+                _engine._jsValueArrayPool.ReturnArray(arguments);
             }
             }
 
 
-            _engine.ReferencePool.Return(r);
+            _engine._referencePool.Return(r);
             return result;
             return result;
         }
         }
 
 
@@ -966,48 +911,28 @@ namespace Jint.Runtime
         public JsValue EvaluateUpdateExpression(UpdateExpression updateExpression)
         public JsValue EvaluateUpdateExpression(UpdateExpression updateExpression)
         {
         {
             var value = _engine.EvaluateExpression(updateExpression.Argument);
             var value = _engine.EvaluateExpression(updateExpression.Argument);
-            Reference r;
-
-            switch (updateExpression.Operator)
-            {
-                case UnaryOperator.Increment:
-                    r = value as Reference;
-                    if (r != null
-                        && r.IsStrict()
-                        && r.GetBase() is EnvironmentRecord
-                        && ("eval" == r.GetReferencedName() || "arguments" == r.GetReferencedName()))
-                    {
-                        throw new JavaScriptException(_engine.SyntaxError);
-                    }
-
-                    var oldValue = TypeConverter.ToNumber(_engine.GetValue(value));
-                    var newValue = oldValue + 1;
-                    _engine.PutValue(r, newValue);
-
-                    _engine.ReferencePool.Return(r);
-                    return updateExpression.Prefix ? newValue : oldValue;
-
-                case UnaryOperator.Decrement:
-                    r = value as Reference;
-                    if (r != null
-                        && r.IsStrict()
-                        && r.GetBase() is EnvironmentRecord
-                        && ("eval" == r.GetReferencedName() || "arguments" == r.GetReferencedName()))
-                    {
-                        throw new JavaScriptException(_engine.SyntaxError);
-                    }
 
 
-                    oldValue = TypeConverter.ToNumber(_engine.GetValue(value));
-                    newValue = oldValue - 1;
+            var r = (Reference) value;
+            r.AssertValid(_engine);
 
 
-                    _engine.PutValue(r, newValue);
-                    _engine.ReferencePool.Return(r);
-
-                    return updateExpression.Prefix ? newValue : oldValue;
-                default:
-                    throw new ArgumentException();
+            var oldValue = TypeConverter.ToNumber(_engine.GetValue(value, false));
+            double newValue = 0;
+            if (updateExpression.Operator == UnaryOperator.Increment)
+            {
+                newValue = oldValue + 1;
+            }
+            else if (updateExpression.Operator == UnaryOperator.Decrement)
+            {
+                newValue = oldValue - 1;
+            }
+            else
+            {
+                ExceptionHelper.ThrowArgumentException();
             }
             }
 
 
+            _engine.PutValue(r, newValue);
+            _engine._referencePool.Return(r);
+            return updateExpression.Prefix ? newValue : oldValue;
         }
         }
 
 
         public JsValue EvaluateThisExpression(ThisExpression thisExpression)
         public JsValue EvaluateThisExpression(ThisExpression thisExpression)
@@ -1017,21 +942,21 @@ namespace Jint.Runtime
 
 
         public JsValue EvaluateNewExpression(NewExpression newExpression)
         public JsValue EvaluateNewExpression(NewExpression newExpression)
         {
         {
-            var arguments = _engine.JsValueArrayPool.RentArray(newExpression.Arguments.Count);
+            var arguments = _engine._jsValueArrayPool.RentArray(newExpression.Arguments.Count);
             BuildArguments(newExpression.Arguments, arguments, out _);
             BuildArguments(newExpression.Arguments, arguments, out _);
 
 
             // todo: optimize by defining a common abstract class or interface
             // todo: optimize by defining a common abstract class or interface
-            var callee = _engine.GetValue(EvaluateExpression(newExpression.Callee), true).TryCast<IConstructor>();
+            var callee = _engine.GetValue(_engine.EvaluateExpression(newExpression.Callee), true).TryCast<IConstructor>();
 
 
             if (callee == null)
             if (callee == null)
             {
             {
-                throw new JavaScriptException(_engine.TypeError, "The object can't be used as constructor.");
+                ExceptionHelper.ThrowTypeError(_engine, "The object can't be used as constructor.");
             }
             }
 
 
             // construct the new instance using the Function's constructor method
             // construct the new instance using the Function's constructor method
             var instance = callee.Construct(arguments);
             var instance = callee.Construct(arguments);
 
 
-            _engine.JsValueArrayPool.ReturnArray(arguments);
+            _engine._jsValueArrayPool.ReturnArray(arguments);
 
 
             return instance;
             return instance;
         }
         }
@@ -1041,22 +966,16 @@ namespace Jint.Runtime
             var elements = arrayExpression.Elements;
             var elements = arrayExpression.Elements;
             var count = elements.Count;
             var count = elements.Count;
             
             
-            var jsValues = _engine.JsValueArrayPool.RentArray(1);
-            jsValues[0] = count;
-            
-            var a = _engine.Array.Construct(jsValues, (uint) count);
+            var a = _engine.Array.ConstructFast((uint) count);
             for (var n = 0; n < count; n++)
             for (var n = 0; n < count; n++)
             {
             {
                 var expr = elements[n];
                 var expr = elements[n];
                 if (expr != null)
                 if (expr != null)
                 {
                 {
-                    var value = _engine.GetValue(EvaluateExpression((Expression) expr), true);
+                    var value = _engine.GetValue(_engine.EvaluateExpression((Expression) expr), true);
                     a.SetIndexValue((uint) n, value, updateLength: false);
                     a.SetIndexValue((uint) n, value, updateLength: false);
                 }
                 }
             }
             }
-            a.SetLength((uint) count);
-            _engine.JsValueArrayPool.ReturnArray(jsValues);
-
             return a;
             return a;
         }
         }
 
 
@@ -1087,34 +1006,34 @@ namespace Jint.Runtime
                     }
                     }
                     if (r.IsUnresolvableReference())
                     if (r.IsUnresolvableReference())
                     {
                     {
-                        if (r.IsStrict())
+                        if (r._strict)
                         {
                         {
-                            throw new JavaScriptException(_engine.SyntaxError);
+                            ExceptionHelper.ThrowSyntaxError(_engine);
                         }
                         }
 
 
-                        _engine.ReferencePool.Return(r);
+                        _engine._referencePool.Return(r);
                         return true;
                         return true;
                     }
                     }
                     if (r.IsPropertyReference())
                     if (r.IsPropertyReference())
                     {
                     {
                         var o = TypeConverter.ToObject(_engine, r.GetBase());
                         var o = TypeConverter.ToObject(_engine, r.GetBase());
-                        var jsValue = o.Delete(r.GetReferencedName(), r.IsStrict());
-                        _engine.ReferencePool.Return(r);
+                        var jsValue = o.Delete(r._name, r._strict);
+                        _engine._referencePool.Return(r);
                         return jsValue;
                         return jsValue;
                     }
                     }
-                    if (r.IsStrict())
+                    if (r._strict)
                     {
                     {
-                        throw new JavaScriptException(_engine.SyntaxError);
+                        ExceptionHelper.ThrowSyntaxError(_engine);
                     }
                     }
 
 
                     var bindings = r.GetBase().TryCast<EnvironmentRecord>();
                     var bindings = r.GetBase().TryCast<EnvironmentRecord>();
                     var referencedName = r.GetReferencedName();
                     var referencedName = r.GetReferencedName();
-                    _engine.ReferencePool.Return(r);
+                    _engine._referencePool.Return(r);
 
 
                     return bindings.DeleteBinding(referencedName);
                     return bindings.DeleteBinding(referencedName);
 
 
                 case UnaryOperator.Void:
                 case UnaryOperator.Void:
-                    _engine.GetValue(value);
+                    _engine.GetValue(value, true);
                     return Undefined.Instance;
                     return Undefined.Instance;
 
 
                 case UnaryOperator.TypeOf:
                 case UnaryOperator.TypeOf:
@@ -1123,7 +1042,7 @@ namespace Jint.Runtime
                     {
                     {
                         if (r.IsUnresolvableReference())
                         if (r.IsUnresolvableReference())
                         {
                         {
-                            _engine.ReferencePool.Return(r);
+                            _engine._referencePool.Return(r);
                             return "undefined";
                             return "undefined";
                         }
                         }
                     }
                     }
@@ -1151,7 +1070,8 @@ namespace Jint.Runtime
                     return "object";
                     return "object";
 
 
                 default:
                 default:
-                    throw new ArgumentException();
+                    ExceptionHelper.ThrowArgumentException();
+                    return null;
             }
             }
         }
         }
 
 
@@ -1161,12 +1081,10 @@ namespace Jint.Runtime
             out bool cacheable)
             out bool cacheable)
         {
         {
             cacheable = true;
             cacheable = true;
-            var count = expressionArguments.Count;
-
-            for (var i = 0; i < count; i++)
+            for (var i = 0; i < (uint) targetArray.Length; i++)
             {
             {
                 var argument = (Expression) expressionArguments[i];
                 var argument = (Expression) expressionArguments[i];
-                targetArray[i] = _engine.GetValue(EvaluateExpression(argument), true);
+                targetArray[i] = _engine.GetValue(_engine.EvaluateExpression(argument), true);
                 cacheable &= argument is Literal;
                 cacheable &= argument is Literal;
             }
             }
         }
         }

+ 3 - 2
Jint/Runtime/Interop/ClrFunctionInstance.cs

@@ -17,8 +17,8 @@ namespace Jint.Runtime.Interop
         {
         {
             _func = func;
             _func = func;
             Prototype = engine.Function.PrototypeObject;
             Prototype = engine.Function.PrototypeObject;
-            SetOwnProperty("length", new PropertyDescriptor(length, PropertyFlag.AllForbidden));
             Extensible = true;
             Extensible = true;
+            _length = new PropertyDescriptor(length, PropertyFlag.AllForbidden);
         }
         }
 
 
         public ClrFunctionInstance(Engine engine, Func<JsValue, JsValue[], JsValue> func)
         public ClrFunctionInstance(Engine engine, Func<JsValue, JsValue[], JsValue> func)
@@ -35,7 +35,8 @@ namespace Jint.Runtime.Interop
             }
             }
             catch (InvalidCastException)
             catch (InvalidCastException)
             {
             {
-                throw new JavaScriptException(Engine.TypeError);
+                ExceptionHelper.ThrowTypeError(Engine);
+                return null;
             }
             }
         }
         }
     }
     }

+ 14 - 13
Jint/Runtime/Interop/DefaultTypeConverter.cs

@@ -31,7 +31,7 @@ namespace Jint.Runtime.Interop
                     return null;
                     return null;
                 }
                 }
 
 
-                throw new NotSupportedException(string.Format("Unable to convert null to '{0}'", type.FullName));
+                ExceptionHelper.ThrowNotSupportedException($"Unable to convert null to '{type.FullName}'");
             }
             }
 
 
             // don't try to convert if value is derived from type
             // don't try to convert if value is derived from type
@@ -40,12 +40,12 @@ namespace Jint.Runtime.Interop
                 return value;
                 return value;
             }
             }
 
 
-            if (type.IsEnum())
+            if (type.IsEnum)
             {
             {
                 var integer = System.Convert.ChangeType(value, typeof(int), formatProvider);
                 var integer = System.Convert.ChangeType(value, typeof(int), formatProvider);
                 if (integer == null)
                 if (integer == null)
                 {
                 {
-                    throw new ArgumentOutOfRangeException();
+                    ExceptionHelper.ThrowArgumentOutOfRangeException();
                 }
                 }
 
 
                 return Enum.ToObject(type, integer);
                 return Enum.ToObject(type, integer);
@@ -57,7 +57,7 @@ namespace Jint.Runtime.Interop
             {
             {
                 var function = (Func<JsValue, JsValue[], JsValue>)value;
                 var function = (Func<JsValue, JsValue[], JsValue>)value;
 
 
-                if (type.IsGenericType())
+                if (type.IsGenericType)
                 {
                 {
                     var genericType = type.GetGenericTypeDefinition();
                     var genericType = type.GetGenericTypeDefinition();
 
 
@@ -75,7 +75,7 @@ namespace Jint.Runtime.Interop
                         for (var i = 0; i < @params.Length; i++)
                         for (var i = 0; i < @params.Length; i++)
                         {
                         {
                             var param = @params[i];
                             var param = @params[i];
-                            if (param.Type.IsValueType())
+                            if (param.Type.IsValueType)
                             {
                             {
                                 var boxing = Expression.Convert(param, typeof(object));
                                 var boxing = Expression.Convert(param, typeof(object));
                                 tmpVars[i] = Expression.Call(null, jsValueFromObject, Expression.Constant(_engine, typeof(Engine)), boxing);
                                 tmpVars[i] = Expression.Call(null, jsValueFromObject, Expression.Constant(_engine, typeof(Engine)), boxing);
@@ -89,7 +89,7 @@ namespace Jint.Runtime.Interop
 
 
                         var callExpresion = Expression.Block(Expression.Call(
                         var callExpresion = Expression.Block(Expression.Call(
                                                 Expression.Call(Expression.Constant(function.Target),
                                                 Expression.Call(Expression.Constant(function.Target),
-                                                    function.GetMethodInfo(),
+                                                    function.Method,
                                                     Expression.Constant(JsValue.Undefined, typeof(JsValue)),
                                                     Expression.Constant(JsValue.Undefined, typeof(JsValue)),
                                                     @vars),
                                                     @vars),
                                                 jsValueToObject), Expression.Empty());
                                                 jsValueToObject), Expression.Empty());
@@ -123,7 +123,7 @@ namespace Jint.Runtime.Interop
                                                     convertChangeType,
                                                     convertChangeType,
                                                     Expression.Call(
                                                     Expression.Call(
                                                             Expression.Call(Expression.Constant(function.Target),
                                                             Expression.Call(Expression.Constant(function.Target),
-                                                                    function.GetMethodInfo(),
+                                                                    function.Method,
                                                                     Expression.Constant(JsValue.Undefined, typeof(JsValue)),
                                                                     Expression.Constant(JsValue.Undefined, typeof(JsValue)),
                                                                     @vars),
                                                                     @vars),
                                                             jsValueToObject),
                                                             jsValueToObject),
@@ -163,7 +163,7 @@ namespace Jint.Runtime.Interop
                         var callExpression = Expression.Block(
                         var callExpression = Expression.Block(
                                                 Expression.Call(
                                                 Expression.Call(
                                                     Expression.Call(Expression.Constant(function.Target),
                                                     Expression.Call(Expression.Constant(function.Target),
-                                                        function.GetMethodInfo(),
+                                                        function.Method,
                                                         Expression.Constant(JsValue.Undefined, typeof(JsValue)),
                                                         Expression.Constant(JsValue.Undefined, typeof(JsValue)),
                                                         @vars),
                                                         @vars),
                                                     typeof(JsValue).GetMethod("ToObject")),
                                                     typeof(JsValue).GetMethod("ToObject")),
@@ -181,7 +181,9 @@ namespace Jint.Runtime.Interop
             {
             {
                 var source = value as object[];
                 var source = value as object[];
                 if (source == null)
                 if (source == null)
-                    throw new ArgumentException(String.Format("Value of object[] type is expected, but actual type is {0}.", value.GetType()));
+                {
+                    ExceptionHelper.ThrowArgumentException($"Value of object[] type is expected, but actual type is {value.GetType()}.");
+                }
 
 
                 var targetElementType = type.GetElementType();
                 var targetElementType = type.GetElementType();
                 var itemsConverted = new object[source.Length];
                 var itemsConverted = new object[source.Length];
@@ -194,7 +196,7 @@ namespace Jint.Runtime.Interop
                 return result;
                 return result;
             }
             }
 
 
-            if (type.IsGenericType() && type.GetGenericTypeDefinition() == typeof(Nullable<>))
+            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
             {
             {
                 type = Nullable.GetUnderlyingType(type);
                 type = Nullable.GetUnderlyingType(type);
             }
             }
@@ -204,10 +206,9 @@ namespace Jint.Runtime.Interop
 
 
         public virtual bool TryConvert(object value, Type type, IFormatProvider formatProvider, out object converted)
         public virtual bool TryConvert(object value, Type type, IFormatProvider formatProvider, out object converted)
         {
         {
-            bool canConvert;
-            var key = value == null ? String.Format("Null->{0}", type) : String.Format("{0}->{1}", value.GetType(), type);
+            var key = value == null ? $"Null->{type}" : $"{value.GetType()}->{type}";
 
 
-            if (!_knownConversions.TryGetValue(key, out canConvert))
+            if (!_knownConversions.TryGetValue(key, out var canConvert))
             {
             {
                 lock (_lockObject)
                 lock (_lockObject)
                 {
                 {

+ 19 - 9
Jint/Runtime/Interop/DelegateWrapper.cs

@@ -1,6 +1,5 @@
 using System;
 using System;
 using System.Globalization;
 using System.Globalization;
-using System.Linq;
 using System.Reflection;
 using System.Reflection;
 using Jint.Native;
 using Jint.Native;
 using Jint.Native.Function;
 using Jint.Native.Function;
@@ -14,20 +13,31 @@ namespace Jint.Runtime.Interop
     public sealed class DelegateWrapper : FunctionInstance
     public sealed class DelegateWrapper : FunctionInstance
     {
     {
         private readonly Delegate _d;
         private readonly Delegate _d;
+        private readonly bool _delegateContainsParamsArgument;
 
 
         public DelegateWrapper(Engine engine, Delegate d) : base(engine, null, null, false)
         public DelegateWrapper(Engine engine, Delegate d) : base(engine, null, null, false)
         {
         {
             _d = d;
             _d = d;
             Prototype = engine.Function.PrototypeObject;
             Prototype = engine.Function.PrototypeObject;
+
+            var parameterInfos = _d.Method.GetParameters();
+
+            _delegateContainsParamsArgument = false;
+            foreach (var p in parameterInfos)
+            {
+                if (Attribute.IsDefined(p, typeof(ParamArrayAttribute)))
+                {
+                    _delegateContainsParamsArgument = true;
+                    break;
+                }
+            }
         }
         }
 
 
         public override JsValue Call(JsValue thisObject, JsValue[] jsArguments)
         public override JsValue Call(JsValue thisObject, JsValue[] jsArguments)
         {
         {
-            var parameterInfos = _d.GetMethodInfo().GetParameters();
-
-            bool delegateContainsParamsArgument = parameterInfos.Any(p => p.HasAttribute<ParamArrayAttribute>());
+            var parameterInfos = _d.Method.GetParameters();
             int delegateArgumentsCount = parameterInfos.Length;
             int delegateArgumentsCount = parameterInfos.Length;
-            int delegateNonParamsArgumentsCount = delegateContainsParamsArgument ? delegateArgumentsCount - 1 : delegateArgumentsCount;
+            int delegateNonParamsArgumentsCount = _delegateContainsParamsArgument ? delegateArgumentsCount - 1 : delegateArgumentsCount;
 
 
             int jsArgumentsCount = jsArguments.Length;
             int jsArgumentsCount = jsArguments.Length;
             int jsArgumentsWithoutParamsCount = Math.Min(jsArgumentsCount, delegateNonParamsArgumentsCount);
             int jsArgumentsWithoutParamsCount = Math.Min(jsArgumentsCount, delegateNonParamsArgumentsCount);
@@ -55,7 +65,7 @@ namespace Jint.Runtime.Interop
             // assign null to parameters not provided
             // assign null to parameters not provided
             for (var i = jsArgumentsWithoutParamsCount; i < delegateNonParamsArgumentsCount; i++)
             for (var i = jsArgumentsWithoutParamsCount; i < delegateNonParamsArgumentsCount; i++)
             {
             {
-                if (parameterInfos[i].ParameterType.IsValueType())
+                if (parameterInfos[i].ParameterType.IsValueType)
                 {
                 {
                     parameters[i] = Activator.CreateInstance(parameterInfos[i].ParameterType);
                     parameters[i] = Activator.CreateInstance(parameterInfos[i].ParameterType);
                 }
                 }
@@ -66,7 +76,7 @@ namespace Jint.Runtime.Interop
             }
             }
 
 
             // assign params to array and converts each objet to expected type
             // assign params to array and converts each objet to expected type
-            if(delegateContainsParamsArgument)
+            if(_delegateContainsParamsArgument)
             {
             {
                 int paramsArgumentIndex = delegateArgumentsCount - 1;
                 int paramsArgumentIndex = delegateArgumentsCount - 1;
                 int paramsCount = Math.Max(0, jsArgumentsCount - delegateNonParamsArgumentsCount);
                 int paramsCount = Math.Max(0, jsArgumentsCount - delegateNonParamsArgumentsCount);
@@ -103,10 +113,10 @@ namespace Jint.Runtime.Interop
 
 
                 if (handler != null && handler(meaningfulException))
                 if (handler != null && handler(meaningfulException))
                 {
                 {
-                    throw new JavaScriptException(Engine.Error, meaningfulException.Message);
+                    ExceptionHelper.ThrowError(_engine, meaningfulException.Message);
                 }
                 }
 
 
-                throw meaningfulException;         
+                throw meaningfulException;
             }
             }
         }
         }
     }
     }

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

@@ -28,14 +28,15 @@ namespace Jint.Runtime.Interop
             var arguments = ProcessParamsArrays(jsArguments, methodInfos);
             var arguments = ProcessParamsArrays(jsArguments, methodInfos);
             var converter = Engine.ClrTypeConverter;
             var converter = Engine.ClrTypeConverter;
 
 
-            foreach (var method in TypeConverter.FindBestMatch(Engine, methodInfos, arguments))
+            foreach (var method in TypeConverter.FindBestMatch(methodInfos, arguments))
             {
             {
                 var parameters = new object[arguments.Length];
                 var parameters = new object[arguments.Length];
+                var methodParameters = method.GetParameters();
                 var argumentsMatch = true;
                 var argumentsMatch = true;
 
 
                 for (var i = 0; i < arguments.Length; i++)
                 for (var i = 0; i < arguments.Length; i++)
                 {
                 {
-                    var parameterType = method.GetParameters()[i].ParameterType;
+                    var parameterType = methodParameters[i].ParameterType;
 
 
                     if (typeof(JsValue).IsAssignableFrom(parameterType))
                     if (typeof(JsValue).IsAssignableFrom(parameterType))
                     {
                     {
@@ -62,8 +63,7 @@ namespace Jint.Runtime.Interop
                             break;
                             break;
                         }
                         }
 
 
-                        var lambdaExpression = parameters[i] as LambdaExpression;
-                        if (lambdaExpression != null)
+                        if (parameters[i] is LambdaExpression lambdaExpression)
                         {
                         {
                             parameters[i] = lambdaExpression.Compile();
                             parameters[i] = lambdaExpression.Compile();
                         }
                         }
@@ -78,7 +78,7 @@ namespace Jint.Runtime.Interop
                 // todo: cache method info
                 // todo: cache method info
                 try
                 try
                 {
                 {
-                    return JsValue.FromObject(Engine, method.Invoke(thisObject.ToObject(), parameters));
+                    return FromObject(Engine, method.Invoke(thisObject.ToObject(), parameters));
                 }
                 }
                 catch (TargetInvocationException exception)
                 catch (TargetInvocationException exception)
                 {
                 {
@@ -87,14 +87,15 @@ namespace Jint.Runtime.Interop
 
 
                     if (handler != null && handler(meaningfulException))
                     if (handler != null && handler(meaningfulException))
                     {
                     {
-                        throw new JavaScriptException(Engine.Error, meaningfulException.Message);
+                        ExceptionHelper.ThrowError(_engine, meaningfulException.Message);
                     }
                     }
 
 
                     throw meaningfulException;
                     throw meaningfulException;
                 }
                 }
             }
             }
 
 
-            throw new JavaScriptException(Engine.TypeError, "No public methods with the specified arguments were found.");
+            ExceptionHelper.ThrowTypeError(_engine, "No public methods with the specified arguments were found.");
+            return null;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -102,15 +103,14 @@ namespace Jint.Runtime.Interop
         /// </summary>
         /// </summary>
         private JsValue[] ProcessParamsArrays(JsValue[] jsArguments, MethodInfo[] methodInfos)
         private JsValue[] ProcessParamsArrays(JsValue[] jsArguments, MethodInfo[] methodInfos)
         {
         {
-            for (var i = 0; i < methodInfos.Length; i++)
+            foreach (var methodInfo in methodInfos)
             {
             {
-                var methodInfo = methodInfos[i];
                 var parameters = methodInfo.GetParameters();
                 var parameters = methodInfo.GetParameters();
 
 
                 bool hasParamArrayAttribute = false;
                 bool hasParamArrayAttribute = false;
-                for (int j = 0; j < parameters.Length; ++j)
+                foreach (var parameter in parameters)
                 {
                 {
-                    if (parameters[j].HasAttribute<ParamArrayAttribute>())
+                    if (Attribute.IsDefined(parameter, typeof(ParamArrayAttribute)))
                     {
                     {
                         hasParamArrayAttribute = true;
                         hasParamArrayAttribute = true;
                         break;
                         break;

+ 29 - 26
Jint/Runtime/Interop/NamespaceReference.cs

@@ -27,7 +27,7 @@ namespace Jint.Runtime.Interop
         {
         {
             if (throwOnError)
             if (throwOnError)
             {
             {
-                throw new JavaScriptException(Engine.TypeError, "Can't define a property of a NamespaceReference");
+                ExceptionHelper.ThrowTypeError(_engine, "Can't define a property of a NamespaceReference");
             }
             }
 
 
             return false;
             return false;
@@ -37,7 +37,7 @@ namespace Jint.Runtime.Interop
         {
         {
             if (throwOnError)
             if (throwOnError)
             {
             {
-                throw new JavaScriptException(Engine.TypeError, "Can't delete a property of a NamespaceReference");
+                ExceptionHelper.ThrowTypeError(_engine, "Can't delete a property of a NamespaceReference");
             }
             }
 
 
             return false;
             return false;
@@ -49,15 +49,15 @@ namespace Jint.Runtime.Interop
             var genericTypes = new Type[arguments.Length];
             var genericTypes = new Type[arguments.Length];
             for (int i = 0; i < arguments.Length; i++)
             for (int i = 0; i < arguments.Length; i++)
             {
             {
-                var genericTypeReference = arguments.At(i);
+                var genericTypeReference = arguments[i];
                 if (genericTypeReference.IsUndefined()
                 if (genericTypeReference.IsUndefined()
                     || !genericTypeReference.IsObject() 
                     || !genericTypeReference.IsObject() 
                     || genericTypeReference.AsObject().Class != "TypeReference")
                     || genericTypeReference.AsObject().Class != "TypeReference")
                 {
                 {
-                    throw new JavaScriptException(Engine.TypeError, "Invalid generic type parameter on " + _path + ", if this is not a generic type / method, are you missing a lookup assembly?");
+                    ExceptionHelper.ThrowTypeError(_engine, "Invalid generic type parameter on " + _path + ", if this is not a generic type / method, are you missing a lookup assembly?");
                 }
                 }
 
 
-                genericTypes[i] = arguments.At(i).As<TypeReference>().ReferenceType;
+                genericTypes[i] = ((TypeReference) genericTypeReference).ReferenceType;
             }
             }
 
 
             var typeReference = GetPath(_path + "`" + arguments.Length.ToString(CultureInfo.InvariantCulture)).As<TypeReference>();
             var typeReference = GetPath(_path + "`" + arguments.Length.ToString(CultureInfo.InvariantCulture)).As<TypeReference>();
@@ -75,7 +75,8 @@ namespace Jint.Runtime.Interop
             }
             }
             catch (Exception e)
             catch (Exception e)
             {
             {
-                throw new JavaScriptException(Engine.TypeError, "Invalid generic type parameter on " + _path + ", if this is not a generic type / method, are you missing a lookup assembly?", e);
+                ExceptionHelper.ThrowTypeError(_engine, "Invalid generic type parameter on " + _path + ", if this is not a generic type / method, are you missing a lookup assembly?", e);
+                return null;
             }
             }
         }
         }
 
 
@@ -88,14 +89,14 @@ namespace Jint.Runtime.Interop
 
 
         public JsValue GetPath(string path)
         public JsValue GetPath(string path)
         {
         {
-            if (Engine.TypeCache.TryGetValue(path, out var type))
+            if (_engine.TypeCache.TryGetValue(path, out var type))
             {
             {
                 if (type == null)
                 if (type == null)
                 {
                 {
-                    return new NamespaceReference(Engine, path);
+                    return new NamespaceReference(_engine, path);
                 }
                 }
 
 
-                return TypeReference.CreateTypeReference(Engine, type);
+                return TypeReference.CreateTypeReference(_engine, type);
             }
             }
 
 
             // in CoreCLR, for example, classes that used to be in
             // in CoreCLR, for example, classes that used to be in
@@ -106,54 +107,55 @@ namespace Jint.Runtime.Interop
             // search in loaded assemblies
             // search in loaded assemblies
             var lookupAssemblies = new[] {Assembly.GetCallingAssembly(), Assembly.GetExecutingAssembly()};
             var lookupAssemblies = new[] {Assembly.GetCallingAssembly(), Assembly.GetExecutingAssembly()};
 
 
-            var lookupAssembliesLength = lookupAssemblies.Length;
-            for (var i = 0; i < lookupAssembliesLength; i++)
+            foreach (var assembly in lookupAssemblies)
             {
             {
-                var assembly = lookupAssemblies[i];
                 type = assembly.GetType(path);
                 type = assembly.GetType(path);
                 if (type != null)
                 if (type != null)
                 {
                 {
-                    Engine.TypeCache.Add(path, type);
-                    return TypeReference.CreateTypeReference(Engine, type);
+                    _engine.TypeCache.Add(path, type);
+                    return TypeReference.CreateTypeReference(_engine, type);
                 }
                 }
             }
             }
 
 
             // search in lookup assemblies
             // search in lookup assemblies
-            foreach (var assembly in Engine.Options._LookupAssemblies)
+            var comparedPath = path.Replace("+", ".");
+            foreach (var assembly in _engine.Options._LookupAssemblies)
             {
             {
                 type = assembly.GetType(path);
                 type = assembly.GetType(path);
                 if (type != null)
                 if (type != null)
                 {
                 {
-                    Engine.TypeCache.Add(path, type);
-                    return TypeReference.CreateTypeReference(Engine, type);
+                    _engine.TypeCache.Add(path, type);
+                    return TypeReference.CreateTypeReference(_engine, type);
                 }
                 }
 
 
                 var lastPeriodPos = path.LastIndexOf(".", StringComparison.Ordinal);
                 var lastPeriodPos = path.LastIndexOf(".", StringComparison.Ordinal);
                 var trimPath = path.Substring(0, lastPeriodPos);
                 var trimPath = path.Substring(0, lastPeriodPos);
                 type = GetType(assembly, trimPath);
                 type = GetType(assembly, trimPath);
                 if (type != null)
                 if (type != null)
+                {
                     foreach (Type nType in GetAllNestedTypes(type))
                     foreach (Type nType in GetAllNestedTypes(type))
                     {
                     {
-                        if (nType.FullName.Replace("+", ".").Equals(path.Replace("+", ".")))
+                        if (nType.FullName.Replace("+", ".").Equals(comparedPath))
                         {
                         {
-                            Engine.TypeCache.Add(path.Replace("+", "."), nType);
-                            return TypeReference.CreateTypeReference(Engine, nType);
+                            _engine.TypeCache.Add(comparedPath, nType);
+                            return TypeReference.CreateTypeReference(_engine, nType);
                         }
                         }
                     }
                     }
+                }
             }
             }
 
 
             // search for type in mscorlib
             // search for type in mscorlib
             type = System.Type.GetType(path);
             type = System.Type.GetType(path);
             if (type != null)
             if (type != null)
             {
             {
-                Engine.TypeCache.Add(path, type);
-                return TypeReference.CreateTypeReference(Engine, type);
+                _engine.TypeCache.Add(path, type);
+                return TypeReference.CreateTypeReference(_engine, type);
             }
             }
 
 
             // the new path doesn't represent a known class, thus return a new namespace instance
             // the new path doesn't represent a known class, thus return a new namespace instance
 
 
-            Engine.TypeCache.Add(path, null);
-            return new NamespaceReference(Engine, path);
+            _engine.TypeCache.Add(path, null);
+            return new NamespaceReference(_engine, path);
         }
         }
 
 
         /// <summary>   Gets a type. </summary>
         /// <summary>   Gets a type. </summary>
@@ -164,10 +166,11 @@ namespace Jint.Runtime.Interop
         /// <returns>   The type. </returns>
         /// <returns>   The type. </returns>
         private static Type GetType(Assembly assembly, string typeName)
         private static Type GetType(Assembly assembly, string typeName)
         {
         {
+            var compared = typeName.Replace("+", ".");
             Type[] types = assembly.GetTypes();
             Type[] types = assembly.GetTypes();
             foreach (Type t in types)
             foreach (Type t in types)
             {
             {
-                if (t.FullName.Replace("+", ".") == typeName.Replace("+", "."))
+                if (t.FullName.Replace("+", ".") == compared)
                 {
                 {
                     return t;
                     return t;
                 }
                 }
@@ -176,7 +179,7 @@ namespace Jint.Runtime.Interop
             return null;
             return null;
         }
         }
 
 
-        private static IEnumerable<Type> GetAllNestedTypes(Type type)
+        private static Type[] GetAllNestedTypes(Type type)
         {
         {
             var types = new List<Type>();
             var types = new List<Type>();
             AddNestedTypesRecursively(types, type);
             AddNestedTypesRecursively(types, type);

+ 117 - 62
Jint/Runtime/Interop/ObjectWrapper.cs

@@ -1,5 +1,5 @@
 using System;
 using System;
-using System.Linq;
+using System.Collections.Generic;
 using System.Reflection;
 using System.Reflection;
 using Jint.Native;
 using Jint.Native;
 using Jint.Native.Object;
 using Jint.Native.Object;
@@ -13,21 +13,21 @@ namespace Jint.Runtime.Interop
 	/// </summary>
 	/// </summary>
 	public sealed class ObjectWrapper : ObjectInstance, IObjectWrapper
 	public sealed class ObjectWrapper : ObjectInstance, IObjectWrapper
     {
     {
-        public Object Target { get; set; }
-
-        public ObjectWrapper(Engine engine, Object obj)
+        public ObjectWrapper(Engine engine, object obj)
             : base(engine)
             : base(engine)
         {
         {
             Target = obj;
             Target = obj;
         }
         }
 
 
+        public object Target { get; }
+
         public override void Put(string propertyName, JsValue value, bool throwOnError)
         public override void Put(string propertyName, JsValue value, bool throwOnError)
         {
         {
             if (!CanPut(propertyName))
             if (!CanPut(propertyName))
             {
             {
                 if (throwOnError)
                 if (throwOnError)
                 {
                 {
-                    throw new JavaScriptException(Engine.TypeError);
+                    ExceptionHelper.ThrowTypeError(Engine);
                 }
                 }
 
 
                 return;
                 return;
@@ -39,105 +39,160 @@ namespace Jint.Runtime.Interop
             {
             {
                 if (throwOnError)
                 if (throwOnError)
                 {
                 {
-                    throw new JavaScriptException(Engine.TypeError, "Unknown member: " + propertyName);
-                }
-                else
-                {
-                    return;
+                    ExceptionHelper.ThrowTypeError(_engine, "Unknown member: " + propertyName);
                 }
                 }
             }
             }
-
-            ownDesc.Value = value;
+            else
+            {
+                ownDesc.Value = value;
+            }
         }
         }
 
 
         public override PropertyDescriptor GetOwnProperty(string propertyName)
         public override PropertyDescriptor GetOwnProperty(string propertyName)
         {
         {
             if (TryGetProperty(propertyName, out var x))
             if (TryGetProperty(propertyName, out var x))
+            {
                 return x;
                 return x;
+            }
 
 
             var type = Target.GetType();
             var type = Target.GetType();
+            var key = (type, propertyName);
+
+            if (!_engine.ClrPropertyDescriptorFactories.TryGetValue(key, out var factory))
+            {
+                factory = ResolveProperty(type, propertyName);
+                _engine.ClrPropertyDescriptorFactories[key] = factory;
+            }
 
 
+            var descriptor = factory(_engine, Target);
+            AddProperty(propertyName, descriptor);
+            return descriptor;
+        }
+
+        private static Func<Engine, object, PropertyDescriptor> ResolveProperty(Type type, string propertyName)
+        {
             // look for a property
             // look for a property
-            var property = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public)
-                .Where(p => EqualsIgnoreCasing(p.Name, propertyName))
-                .FirstOrDefault();
+            PropertyInfo property = null;
+            foreach (var p in type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
+            {
+                if (EqualsIgnoreCasing(p.Name, propertyName))
+                {
+                    property = p;
+                    break;
+                }
+            }
+
             if (property != null)
             if (property != null)
             {
             {
-                var descriptor = new PropertyInfoDescriptor(Engine, property, Target);
-                AddProperty(propertyName, descriptor);
-                return descriptor;
+                return (engine, target) => new PropertyInfoDescriptor(engine, property, target);
             }
             }
 
 
             // look for a field
             // look for a field
-            var field = type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public)
-                .Where(f => EqualsIgnoreCasing(f.Name, propertyName))
-                .FirstOrDefault();
+            FieldInfo field = null;
+            foreach (var f in type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
+            {
+                if (EqualsIgnoreCasing(f.Name, propertyName))
+                {
+                    field = f;
+                    break;
+                }
+            }
+
             if (field != null)
             if (field != null)
             {
             {
-                var descriptor = new FieldInfoDescriptor(Engine, field, Target);
-                AddProperty(propertyName, descriptor);
-                return descriptor;
+                return (engine, target) => new FieldInfoDescriptor(engine, field, target);
             }
             }
 
 
             // if no properties were found then look for a method
             // if no properties were found then look for a method
-            var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public)
-                .Where(m => EqualsIgnoreCasing(m.Name, propertyName))
-                .ToArray();
+            List<MethodInfo> methods = null;
+            foreach (var m in type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
+            {
+                if (EqualsIgnoreCasing(m.Name, propertyName))
+                {
+                    methods = methods ?? new List<MethodInfo>();
+                    methods.Add(m);
+                }
+            }
 
 
-            if (methods.Any())
+            if (methods?.Count > 0)
             {
             {
-                var descriptor = new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, methods), PropertyFlag.OnlyEnumerable);
-                AddProperty(propertyName, descriptor);
-                return descriptor;
+                return (engine, target) => new PropertyDescriptor(new MethodInfoFunctionInstance(engine, methods.ToArray()), PropertyFlag.OnlyEnumerable);
             }
             }
 
 
             // if no methods are found check if target implemented indexing
             // if no methods are found check if target implemented indexing
-            if (type.GetProperties().Where(p => p.GetIndexParameters().Length != 0).FirstOrDefault() != null)
+            PropertyInfo first = null;
+            foreach (var p in type.GetProperties())
             {
             {
-                return new IndexDescriptor(Engine, propertyName, Target);
+                if (p.GetIndexParameters().Length != 0)
+                {
+                    first = p;
+                    break;
+                }
             }
             }
 
 
-            var interfaces = type.GetInterfaces();
+            if (first != null)
+            {
+                return (engine, target) => new IndexDescriptor(engine, propertyName, target);
+            }
 
 
             // try to find a single explicit property implementation
             // try to find a single explicit property implementation
-            var explicitProperties = (from iface in interfaces
-                                      from iprop in iface.GetProperties()
-                                      where EqualsIgnoreCasing(iprop.Name, propertyName)
-                                      select iprop).ToArray();
+            List<PropertyInfo> list = null;
+            foreach (Type iface in type.GetInterfaces())
+            {
+                foreach (var iprop in iface.GetProperties())
+                {
+                    if (EqualsIgnoreCasing(iprop.Name, propertyName))
+                    {
+                        list = list ?? new List<PropertyInfo>();
+                        list.Add(iprop);
+                    }
+                }
+            }
 
 
-            if (explicitProperties.Length == 1)
+            if (list?.Count == 1)
             {
             {
-                var descriptor = new PropertyInfoDescriptor(Engine, explicitProperties[0], Target);
-                AddProperty(propertyName, descriptor);
-                return descriptor;
+                return (engine, target) => new PropertyInfoDescriptor(engine, list[0], target);
             }
             }
 
 
             // try to find explicit method implementations
             // try to find explicit method implementations
-            var explicitMethods = (from iface in interfaces
-                                   from imethod in iface.GetMethods()
-                                   where EqualsIgnoreCasing(imethod.Name, propertyName)
-                                   select imethod).ToArray();
+            List<MethodInfo> explicitMethods = null;
+            foreach (Type iface in type.GetInterfaces())
+            {
+                foreach (var imethod in iface.GetMethods())
+                {
+                    if (EqualsIgnoreCasing(imethod.Name, propertyName))
+                    {
+                        explicitMethods = explicitMethods ?? new List<MethodInfo>();
+                        explicitMethods.Add(imethod);
+                    }
+                }
+            }
 
 
-            if (explicitMethods.Length > 0)
+            if (explicitMethods?.Count > 0)
             {
             {
-                var descriptor = new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, explicitMethods), PropertyFlag.OnlyEnumerable);
-                AddProperty(propertyName, descriptor);
-                return descriptor;
+                return (engine, target) => new PropertyDescriptor(new MethodInfoFunctionInstance(engine, explicitMethods.ToArray()), PropertyFlag.OnlyEnumerable);
             }
             }
 
 
             // try to find explicit indexer implementations
             // try to find explicit indexer implementations
-            var explicitIndexers =
-                (from iface in interfaces
-                 from iprop in iface.GetProperties()
-                 where iprop.GetIndexParameters().Length != 0
-                 select iprop).ToArray();
+            List<PropertyInfo> explicitIndexers = null;
+            foreach (Type iface in type.GetInterfaces())
+            {
+                foreach (var iprop in iface.GetProperties())
+                {
+                    if (iprop.GetIndexParameters().Length != 0)
+                    {
+                        explicitIndexers = explicitIndexers ?? new List<PropertyInfo>();
+                        explicitIndexers.Add(iprop);
+                    }
+                }
+            }
 
 
-            if (explicitIndexers.Length == 1)
+            if (explicitIndexers?.Count == 1)
             {
             {
-                return new IndexDescriptor(Engine, explicitIndexers[0].DeclaringType, propertyName, Target);
+                return (engine, target) => new IndexDescriptor(engine, explicitIndexers[0].DeclaringType, propertyName, target);
             }
             }
 
 
-            return PropertyDescriptor.Undefined;
+            return (engine, target) => PropertyDescriptor.Undefined;
         }
         }
 
 
         private static bool EqualsIgnoreCasing(string s1, string s2)
         private static bool EqualsIgnoreCasing(string s1, string s2)
@@ -145,13 +200,13 @@ namespace Jint.Runtime.Interop
             bool equals = false;
             bool equals = false;
             if (s1.Length == s2.Length)
             if (s1.Length == s2.Length)
             {
             {
-                if (s1.Length > 0 && s2.Length > 0)
+                if (s1.Length > 0)
                 {
                 {
-                    equals = (s1.ToLower()[0] == s2.ToLower()[0]);
+                    equals = char.ToLowerInvariant(s1[0]) == char.ToLowerInvariant(s2[0]);
                 }
                 }
-                if (s1.Length > 1 && s2.Length > 1)
+                if (equals && s1.Length > 1)
                 {
                 {
-                    equals = equals && (s1.Substring(1) == s2.Substring(1));
+                    equals = s1.Substring(1) == s2.Substring(1);
                 }
                 }
             }
             }
             return equals;
             return equals;

+ 27 - 24
Jint/Runtime/Interop/TypeReference.cs

@@ -1,6 +1,6 @@
 using System;
 using System;
+using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
-using System.Linq;
 using System.Reflection;
 using System.Reflection;
 using Jint.Native;
 using Jint.Native;
 using Jint.Native.Function;
 using Jint.Native.Function;
@@ -10,7 +10,7 @@ using Jint.Runtime.Descriptors.Specialized;
 
 
 namespace Jint.Runtime.Interop
 namespace Jint.Runtime.Interop
 {
 {
-    public class TypeReference : FunctionInstance, IConstructor, IObjectWrapper
+    public sealed class TypeReference : FunctionInstance, IConstructor, IObjectWrapper
     {
     {
         private TypeReference(Engine engine)
         private TypeReference(Engine engine)
             : base(engine, null, null, false, "TypeReference")
             : base(engine, null, null, false, "TypeReference")
@@ -27,11 +27,10 @@ namespace Jint.Runtime.Interop
 
 
             // The value of the [[Prototype]] internal property of the TypeReference constructor is the Function prototype object
             // The value of the [[Prototype]] internal property of the TypeReference constructor is the Function prototype object
             obj.Prototype = engine.Function.PrototypeObject;
             obj.Prototype = engine.Function.PrototypeObject;
-
-            obj.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden));
+            obj._length = new PropertyDescriptor(0, PropertyFlag.AllForbidden);
 
 
             // The initial value of Boolean.prototype is the Boolean prototype object
             // The initial value of Boolean.prototype is the Boolean prototype object
-            obj.SetOwnProperty("prototype", new PropertyDescriptor(engine.Object.PrototypeObject, PropertyFlag.AllForbidden));
+            obj._prototype = new PropertyDescriptor(engine.Object.PrototypeObject, PropertyFlag.AllForbidden);
 
 
             return obj;
             return obj;
         }
         }
@@ -44,7 +43,7 @@ namespace Jint.Runtime.Interop
 
 
         public ObjectInstance Construct(JsValue[] arguments)
         public ObjectInstance Construct(JsValue[] arguments)
         {
         {
-            if (arguments.Length == 0 && ReferenceType.IsValueType())
+            if (arguments.Length == 0 && ReferenceType.IsValueType)
             {
             {
                 var instance = Activator.CreateInstance(ReferenceType);
                 var instance = Activator.CreateInstance(ReferenceType);
                 var result = TypeConverter.ToObject(Engine, JsValue.FromObject(Engine, instance));
                 var result = TypeConverter.ToObject(Engine, JsValue.FromObject(Engine, instance));
@@ -54,16 +53,15 @@ namespace Jint.Runtime.Interop
 
 
             var constructors = ReferenceType.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
             var constructors = ReferenceType.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
 
 
-            var methods = TypeConverter.FindBestMatch(Engine, constructors, arguments).ToList();
-
-            foreach (var method in methods)
+            foreach (var method in TypeConverter.FindBestMatch(constructors, arguments))
             {
             {
                 var parameters = new object[arguments.Length];
                 var parameters = new object[arguments.Length];
+                var methodParameters = method.GetParameters();
                 try
                 try
                 {
                 {
                     for (var i = 0; i < arguments.Length; i++)
                     for (var i = 0; i < arguments.Length; i++)
                     {
                     {
-                        var parameterType = method.GetParameters()[i].ParameterType;
+                        var parameterType = methodParameters[i].ParameterType;
 
 
                         if (typeof(JsValue).IsAssignableFrom(parameterType))
                         if (typeof(JsValue).IsAssignableFrom(parameterType))
                         {
                         {
@@ -79,7 +77,7 @@ namespace Jint.Runtime.Interop
                     }
                     }
 
 
                     var constructor = (ConstructorInfo)method;
                     var constructor = (ConstructorInfo)method;
-                    var instance = constructor.Invoke(parameters.ToArray());
+                    var instance = constructor.Invoke(parameters);
                     var result = TypeConverter.ToObject(Engine, JsValue.FromObject(Engine, instance));
                     var result = TypeConverter.ToObject(Engine, JsValue.FromObject(Engine, instance));
 
 
                     // todo: cache method info
                     // todo: cache method info
@@ -92,8 +90,8 @@ namespace Jint.Runtime.Interop
                 }
                 }
             }
             }
 
 
-            throw new JavaScriptException(Engine.TypeError, "No public methods with the specified arguments were found.");
-
+            ExceptionHelper.ThrowTypeError(_engine, "No public methods with the specified arguments were found.");
+            return null;
         }
         }
 
 
         public override bool HasInstance(JsValue v)
         public override bool HasInstance(JsValue v)
@@ -112,7 +110,7 @@ namespace Jint.Runtime.Interop
         {
         {
             if (throwOnError)
             if (throwOnError)
             {
             {
-                throw new JavaScriptException(Engine.TypeError, "Can't define a property of a TypeReference");
+                ExceptionHelper.ThrowTypeError(_engine, "Can't define a property of a TypeReference");
             }
             }
 
 
             return false;
             return false;
@@ -122,7 +120,7 @@ namespace Jint.Runtime.Interop
         {
         {
             if (throwOnError)
             if (throwOnError)
             {
             {
-                throw new JavaScriptException(Engine.TypeError, "Can't delete a property of a TypeReference");
+                ExceptionHelper.ThrowTypeError(_engine, "Can't delete a property of a TypeReference");
             }
             }
 
 
             return false;
             return false;
@@ -134,7 +132,7 @@ namespace Jint.Runtime.Interop
             {
             {
                 if (throwOnError)
                 if (throwOnError)
                 {
                 {
-                    throw new JavaScriptException(Engine.TypeError);
+                    ExceptionHelper.ThrowTypeError(Engine);
                 }
                 }
 
 
                 return;
                 return;
@@ -146,7 +144,7 @@ namespace Jint.Runtime.Interop
             {
             {
                 if (throwOnError)
                 if (throwOnError)
                 {
                 {
-                    throw new JavaScriptException(Engine.TypeError, "Unknown member: " + propertyName);
+                    ExceptionHelper.ThrowTypeError(_engine, "Unknown member: " + propertyName);
                 }
                 }
                 else
                 else
                 {
                 {
@@ -161,7 +159,7 @@ namespace Jint.Runtime.Interop
         {
         {
             // todo: cache members locally
             // todo: cache members locally
 
 
-            if (ReferenceType.IsEnum())
+            if (ReferenceType.IsEnum)
             {
             {
                 Array enumValues = Enum.GetValues(ReferenceType);
                 Array enumValues = Enum.GetValues(ReferenceType);
                 Array enumNames = Enum.GetNames(ReferenceType);
                 Array enumNames = Enum.GetNames(ReferenceType);
@@ -188,17 +186,22 @@ namespace Jint.Runtime.Interop
                 return new FieldInfoDescriptor(Engine, fieldInfo, Type);
                 return new FieldInfoDescriptor(Engine, fieldInfo, Type);
             }
             }
 
 
-            var methodInfo = ReferenceType
-                .GetMethods(BindingFlags.Public | BindingFlags.Static)
-                .Where(mi => mi.Name == propertyName)
-                .ToArray();
+            List<MethodInfo> methodInfo = null;
+            foreach (var mi in ReferenceType.GetMethods(BindingFlags.Public | BindingFlags.Static))
+            {
+                if (mi.Name == propertyName)
+                {
+                    methodInfo = methodInfo ?? new List<MethodInfo>();
+                    methodInfo.Add(mi);
+                }
+            }
 
 
-            if (methodInfo.Length == 0)
+            if (methodInfo?.Count == 0)
             {
             {
                 return PropertyDescriptor.Undefined;
                 return PropertyDescriptor.Undefined;
             }
             }
 
 
-            return new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, methodInfo), PropertyFlag.AllForbidden);
+            return new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, methodInfo.ToArray()), PropertyFlag.AllForbidden);
         }
         }
 
 
         public object Target => ReferenceType;
         public object Target => ReferenceType;

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

@@ -31,7 +31,7 @@
 //            var typeReference = thisObj.As<TypeReference>();
 //            var typeReference = thisObj.As<TypeReference>();
 //            if (typeReference == null)
 //            if (typeReference == null)
 //            {
 //            {
-//                throw new JavaScriptException(Engine.TypeError);
+//                ExceptionHelper.ThrowTypeError(Engine);
 //            }
 //            }
 
 
 //            return typeReference.Type.FullName;
 //            return typeReference.Type.FullName;

+ 0 - 165
Jint/Runtime/MruPropertyCache.cs

@@ -1,165 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-
-namespace Jint.Runtime
-{
-    internal class MruPropertyCache<TKey, TValue> : IDictionary<TKey, TValue>
-    {
-        private IDictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>();
-        private LinkedList<KeyValuePair<TKey, TValue>> _list;
-        private uint _length;
-
-        public MruPropertyCache(uint length) {
-            _length = length;
-            _list = new LinkedList<KeyValuePair<TKey, TValue>>();
-            for(int i=0; i<length; i++) {
-                _list.AddLast(new KeyValuePair<TKey, TValue>(default(TKey), default(TValue)));
-            }
-        }
-
-        private bool Find(TKey key, out LinkedListNode<KeyValuePair<TKey, TValue>> result) {
-            result = _list.First;
-            while(result != null) {
-                if(key.Equals(result.Value.Key)) {
-                    return true;
-                }
-
-                result = result.Next;
-            }
-
-            return false;
-        }
-        public TValue this[TKey key] {
-            get {
-                LinkedListNode<KeyValuePair<TKey, TValue>> node;
-                if(Find(key, out node)) {
-                    return node.Value.Value;
-                }
-                
-                return _dictionary[key];
-            }
-
-            set {
-                LinkedListNode<KeyValuePair<TKey, TValue>> node;
-                if (!Find(key, out node)) {
-                    _list.AddFirst(new KeyValuePair<TKey, TValue>(key, value));
-                    _list.RemoveLast();
-                }
-                else
-                {
-                    node.Value = new KeyValuePair<TKey, TValue>(key, value);
-                }
-
-                _dictionary[key] = value;
-            }
-        }
-
-        public int Count {
-            get {
-                return _dictionary.Count;
-            }
-        }
-
-        public bool IsReadOnly {
-            get {
-                return _dictionary.IsReadOnly;
-            }
-        }
-
-        public ICollection<TKey> Keys {
-            get {
-                return _dictionary.Keys;
-            }
-        }
-
-        public ICollection<TValue> Values {
-            get {
-                return _dictionary.Values;
-            }
-        }
-
-        public void Add(KeyValuePair<TKey, TValue> item) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (!Find(item.Key, out node)) {
-                _list.AddFirst(item);
-                _list.RemoveLast();
-            }
-
-            _dictionary.Add(item);
-        }
-
-        public void Add(TKey key, TValue value) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (!Find(key, out node)) {
-                _list.AddFirst(new KeyValuePair<TKey, TValue>(key, value));
-                _list.RemoveLast();
-            }
-            _dictionary.Add(key, value);
-        }
-
-        public void Clear() {
-            _list.Clear();
-            _dictionary.Clear();
-        }
-
-        public bool Contains(KeyValuePair<TKey, TValue> item) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (Find(item.Key, out node)) {
-                return true;
-            }
-
-            return _dictionary.Contains(item);
-        }
-
-        public bool ContainsKey(TKey key) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (Find(key, out node)) {
-                return true;
-            }
-
-            return _dictionary.ContainsKey(key);
-        }
-
-        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
-            _dictionary.CopyTo(array, arrayIndex);
-        }
-
-        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
-            return _dictionary.GetEnumerator();
-        }
-
-        public bool Remove(KeyValuePair<TKey, TValue> item) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (Find(item.Key, out node))
-            {
-                _list.Remove(node);
-            }
-
-            return _dictionary.Remove(item);
-        }
-
-        public bool Remove(TKey key) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (Find(key, out node))
-            {
-                _list.Remove(node);
-            }
-
-            return _dictionary.Remove(key);
-        }
-
-        public bool TryGetValue(TKey key, out TValue value) {
-            LinkedListNode<KeyValuePair<TKey, TValue>> node;
-            if (Find(key, out node)) {
-                value = node.Value.Value;
-                return true;
-            }
-
-            return _dictionary.TryGetValue(key, out value);
-        }
-
-        IEnumerator IEnumerable.GetEnumerator() {
-            return _dictionary.GetEnumerator();
-        }
-    }
-}

+ 0 - 172
Jint/Runtime/MruPropertyCache2.cs

@@ -1,172 +0,0 @@
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-
-namespace Jint.Runtime
-{
-    internal class MruPropertyCache2<TValue> where TValue : class
-    {
-        private Dictionary<string, TValue> _dictionary;
-        private bool _set;
-        private string _key;
-        private TValue _value;
-
-        public TValue this[string key]
-        {
-            get
-            {
-                if (_set && _key == key)
-                {
-                    return _value;
-                }
-
-                return _dictionary?[key];
-            }
-
-            set
-            {
-                EnsureInitialized(key);
-                _set = true;
-                _key = key;
-                _value = value;
-
-                if (_dictionary != null)
-                {
-                    _dictionary[key] = value;
-                }
-            }
-        }
-
-        public int Count
-        {
-            get
-            {
-                int count = _set ? 1 : 0;
-                if (_dictionary != null)
-                {
-                    count += _dictionary.Count;
-                }
-
-                return count;
-            }
-        }
-
-        public void Add(string key, TValue value)
-        {
-            EnsureInitialized(key);
-            _set = true;
-            _key = key;
-            _value = value;
-
-            _dictionary?.Add(key, value);
-        }
-
-        public void Clear()
-        {
-            _set = false;
-            _key = default(string);
-            _value = null;
-
-            _dictionary?.Clear();
-        }
-
-        public bool ContainsKey(string key)
-        {
-            if (_set && key.Equals(_key))
-            {
-                return true;
-            }
-
-            return _dictionary != null && _dictionary.ContainsKey(key);
-        }
-
-        public IEnumerable<KeyValuePair<string, TValue>> GetEnumerator()
-        {
-            if (_dictionary == null)
-            {
-                if (_set)
-                {
-                    yield return new KeyValuePair<string, TValue>(_key, _value);
-                }
-
-                yield break;
-            }
-
-            foreach (var pair in _dictionary)
-            {
-                yield return pair;
-            }
-        }
-
-        public bool Remove(string key)
-        {
-            bool removed = false;
-            if (_set && key.Equals(_key))
-            {
-                _set = false;
-                _key = null;
-                _value = null;
-                removed = true;
-            }
-
-            _dictionary?.Remove(key);
-            return removed;
-        }
-
-        public bool TryGetValue(string key, out TValue value)
-        {
-            if (_set && _key.Equals(key))
-            {
-                value = _value;
-                return true;
-            }
-
-            if (_dictionary == null)
-            {
-                value = null;
-                return false;
-            }
-
-            return _dictionary.TryGetValue(key, out value);
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private void EnsureInitialized(string key)
-        {
-            if (_set && _key != key)
-            {
-                if (_dictionary == null)
-                {
-                    _dictionary = new Dictionary<string, TValue>();
-                }
-
-                _dictionary[_key] = _value;
-            }
-        }
-
-        public string[] GetKeys()
-        {
-            int size = _set ? 1 : 0;
-            if (_dictionary != null)
-            {
-                size += _dictionary.Count;
-            }
-
-            var keys = new string[size];
-            int n = 0;
-            if (_set)
-            {
-                keys[n++] = _key;
-            }
-
-            if (_dictionary != null)
-            {
-                foreach (var key in _dictionary.Keys)
-                {
-                    keys[n++] = key;
-                }
-            }
-
-            return keys;
-        }
-    }
-}

+ 4 - 9
Jint/Runtime/RefStack.cs

@@ -7,7 +7,7 @@ namespace Jint.Runtime
     internal sealed class ExecutionContextStack
     internal sealed class ExecutionContextStack
     {
     {
         private ExecutionContext[] _array;
         private ExecutionContext[] _array;
-        private int _size;
+        private uint _size;
 
 
         private const int DefaultCapacity = 4;
         private const int DefaultCapacity = 4;
 
 
@@ -22,7 +22,7 @@ namespace Jint.Runtime
         {
         {
             if (_size == 0)
             if (_size == 0)
             {
             {
-                ThrowEmptyStackException();
+                ExceptionHelper.ThrowInvalidOperationException("stack is empty");
             }
             }
             return ref _array[_size - 1];
             return ref _array[_size - 1];
         }
         }
@@ -32,7 +32,7 @@ namespace Jint.Runtime
         {
         {
             if (_size == 0)
             if (_size == 0)
             {
             {
-                ThrowEmptyStackException();
+                ExceptionHelper.ThrowInvalidOperationException("stack is empty");
             }
             }
             _size--;
             _size--;
         }
         }
@@ -40,7 +40,7 @@ namespace Jint.Runtime
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public void Push(in ExecutionContext item)
         public void Push(in ExecutionContext item)
         {
         {
-            if (_size == _array.Length)
+            if (_size == (uint) _array.Length)
             {
             {
                 var newSize = 2 * _array.Length;
                 var newSize = 2 * _array.Length;
                 var newArray = new ExecutionContext[newSize];
                 var newArray = new ExecutionContext[newSize];
@@ -51,11 +51,6 @@ namespace Jint.Runtime
             _array[_size++] = item;
             _array[_size++] = item;
         }
         }
 
 
-        private static void ThrowEmptyStackException()
-        {
-            throw new InvalidOperationException("stack is empty");
-        }
-
         public void ReplaceTopLexicalEnvironment(LexicalEnvironment newEnv)
         public void ReplaceTopLexicalEnvironment(LexicalEnvironment newEnv)
         {
         {
             _array[_size - 1] = _array[_size - 1].UpdateLexicalEnvironment(newEnv);
             _array[_size - 1] = _array[_size - 1].UpdateLexicalEnvironment(newEnv);

+ 15 - 6
Jint/Runtime/References/Reference.cs

@@ -11,9 +11,9 @@ namespace Jint.Runtime.References
     /// </summary>
     /// </summary>
     public sealed class Reference
     public sealed class Reference
     {
     {
-        private JsValue _baseValue;
-        private string _name;
-        private bool _strict;
+        internal JsValue _baseValue;
+        internal string _name;
+        internal bool _strict;
 
 
         public Reference(JsValue baseValue, string name, bool strict)
         public Reference(JsValue baseValue, string name, bool strict)
         {
         {
@@ -42,20 +42,21 @@ namespace Jint.Runtime.References
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool HasPrimitiveBase()
         public bool HasPrimitiveBase()
         {
         {
-            return _baseValue.IsPrimitive();
+            return _baseValue._type != Types.Object && _baseValue._type != Types.None;
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsUnresolvableReference()
         public bool IsUnresolvableReference()
         {
         {
-            return _baseValue.IsUndefined();
+            return _baseValue._type == Types.Undefined;
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsPropertyReference()
         public bool IsPropertyReference()
         {
         {
             // http://www.ecma-international.org/ecma-262/5.1/#sec-8.7
             // http://www.ecma-international.org/ecma-262/5.1/#sec-8.7
-            return _baseValue.IsPrimitive() || (_baseValue.IsObject() && !(_baseValue is EnvironmentRecord));
+            return _baseValue._type != Types.Object && _baseValue._type != Types.None
+                   || _baseValue._type == Types.Object && !(_baseValue is EnvironmentRecord);
         }
         }
 
 
         internal Reference Reassign(JsValue baseValue, string name, bool strict)
         internal Reference Reassign(JsValue baseValue, string name, bool strict)
@@ -66,5 +67,13 @@ namespace Jint.Runtime.References
 
 
             return this;
             return this;
         }
         }
+
+        internal void AssertValid(Engine engine)
+        {
+            if(_strict && (_name == "eval" || _name == "arguments") && _baseValue is EnvironmentRecord)
+            {
+                ExceptionHelper.ThrowSyntaxError(engine);
+            }
+        }
     }
     }
 }
 }

+ 56 - 34
Jint/Runtime/StatementInterpreter.cs

@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using Esprima.Ast;
 using Esprima.Ast;
 using Jint.Native;
 using Jint.Native;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
@@ -8,7 +7,7 @@ using Jint.Runtime.References;
 
 
 namespace Jint.Runtime
 namespace Jint.Runtime
 {
 {
-    public class StatementInterpreter
+    public sealed class StatementInterpreter
     {
     {
         private readonly Engine _engine;
         private readonly Engine _engine;
 
 
@@ -38,11 +37,11 @@ namespace Jint.Runtime
             Completion result;
             Completion result;
             if (TypeConverter.ToBoolean(_engine.GetValue(_engine.EvaluateExpression(ifStatement.Test), true)))
             if (TypeConverter.ToBoolean(_engine.GetValue(_engine.EvaluateExpression(ifStatement.Test), true)))
             {
             {
-                result = ExecuteStatement(ifStatement.Consequent);
+                result = _engine.ExecuteStatement(ifStatement.Consequent);
             }
             }
             else if (ifStatement.Alternate != null)
             else if (ifStatement.Alternate != null)
             {
             {
-                result = ExecuteStatement(ifStatement.Alternate);
+                result = _engine.ExecuteStatement(ifStatement.Alternate);
             }
             }
             else
             else
             {
             {
@@ -57,7 +56,7 @@ namespace Jint.Runtime
             // TODO: Esprima added Statement.Label, maybe not necessary as this line is finding the
             // TODO: Esprima added Statement.Label, maybe not necessary as this line is finding the
             // containing label and could keep a table per program with all the labels
             // containing label and could keep a table per program with all the labels
             // labeledStatement.Body.LabelSet = labeledStatement.Label;
             // labeledStatement.Body.LabelSet = labeledStatement.Label;
-            var result = ExecuteStatement(labeledStatement.Body);
+            var result = _engine.ExecuteStatement(labeledStatement.Body);
             if (result.Type == CompletionType.Break && result.Identifier == labeledStatement.Label.Name)
             if (result.Type == CompletionType.Break && result.Identifier == labeledStatement.Label.Name)
             {
             {
                 var value = result.Value;
                 var value = result.Value;
@@ -79,7 +78,7 @@ namespace Jint.Runtime
 
 
             do
             do
             {
             {
-                var stmt = ExecuteStatement(doWhileStatement.Body);
+                var stmt = _engine.ExecuteStatement(doWhileStatement.Body);
                 if (!ReferenceEquals(stmt.Value, null))
                 if (!ReferenceEquals(stmt.Value, null))
                 {
                 {
                     v = stmt.Value;
                     v = stmt.Value;
@@ -121,7 +120,7 @@ namespace Jint.Runtime
                     return new Completion(CompletionType.Normal, v, null);
                     return new Completion(CompletionType.Normal, v, null);
                 }
                 }
 
 
-                var stmt = ExecuteStatement(whileStatement.Body);
+                var stmt = _engine.ExecuteStatement(whileStatement.Body);
 
 
                 if (!ReferenceEquals(stmt.Value, null))
                 if (!ReferenceEquals(stmt.Value, null))
                 {
                 {
@@ -155,7 +154,7 @@ namespace Jint.Runtime
             {
             {
                 if (init.Type == Nodes.VariableDeclaration)
                 if (init.Type == Nodes.VariableDeclaration)
                 {
                 {
-                    var c = ExecuteStatement((Statement) init);
+                    var c = _engine.ExecuteStatement((Statement) init);
 
 
                 }
                 }
                 else
                 else
@@ -176,7 +175,7 @@ namespace Jint.Runtime
                     }
                     }
                 }
                 }
 
 
-                var stmt = ExecuteStatement(forStatement.Body);
+                var stmt = _engine.ExecuteStatement(forStatement.Body);
                 if (!ReferenceEquals(stmt.Value, null))
                 if (!ReferenceEquals(stmt.Value, null))
                 {
                 {
                     v = stmt.Value;
                     v = stmt.Value;
@@ -230,7 +229,8 @@ namespace Jint.Runtime
             {
             {
                 var keys = _engine.Object.GetOwnPropertyNames(Undefined.Instance, Arguments.From(cursor)).AsArray();
                 var keys = _engine.Object.GetOwnPropertyNames(Undefined.Instance, Arguments.From(cursor)).AsArray();
 
 
-                for (var i = 0; i < keys.GetLength(); i++)
+                var length = keys.GetLength();
+                for (var i = 0; i < length; i++)
                 {
                 {
                     var p = keys.GetOwnProperty(TypeConverter.ToString(i)).Value.AsStringWithoutTypeCheck();
                     var p = keys.GetOwnProperty(TypeConverter.ToString(i)).Value.AsStringWithoutTypeCheck();
 
 
@@ -255,7 +255,7 @@ namespace Jint.Runtime
 
 
                     _engine.PutValue(varRef, p);
                     _engine.PutValue(varRef, p);
 
 
-                    var stmt = ExecuteStatement(forInStatement.Body);
+                    var stmt = _engine.ExecuteStatement(forInStatement.Body);
                     if (!ReferenceEquals(stmt.Value, null))
                     if (!ReferenceEquals(stmt.Value, null))
                     {
                     {
                         v = stmt.Value;
                         v = stmt.Value;
@@ -337,7 +337,7 @@ namespace Jint.Runtime
             Completion c;
             Completion c;
             try
             try
             {
             {
-                c = ExecuteStatement(withStatement.Body);
+                c = _engine.ExecuteStatement(withStatement.Body);
             }
             }
             catch (JavaScriptException e)
             catch (JavaScriptException e)
             {
             {
@@ -419,17 +419,24 @@ namespace Jint.Runtime
 
 
         public Completion ExecuteStatementList(List<StatementListItem> statementList)
         public Completion ExecuteStatementList(List<StatementListItem> statementList)
         {
         {
+            // optimize common case without loop
+            return statementList.Count == 1 
+                ? ExecuteSingleStatement((Statement) statementList[0]) 
+                : ExecuteMultipleStatements(statementList);
+        }
+
+        private Completion ExecuteMultipleStatements(List<StatementListItem> statementList)
+        {
+            Statement s = null;
             var c = new Completion(CompletionType.Normal, null, null);
             var c = new Completion(CompletionType.Normal, null, null);
             Completion sl = c;
             Completion sl = c;
-            Statement s = null;
-
             try
             try
             {
             {
                 var statementListCount = statementList.Count;
                 var statementListCount = statementList.Count;
                 for (var i = 0; i < statementListCount; i++)
                 for (var i = 0; i < statementListCount; i++)
                 {
                 {
                     s = (Statement) statementList[i];
                     s = (Statement) statementList[i];
-                    c = ExecuteStatement(s);
+                    c = _engine.ExecuteStatement(s);
                     if (c.Type != CompletionType.Normal)
                     if (c.Type != CompletionType.Normal)
                     {
                     {
                         var executeStatementList = new Completion(
                         var executeStatementList = new Completion(
@@ -446,13 +453,37 @@ namespace Jint.Runtime
             }
             }
             catch (JavaScriptException v)
             catch (JavaScriptException v)
             {
             {
-                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;
             }
             }
 
 
             return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
             return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
         }
         }
 
 
+        private Completion ExecuteSingleStatement(Statement s)
+        {
+            try
+            {
+                var c = _engine.ExecuteStatement(s);
+                if (c.Type != CompletionType.Normal)
+                {
+                    var completion = new Completion(
+                        c.Type,
+                        c.Value,
+                        c.Identifier,
+                        c.Location);
+
+                    return completion;
+                }
+
+                return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
+            }
+            catch (JavaScriptException v)
+            {
+                return new Completion(CompletionType.Throw, v.Error, null, v.Location ?? s?.Location);
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.13
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.13
         /// </summary>
         /// </summary>
@@ -471,27 +502,27 @@ namespace Jint.Runtime
         /// <returns></returns>
         /// <returns></returns>
         public Completion ExecuteTryStatement(TryStatement tryStatement)
         public Completion ExecuteTryStatement(TryStatement tryStatement)
         {
         {
-            var b = ExecuteStatement(tryStatement.Block);
+            var b = _engine.ExecuteStatement(tryStatement.Block);
             if (b.Type == CompletionType.Throw)
             if (b.Type == CompletionType.Throw)
             {
             {
                 // execute catch
                 // execute catch
                 var catchClause = tryStatement.Handler;
                 var catchClause = tryStatement.Handler;
                 if (catchClause != null)
                 if (catchClause != null)
                 {
                 {
-                    var c = _engine.GetValue(b);
+                    var c = b.Value;
                     var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
                     var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
                     var catchEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
                     var catchEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
-                    catchEnv.Record.CreateMutableBinding(((Identifier) catchClause.Param).Name, c);
+                    catchEnv._record.CreateMutableBinding(((Identifier) catchClause.Param).Name, c);
 
 
                     _engine.UpdateLexicalEnvironment(catchEnv);
                     _engine.UpdateLexicalEnvironment(catchEnv);
-                    b = ExecuteStatement(catchClause.Body);
+                    b = _engine.ExecuteStatement(catchClause.Body);
                     _engine.UpdateLexicalEnvironment(oldEnv);
                     _engine.UpdateLexicalEnvironment(oldEnv);
                 }
                 }
             }
             }
 
 
             if (tryStatement.Finalizer != null)
             if (tryStatement.Finalizer != null)
             {
             {
-                var f = ExecuteStatement(tryStatement.Finalizer);
+                var f = _engine.ExecuteStatement(tryStatement.Finalizer);
                 if (f.Type == CompletionType.Normal)
                 if (f.Type == CompletionType.Normal)
                 {
                 {
                     return b;
                     return b;
@@ -516,21 +547,12 @@ namespace Jint.Runtime
                 var declaration = statement.Declarations[i];
                 var declaration = statement.Declarations[i];
                 if (declaration.Init != null)
                 if (declaration.Init != null)
                 {
                 {
-                    if (!(_engine.EvaluateExpression(declaration.Id) is Reference lhs))
-                    {
-                        throw new ArgumentException();
-                    }
-
-                    if (lhs.IsStrict()
-                        && lhs.GetBase() is EnvironmentRecord
-                        && (lhs.GetReferencedName() == "eval" || lhs.GetReferencedName() == "arguments"))
-                    {
-                        throw new JavaScriptException(_engine.SyntaxError);
-                    }
+                    var lhs = (Reference) _engine.EvaluateExpression(declaration.Id);
+                    lhs.AssertValid(_engine);
 
 
                     var value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init), true);
                     var value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init), true);
                     _engine.PutValue(lhs, value);
                     _engine.PutValue(lhs, value);
-                    _engine.ReferencePool.Return(lhs);
+                    _engine._referencePool.Return(lhs);
                 }
                 }
             }
             }
 
 

+ 802 - 0
Jint/Runtime/StructDictionary.cs

@@ -0,0 +1,802 @@
+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);
+            }
+        }
+    }
+}

+ 115 - 194
Jint/Runtime/TypeConverter.cs

@@ -1,7 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
-using System.Linq;
 using System.Reflection;
 using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Esprima.Ast;
@@ -10,24 +9,23 @@ using Jint.Native.Number;
 using Jint.Native.Object;
 using Jint.Native.Object;
 using Jint.Native.String;
 using Jint.Native.String;
 using Jint.Runtime.References;
 using Jint.Runtime.References;
-using Jint.Native.Symbol;
 
 
 namespace Jint.Runtime
 namespace Jint.Runtime
 {
 {
     public enum Types
     public enum Types
     {
     {
-        None,
-        Undefined,
-        Null,
-        Boolean,
-        String,
-        Number,
-        Object,
-        Completion,
-        Symbol
+        None = 0,
+        Undefined = 1,
+        Null = 2,
+        Boolean = 3,
+        String = 4,
+        Number = 5,
+        Symbol = 9,
+        Object = 10,
+        Completion = 20,
     }
     }
 
 
-    public class TypeConverter
+    public static class TypeConverter
     {
     {
         // how many decimals to check when determining if double is actually an int
         // how many decimals to check when determining if double is actually an int
         private const double DoubleIsIntegerTolerance = double.Epsilon * 100;
         private const double DoubleIsIntegerTolerance = double.Epsilon * 100;
@@ -55,7 +53,7 @@ namespace Jint.Runtime
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static JsValue ToPrimitive(JsValue input, Types preferredType = Types.None)
         public static JsValue ToPrimitive(JsValue input, Types preferredType = Types.None)
         {
         {
-            if (input._type == Types.Null || input._type == Types.Undefined || input.IsPrimitive())
+            if (input._type > Types.None && input._type < Types.Object) 
             {
             {
                 return input;
                 return input;
             }
             }
@@ -69,77 +67,46 @@ namespace Jint.Runtime
         /// </summary>
         /// </summary>
         public static bool ToBoolean(JsValue o)
         public static bool ToBoolean(JsValue o)
         {
         {
-            if (o.IsBoolean())
+            switch (o._type)
             {
             {
-                return ((JsBoolean) o)._value;
-            }
-
-            if (o.IsUndefined() || o.IsNull())
-            {
-                return false;
-            }
-
-            if (o.IsNumber())
-            {
-                var n = ((JsNumber) o)._value;
-                if (n.Equals(0) || double.IsNaN(n))
-                {
+                case Types.Boolean:
+                    return ((JsBoolean) o)._value;
+                case Types.Undefined:
+                case Types.Null:
                     return false;
                     return false;
-                }
-
-                return true;
-            }
-
-            if (o.IsString())
-            {
-                return !((JsString) o).IsNullOrEmpty();
+                case Types.Number:
+                    var n = ((JsNumber) o)._value;
+                    return n != 0 && !double.IsNaN(n);
+                case Types.String:
+                    return !((JsString) o).IsNullOrEmpty();
+                default:
+                    return true;
             }
             }
-
-            return true;
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.3
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.3
         /// </summary>
         /// </summary>
-        /// <param name="o"></param>
-        /// <returns></returns>
         public static double ToNumber(JsValue o)
         public static double ToNumber(JsValue o)
         {
         {
-            // check number first as this is what is usually expected
-            if (o.IsNumber())
-            {
-                return ((JsNumber) o)._value;
-            }
-
-            if (o.IsUndefined())
-            {
-                return double.NaN;
-            }
-
-            if (o.IsNull())
-            {
-                return 0;
-            }
-
-            if (o._type == Types.Object)
-            {
-                if (o is IPrimitiveInstance p)
-                {
-                    o = p.PrimitiveValue;
-                }
-            }
-
-            if (o.IsBoolean())
-            {
-                return ((JsBoolean) o)._value ? 1 : 0;
+            switch (o._type)
+            {
+                // check number first as this is what is usually expected
+                case Types.Number:
+                    return ((JsNumber) o)._value;
+                case Types.Undefined:
+                    return double.NaN;
+                case Types.Null:
+                    return 0;
+                case Types.Object when o is IPrimitiveInstance p:
+                    return ToNumber(ToPrimitive(p.PrimitiveValue, Types.Number));
+                case Types.Boolean:
+                    return ((JsBoolean) o)._value ? 1 : 0;
+                case Types.String:
+                    return ToNumber(o.AsStringWithoutTypeCheck());
+                default:
+                    return ToNumber(ToPrimitive(o, Types.Number));
             }
             }
-
-            if (o.IsString())
-            {
-                return ToNumber(o.AsStringWithoutTypeCheck());
-            }
-
-            return ToNumber(ToPrimitive(o, Types.Number));
         }
         }
 
 
         private static double ToNumber(string input)
         private static double ToNumber(string input)
@@ -154,19 +121,22 @@ namespace Jint.Runtime
                 ? StringPrototype.TrimEx(input)
                 ? StringPrototype.TrimEx(input)
                 : input;
                 : input;
 
 
-            if (string.IsNullOrEmpty(s))
+            if (s.Length == 0)
             {
             {
                 return 0;
                 return 0;
             }
             }
 
 
-            if ("+Infinity".Equals(s) || "Infinity".Equals(s))
+            if (s.Length == 8 || s.Length == 9)
             {
             {
-                return double.PositiveInfinity;
-            }
+                if ("+Infinity" == s || "Infinity" == s)
+                {
+                    return double.PositiveInfinity;
+                }
 
 
-            if ("-Infinity".Equals(s))
-            {
-                return double.NegativeInfinity;
+                if ("-Infinity" == s)
+                {
+                    return double.NegativeInfinity;
+                }
             }
             }
 
 
             // todo: use a common implementation with JavascriptParser
             // todo: use a common implementation with JavascriptParser
@@ -180,11 +150,11 @@ namespace Jint.Runtime
                         return double.NaN;
                         return double.NaN;
                     }
                     }
 
 
-                    double n = Double.Parse(s,
+                    double n = double.Parse(s,
                         NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign |
                         NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign |
                         NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite |
                         NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite |
                         NumberStyles.AllowExponent, CultureInfo.InvariantCulture);
                         NumberStyles.AllowExponent, CultureInfo.InvariantCulture);
-                    if (s.StartsWith("-") && n.Equals(0))
+                    if (s.StartsWith("-") && n == 0)
                     {
                     {
                         return -0.0;
                         return -0.0;
                     }
                     }
@@ -220,7 +190,7 @@ namespace Jint.Runtime
                 return 0;
                 return 0;
             }
             }
 
 
-            if (number.Equals(0) || double.IsInfinity(number))
+            if (number == 0 || double.IsInfinity(number))
             {
             {
                 return number;
                 return number;
             }
             }
@@ -237,7 +207,7 @@ namespace Jint.Runtime
                 return 0;
                 return 0;
             }
             }
 
 
-            if (number.Equals(0) || double.IsInfinity(number))
+            if (number == 0 || double.IsInfinity(number))
             {
             {
                 return number;
                 return number;
             }
             }
@@ -295,7 +265,7 @@ namespace Jint.Runtime
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static string ToString(uint i)
         internal static string ToString(uint i)
         {
         {
-            return i >= 0 && i < intToString.Length
+            return i < (uint) intToString.Length
                 ? intToString[i]
                 ? intToString[i]
                 : i.ToString();
                 : i.ToString();
         }
         }
@@ -327,102 +297,51 @@ namespace Jint.Runtime
         /// <returns></returns>
         /// <returns></returns>
         public static string ToString(JsValue o)
         public static string ToString(JsValue o)
         {
         {
-            if (o.IsString())
-            {
-                return o.AsStringWithoutTypeCheck();
-            }
-
-            if (o.IsUndefined())
-            {
-                return Undefined.Text;
-            }
-
-            if (o.IsNull())
-            {
-                return Null.Text;
+            switch (o._type)
+            {
+                case Types.String:
+                    return o.AsStringWithoutTypeCheck();
+                case Types.Boolean:
+                    return ((JsBoolean) o)._value ? "true" : "false";
+                case Types.Number:
+                    return ToString(((JsNumber) o)._value);
+                case Types.Symbol:
+                    return o.AsSymbol();
+                case Types.Undefined:
+                    return Undefined.Text;
+                case Types.Null:
+                    return Null.Text;
+                case Types.Object when o is IPrimitiveInstance p:
+                    return ToString(ToPrimitive(p.PrimitiveValue, Types.String));
+                default:
+                    return ToString(ToPrimitive(o, Types.String));
             }
             }
-
-            if (o.IsObject())
-            {
-                if (o is IPrimitiveInstance p)
-                {
-                    o = p.PrimitiveValue;
-                }
-                else
-                {
-                    var s = o.AsInstance<SymbolInstance>();
-                    if (!ReferenceEquals(s, null))
-                    {
-                        // TODO: throw a TypeError
-                        // NB: But it requires an Engine reference
-                        throw new JavaScriptException(new JsString("TypeError"));
-                    }
-                }
-            }
-
-            if (o.IsBoolean())
-            {
-                return ((JsBoolean) o)._value ? "true" : "false";
-            }
-
-            if (o.IsNumber())
-            {
-                return ToString(((JsNumber) o)._value);
-            }
-
-            if (o.IsSymbol())
-            {
-                return o.AsSymbol();
-            }
-
-            return ToString(ToPrimitive(o, Types.String));
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static ObjectInstance ToObject(Engine engine, JsValue value)
         public static ObjectInstance ToObject(Engine engine, JsValue value)
         {
         {
-            if (value.IsObject())
-            {
-                return (ObjectInstance) value;
-            }
-
-            if (value.IsBoolean())
-            {
-                return engine.Boolean.Construct(((JsBoolean) value)._value);
-            }
-
-            if (value.IsNumber())
-            {
-                return engine.Number.Construct(((JsNumber) value)._value);
-            }
-
-            if (value.IsString())
-            {
-                return engine.String.Construct(value.AsStringWithoutTypeCheck());
-            }
-
-            if (value.IsSymbol())
-            {
-                return engine.Symbol.Construct(((JsSymbol) value)._value);
+            switch (value._type)
+            {
+                case Types.Object:
+                    return (ObjectInstance) value;
+                case Types.Boolean:
+                    return engine.Boolean.Construct(((JsBoolean) value)._value);
+                case Types.Number:
+                    return engine.Number.Construct(((JsNumber) value)._value);
+                case Types.String:
+                    return engine.String.Construct(value.AsStringWithoutTypeCheck());
+                case Types.Symbol:
+                    return engine.Symbol.Construct(((JsSymbol) value)._value);
+                default:
+                    ExceptionHelper.ThrowTypeError(engine);
+                    return null;
             }
             }
-
-            if (value.IsUndefined() || value.IsNull())
-            {
-                ThrowTypeError(engine);
-            }
-            
-            ThrowTypeError(engine);
-            return null;
-        }
-
-        private static void ThrowTypeError(Engine engine)
-        {
-            throw new JavaScriptException(engine.TypeError);
         }
         }
 
 
         public static Types GetPrimitiveType(JsValue value)
         public static Types GetPrimitiveType(JsValue value)
         {
         {
-            if (value.IsObject())
+            if (value._type == Types.Object)
             {
             {
                 if (value is IPrimitiveInstance primitive)
                 if (value is IPrimitiveInstance primitive)
                 {
                 {
@@ -441,7 +360,7 @@ namespace Jint.Runtime
             MemberExpression expression,
             MemberExpression expression,
             object baseReference)
             object baseReference)
         {
         {
-            if (!o.IsUndefined() && !o.IsNull())
+            if (o._type != Types.Undefined && o._type != Types.Null)
             {
             {
                 return;
                 return;
             }
             }
@@ -452,51 +371,53 @@ namespace Jint.Runtime
                 return;
                 return;
             }
             }
 
 
-            string referencedName;
-            
+            ThrowTypeError(engine, o, expression, baseReference);
+        }
+
+        private static void ThrowTypeError(Engine engine, JsValue o, MemberExpression expression, object baseReference)
+        {
+            var referencedName = "The value";
             if (baseReference is Reference reference)
             if (baseReference is Reference reference)
             {
             {
                 referencedName = reference.GetReferencedName();
                 referencedName = reference.GetReferencedName();
             }
             }
-            else
-            {
-                referencedName = "The value";
-            }
             
             
             var message = $"{referencedName} is {o}";
             var message = $"{referencedName} is {o}";
-
-            throw new JavaScriptException(engine.TypeError, message)
-                .SetCallstack(engine, expression.Location);
+            throw new JavaScriptException(engine.TypeError, message).SetCallstack(engine, expression.Location);
         }
         }
 
 
         public static void CheckObjectCoercible(Engine engine, JsValue o)
         public static void CheckObjectCoercible(Engine engine, JsValue o)
         {
         {
-            if (o.IsUndefined() || o.IsNull())
+            if (o._type == Types.Undefined || o._type == Types.Null)
             {
             {
-                throw new JavaScriptException(engine.TypeError);
+                ExceptionHelper.ThrowTypeError(engine);
             }
             }
         }
         }
 
 
-        public static IEnumerable<MethodBase> FindBestMatch(Engine engine, MethodBase[] methods, JsValue[] arguments)
+        public static IEnumerable<MethodBase> FindBestMatch<T>(T[] methods, JsValue[] arguments) where T : MethodBase
         {
         {
-            methods = methods
-                .Where(m => m.GetParameters().Length == arguments.Length)
-                .ToArray();
+            var matchingByParameterCount = new List<T>();
+            foreach (var m in methods)
+            {
+                if (m.GetParameters().Length == arguments.Length)
+                {
+                    matchingByParameterCount.Add(m);
+                }
+            }
 
 
-            if (methods.Length == 1 && !methods[0].GetParameters().Any())
+            if (matchingByParameterCount.Count == 1 && arguments.Length == 0)
             {
             {
-                yield return methods[0];
+                yield return matchingByParameterCount[0];
                 yield break;
                 yield break;
             }
             }
 
 
-            var objectArguments = arguments.Select(x => x.ToObject()).ToArray();
-            foreach (var method in methods)
+            foreach (var method in matchingByParameterCount)
             {
             {
                 var perfectMatch = true;
                 var perfectMatch = true;
                 var parameters = method.GetParameters();
                 var parameters = method.GetParameters();
                 for (var i = 0; i < arguments.Length; i++)
                 for (var i = 0; i < arguments.Length; i++)
                 {
                 {
-                    var arg = objectArguments[i];
+                    var arg = arguments[i].ToObject();
                     var paramType = parameters[i].ParameterType;
                     var paramType = parameters[i].ParameterType;
 
 
                     if (arg == null)
                     if (arg == null)
@@ -521,15 +442,15 @@ namespace Jint.Runtime
                 }
                 }
             }
             }
 
 
-            foreach (var method in methods)
+            for (var i = 0; i < matchingByParameterCount.Count; i++)
             {
             {
-                yield return method;
+                yield return matchingByParameterCount[i];
             }
             }
         }
         }
 
 
         public static bool TypeIsNullable(Type type)
         public static bool TypeIsNullable(Type type)
         {
         {
-            return !type.IsValueType() || Nullable.GetUnderlyingType(type) != null;
+            return !type.IsValueType || Nullable.GetUnderlyingType(type) != null;
         }
         }
     }
     }
 }
 }

+ 5 - 5
appveyor.yml

@@ -10,9 +10,9 @@ build_script:
   - dotnet --version
   - dotnet --version
   - dotnet pack -c Release
   - dotnet pack -c Release
 test_script:
 test_script:
-  - dotnet test .\Jint.Tests\Jint.Tests.csproj -c Release -f netcoreapp2.0
-  - dotnet test .\Jint.Tests.CommonScripts\Jint.Tests.CommonScripts.csproj -c Release -f netcoreapp2.0
-  - dotnet test .\Jint.Tests.Ecma\Jint.Tests.Ecma.csproj -c Release -f netcoreapp2.0
+  - dotnet test .\Jint.Tests\Jint.Tests.csproj -c Release -f netcoreapp2.1
+  - dotnet test .\Jint.Tests.CommonScripts\Jint.Tests.CommonScripts.csproj -c Release -f netcoreapp2.1
+  - dotnet test .\Jint.Tests.Ecma\Jint.Tests.Ecma.csproj -c Release -f netcoreapp2.1
 artifacts:
 artifacts:
   - path: 'Jint\**\*.nupkg'
   - path: 'Jint\**\*.nupkg'
 deploy:  
 deploy:  
@@ -29,7 +29,7 @@ deploy:
       branch: rel/3.0-beta
       branch: rel/3.0-beta
     server: https://www.nuget.org/api/v2/package
     server: https://www.nuget.org/api/v2/package
     api_key:
     api_key:
-      secure: uSwY6pjuXvfqDxmODdEjabmByaHFCibtCGhEbjmDbEBx2TG5/zRYW8E+6qts+NX5
+      secure: JxLWXrsmfG8K7ECxi5dYFsPZERpRLdi3u5W8RSS/auJ5tzqv+PLCZzUB+yeY1V38
     skip_symbols: true
     skip_symbols: true
     artifact: /.*\.nupkg/  
     artifact: /.*\.nupkg/  
   - provider: NuGet
   - provider: NuGet
@@ -37,6 +37,6 @@ deploy:
       branch: master
       branch: master
     server: https://www.nuget.org/api/v2/package
     server: https://www.nuget.org/api/v2/package
     api_key:
     api_key:
-      secure: uSwY6pjuXvfqDxmODdEjabmByaHFCibtCGhEbjmDbEBx2TG5/zRYW8E+6qts+NX5
+      secure: JxLWXrsmfG8K7ECxi5dYFsPZERpRLdi3u5W8RSS/auJ5tzqv+PLCZzUB+yeY1V38
     skip_symbols: true
     skip_symbols: true
     artifact: /.*\.nupkg/  
     artifact: /.*\.nupkg/