Forráskód Böngészése

Merge remote-tracking branch 'origin/dev' into rel/3.0-beta

Sebastien Ros 7 éve
szülő
commit
5286d88ea5
95 módosított fájl, 2681 hozzáadás és 1969 törlés
  1. 0 0
      Jint.Benchmark/ArrayBenchmark.cs
  2. 2 1
      Jint.Benchmark/Jint.Benchmark.csproj
  3. 1 1
      Jint.Benchmark/UncacheableExpressionsBenchmark.cs
  4. 61 0
      Jint.Tests/Runtime/EngineTests.cs
  5. 23 0
      Jint.Tests/Runtime/InteropTests.cs
  6. 270 150
      Jint/Engine.cs
  7. 3 2
      Jint/Jint.csproj
  8. 65 0
      Jint/JsValueExtensions.cs
  9. 18 18
      Jint/Native/Argument/ArgumentsInstance.cs
  10. 17 9
      Jint/Native/Array/ArrayConstructor.cs
  11. 263 83
      Jint/Native/Array/ArrayInstance.cs
  12. 86 88
      Jint/Native/Array/ArrayPrototype.cs
  13. 3 3
      Jint/Native/Boolean/BooleanConstructor.cs
  14. 4 18
      Jint/Native/Boolean/BooleanInstance.cs
  15. 9 13
      Jint/Native/Boolean/BooleanPrototype.cs
  16. 48 47
      Jint/Native/Date/DateConstructor.cs
  17. 1 9
      Jint/Native/Date/DateInstance.cs
  18. 5 5
      Jint/Native/Date/DatePrototype.cs
  19. 6 5
      Jint/Native/Error/ErrorConstructor.cs
  20. 1 9
      Jint/Native/Error/ErrorInstance.cs
  21. 4 4
      Jint/Native/Error/ErrorPrototype.cs
  22. 3 8
      Jint/Native/Function/EvalFunctionInstance.cs
  23. 7 8
      Jint/Native/Function/FunctionConstructor.cs
  24. 37 27
      Jint/Native/Function/FunctionInstance.cs
  25. 23 15
      Jint/Native/Function/FunctionPrototype.cs
  26. 44 45
      Jint/Native/Function/ScriptFunctionInstance.cs
  27. 2 5
      Jint/Native/Function/ThrowTypeError.cs
  28. 2 2
      Jint/Native/Global/GlobalObject.cs
  29. 6 12
      Jint/Native/JsBoolean.cs
  30. 1 3
      Jint/Native/JsNull.cs
  31. 4 13
      Jint/Native/JsNumber.cs
  32. 25 39
      Jint/Native/JsString.cs
  33. 2 14
      Jint/Native/JsSymbol.cs
  34. 1 3
      Jint/Native/JsUndefined.cs
  35. 124 85
      Jint/Native/JsValue.cs
  36. 5 21
      Jint/Native/Json/JsonInstance.cs
  37. 1 1
      Jint/Native/Json/JsonParser.cs
  38. 20 21
      Jint/Native/Json/JsonSerializer.cs
  39. 188 11
      Jint/Native/Math/MathInstance.cs
  40. 1 4
      Jint/Native/Number/Dtoa/FastDtoaBuilder.cs
  41. 8 8
      Jint/Native/Number/NumberConstructor.cs
  42. 7 19
      Jint/Native/Number/NumberInstance.cs
  43. 7 5
      Jint/Native/Number/NumberPrototype.cs
  44. 46 44
      Jint/Native/Object/ObjectConstructor.cs
  45. 195 185
      Jint/Native/Object/ObjectInstance.cs
  46. 4 4
      Jint/Native/Object/ObjectPrototype.cs
  47. 23 24
      Jint/Native/RegExp/RegExpConstructor.cs
  48. 1 10
      Jint/Native/RegExp/RegExpInstance.cs
  49. 9 9
      Jint/Native/RegExp/RegExpPrototype.cs
  50. 5 5
      Jint/Native/String/StringConstructor.cs
  51. 16 17
      Jint/Native/String/StringInstance.cs
  52. 60 41
      Jint/Native/String/StringPrototype.cs
  53. 5 6
      Jint/Native/Symbol/SymbolConstructor.cs
  54. 3 17
      Jint/Native/Symbol/SymbolInstance.cs
  55. 4 4
      Jint/Native/Symbol/SymbolPrototype.cs
  56. 19 7
      Jint/Options.cs
  57. 1 1
      Jint/Pooling/ArgumentsInstancePool.cs
  58. 0 41
      Jint/Pooling/CompletionPool.cs
  59. 79 0
      Jint/Pooling/JsValueArrayPool.cs
  60. 1 1
      Jint/Runtime/Arguments.cs
  61. 20 41
      Jint/Runtime/Completion.cs
  62. 5 4
      Jint/Runtime/Debugger/DebugHandler.cs
  63. 0 16
      Jint/Runtime/Descriptors/IPropertyDescriptor .cs
  64. 271 57
      Jint/Runtime/Descriptors/PropertyDescriptor.cs
  65. 0 62
      Jint/Runtime/Descriptors/PropertyDescriptorExtensions.cs
  66. 26 0
      Jint/Runtime/Descriptors/PropertyFlag.cs
  67. 0 24
      Jint/Runtime/Descriptors/Specialized/AllForbiddenPropertyDescriptor.cs
  68. 4 9
      Jint/Runtime/Descriptors/Specialized/ClrAccessDescriptor.cs
  69. 0 24
      Jint/Runtime/Descriptors/Specialized/ConfigurableEnumerableWritablePropertyDescriptor.cs
  70. 0 24
      Jint/Runtime/Descriptors/Specialized/EnumerablePropertyDescriptor.cs
  71. 3 7
      Jint/Runtime/Descriptors/Specialized/FieldInfoDescriptor.cs
  72. 43 0
      Jint/Runtime/Descriptors/Specialized/GetSetPropertyDescriptor.cs
  73. 5 7
      Jint/Runtime/Descriptors/Specialized/IndexDescriptor.cs
  74. 0 24
      Jint/Runtime/Descriptors/Specialized/NonConfigurablePropertyDescriptor.cs
  75. 0 24
      Jint/Runtime/Descriptors/Specialized/NonEnumerablePropertyDescriptor.cs
  76. 0 24
      Jint/Runtime/Descriptors/Specialized/NullConfigurationPropertyDescriptor.cs
  77. 3 7
      Jint/Runtime/Descriptors/Specialized/PropertyInfoDescriptor.cs
  78. 0 24
      Jint/Runtime/Descriptors/Specialized/WritablePropertyDescriptor.cs
  79. 3 6
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  80. 15 4
      Jint/Runtime/Environments/ExecutionContext.cs
  81. 27 10
      Jint/Runtime/Environments/LexicalEnvironment.cs
  82. 3 6
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  83. 113 91
      Jint/Runtime/ExpressionIntepreter.cs
  84. 2 2
      Jint/Runtime/Interop/ClrFunctionInstance.cs
  85. 1 1
      Jint/Runtime/Interop/DefaultTypeConverter.cs
  86. 4 4
      Jint/Runtime/Interop/NamespaceReference.cs
  87. 4 4
      Jint/Runtime/Interop/ObjectWrapper.cs
  88. 8 10
      Jint/Runtime/Interop/TypeReference.cs
  89. 5 5
      Jint/Runtime/JavaScriptException.cs
  90. 15 0
      Jint/Runtime/MemoryLimitExceededException.cs
  91. 64 0
      Jint/Runtime/RefStack.cs
  92. 8 1
      Jint/Runtime/References/Reference.cs
  93. 82 101
      Jint/Runtime/StatementInterpreter.cs
  94. 62 78
      Jint/Runtime/TypeConverter.cs
  95. 6 18
      Jint/StrictModeScope.cs

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
Jint.Benchmark/ArrayBenchmark.cs


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

@@ -12,13 +12,14 @@
     <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
     <GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
     <GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
+    <LangVersion>latest</LangVersion>
   </PropertyGroup>
   <ItemGroup>
     <None Include=".\Scripts\**" CopyToOutputDirectory="PreserveNewest" />
     <None Include="..\Jint.Tests.CommonScripts\Scripts\**" CopyToOutputDirectory="PreserveNewest" LinkBase="SunSpider" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.10.12" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.10.14" />
     <ProjectReference Include="..\Jint\Jint.csproj" />
     <PackageReference Include="Jurassic" Version="3.0.0-alpha2" />
     <PackageReference Include="NiL.JS.NetCore" Version="2.5.1200" />

+ 1 - 1
Jint.Benchmark/UncacheableExpressionsBenchmark.cs

@@ -23,7 +23,7 @@ namespace Jint.Benchmark
         {
             public Config()
             {
-                Add(Job.ShortRun);
+                Add(Job.MediumRun.WithLaunchCount(1));
                 Add(MemoryDiagnoser.Default);
             }
         }

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

@@ -647,6 +647,14 @@ namespace Jint.Tests.Runtime
             );
         }
 
+        [Fact]
+        public void ShouldThrowMemoryLimitExceeded()
+        {
+            Assert.Throws<MemoryLimitExceededException>(
+                () => new Engine(cfg => cfg.LimitMemory(2048)).Execute("a=[]; while(true){ a.push(0); }")
+            );
+        }
+
         [Fact]
         public void ShouldThrowTimeout()
         {
@@ -1964,6 +1972,20 @@ namespace Jint.Tests.Runtime
 
             Assert.True(result);
         }
+        
+        [Fact]
+        public void ShouldNotCompareClrInstancesWithObjects()
+        {
+            var engine = new Engine();
+
+            var guid1 = Guid.NewGuid();
+
+            engine.SetValue("guid1", guid1);
+
+            var result = engine.Execute("guid1 == {}").GetCompletionValue().AsBoolean();
+
+            Assert.False(result);
+        }
 
         [Fact]
         public void ShouldStringifyNumWithoutV8DToA()
@@ -1976,6 +1998,45 @@ namespace Jint.Tests.Runtime
             Assert.True(val.AsString() == "53.6841659");
         }
 
+        [Fact]
+        public void ShouldStringifyObjectWithPropertiesToSameRef()
+        {
+            var engine = new Engine();
+            var res = engine.Execute(@"
+                var obj = {
+                    a : [],
+                    a1 : ['str'],
+                    a2 : {},
+                    a3 : { 'prop' : 'val' }
+                };
+                obj.b = obj.a;
+                obj.b1 = obj.a1;
+                JSON.stringify(obj);
+            ").GetCompletionValue();
+
+            Assert.True(res == "{\"a\":[],\"a1\":[\"str\"],\"a2\":{},\"a3\":{\"prop\":\"val\"},\"b\":[],\"b1\":[\"str\"]}");
+        }
+
+        [Fact]
+        public void ShouldThrowOnSerializingCyclicRefObject()
+        {
+            var engine = new Engine();
+            var res = engine.Execute(@"
+                (function(){
+                    try{
+                        a = [];
+                        a[0] = a;
+                        my_text = JSON.stringify(a);
+                    }
+                    catch(ex){
+                        return ex.message;
+                    }
+                })();
+            ").GetCompletionValue();
+
+            Assert.True(res == "Cyclic reference detected.");
+        }
+
         [Theory]
         [InlineData("", "escape('')")]
         [InlineData("%u0100%u0101%u0102", "escape('\u0100\u0101\u0102')")]

+ 23 - 0
Jint.Tests/Runtime/InteropTests.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Reflection;
 using Jint.Native;
+using Jint.Native.Array;
 using Jint.Native.Object;
 using Jint.Tests.Runtime.Converters;
 using Jint.Tests.Runtime.Domain;
@@ -653,6 +654,28 @@ namespace Jint.Tests.Runtime
             Assert.Equal("bar.com", ((object[])parts)[1]);
         }
 
+        [Fact]
+        public void ShouldLoopWithNativeEnumerator()
+        {
+            JsValue adder(JsValue argValue)
+            {
+                ArrayInstance args = argValue.AsArray();
+                double sum = 0;
+                foreach (var item in args)
+                {
+                    if (item.IsNumber())
+                    {
+                        sum += item.AsNumber();
+                    }
+                }
+                return sum;
+            }
+            var result = _engine.SetValue("getSum", new Func<JsValue, JsValue>(adder))
+                .Execute("getSum([1,2,3]);");
+
+            Assert.True(result.GetCompletionValue() == 6);
+        }
+
         [Fact]
         public void ShouldConvertBooleanInstanceToBool()
         {

+ 270 - 150
Jint/Engine.cs

@@ -39,12 +39,19 @@ namespace Jint
 
         private readonly ExpressionInterpreter _expressions;
         private readonly StatementInterpreter _statements;
-        private readonly Stack<ExecutionContext> _executionContexts;
+        private readonly ExecutionContextStack _executionContexts;
         private JsValue _completionValue = JsValue.Undefined;
         private int _statementsCount;
+        private long _initialMemoryUsage;
         private long _timeoutTicks;
-        private INode _lastSyntaxNode = null;
-
+        private INode _lastSyntaxNode;
+
+        // cached access
+        private readonly bool _isDebugMode;
+        private readonly bool _isStrict;
+        private readonly int _maxStatements;
+        private readonly IReferenceResolver _referenceResolver;
+        
         public ITypeConverter ClrTypeConverter;
 
         // cache of types used when resolving CLR type names
@@ -73,13 +80,23 @@ namespace Jint
 
         internal JintCallStack CallStack = new JintCallStack();
 
+        static Engine()
+        {
+            var methodInfo = typeof(GC).GetMethod("GetAllocatedBytesForCurrentThread");
+
+            if (methodInfo != null)
+            {
+                GetAllocatedBytesForCurrentThread =  (Func<long>)Delegate.CreateDelegate(typeof(Func<long>), null, methodInfo);
+            }
+        }
+
         public Engine() : this(null)
         {
         }
 
         public Engine(Action<Options> options)
         {
-            _executionContexts = new Stack<ExecutionContext>();
+            _executionContexts = new ExecutionContextStack();
 
             Global = GlobalObject.CreateGlobalObject(this);
 
@@ -153,10 +170,16 @@ namespace Jint
             Options = new Options();
 
             options?.Invoke(Options);
+            
+            // gather some options as fields for faster checks
+            _isDebugMode = Options.IsDebugMode;
+            _isStrict = Options.IsStrict;
+            _maxStatements = Options._MaxStatements;
+            _referenceResolver = Options.ReferenceResolver;
 
             ReferencePool = new ReferencePool();
-            CompletionPool = new CompletionPool();
             ArgumentsInstancePool = new ArgumentsInstancePool(this);
+            JsValueArrayPool = new JsValueArrayPool();
 
             Eval = new EvalFunctionInstance(this, System.Array.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
             Global.FastAddProperty("eval", Eval, true, false, true);
@@ -178,38 +201,47 @@ namespace Jint
             DebugHandler = new DebugHandler(this);
         }
 
-        public LexicalEnvironment GlobalEnvironment;
-        public GlobalObject Global { get; private set; }
-        public ObjectConstructor Object { get; private set; }
-        public FunctionConstructor Function { get; private set; }
-        public ArrayConstructor Array { get; private set; }
-        public StringConstructor String { get; private set; }
-        public RegExpConstructor RegExp { get; private set; }
-        public BooleanConstructor Boolean { get; private set; }
-        public NumberConstructor Number { get; private set; }
-        public DateConstructor Date { get; private set; }
-        public MathInstance Math { get; private set; }
-        public JsonInstance Json { get; private set; }
-        public SymbolConstructor Symbol { get; private set; }
-        public EvalFunctionInstance Eval { get; private set; }
-
-        public ErrorConstructor Error { get; private set; }
-        public ErrorConstructor EvalError { get; private set; }
-        public ErrorConstructor SyntaxError { get; private set; }
-        public ErrorConstructor TypeError { get; private set; }
-        public ErrorConstructor RangeError { get; private set; }
-        public ErrorConstructor ReferenceError { get; private set; }
-        public ErrorConstructor UriError { get; private set; }
-
-        public ExecutionContext ExecutionContext { get { return _executionContexts.Peek(); } }
+        public LexicalEnvironment GlobalEnvironment { get; }
+        public GlobalObject Global { get; }
+        public ObjectConstructor Object { get; }
+        public FunctionConstructor Function { get; }
+        public ArrayConstructor Array { get; }
+        public StringConstructor String { get; }
+        public RegExpConstructor RegExp { get; }
+        public BooleanConstructor Boolean { get; }
+        public NumberConstructor Number { get; }
+        public DateConstructor Date { get; }
+        public MathInstance Math { get; }
+        public JsonInstance Json { get; }
+        public SymbolConstructor Symbol { get; }
+        public EvalFunctionInstance Eval { get; }
+
+        public ErrorConstructor Error { get; }
+        public ErrorConstructor EvalError { get; }
+        public ErrorConstructor SyntaxError { get; }
+        public ErrorConstructor TypeError { get; }
+        public ErrorConstructor RangeError { get; }
+        public ErrorConstructor ReferenceError { get; }
+        public ErrorConstructor UriError { get; }
+
+        public ref readonly ExecutionContext ExecutionContext
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get { return ref _executionContexts.Peek(); }
+        }
 
         public GlobalSymbolRegistry GlobalSymbolRegistry { get; }
 
-        internal Options Options { get; private set; }
+        internal Options Options { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; }
+
+        internal ReferencePool ReferencePool
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get;
+        }
 
-        internal ReferencePool ReferencePool { get; }
-        internal CompletionPool CompletionPool { get; }
         internal ArgumentsInstancePool ArgumentsInstancePool { get; }
+        internal JsValueArrayPool JsValueArrayPool { get; }
 
         #region Debugger
         public delegate StepMode DebugStepDelegate(object sender, DebugInformation e);
@@ -230,17 +262,19 @@ namespace Jint
         }
         #endregion
 
-        public ExecutionContext EnterExecutionContext(LexicalEnvironment lexicalEnvironment, LexicalEnvironment variableEnvironment, JsValue thisBinding)
+        private static readonly Func<long> GetAllocatedBytesForCurrentThread;
+
+        public void EnterExecutionContext(
+            LexicalEnvironment lexicalEnvironment,
+            LexicalEnvironment variableEnvironment,
+            JsValue thisBinding)
         {
-            var executionContext = new ExecutionContext
-                {
-                    LexicalEnvironment = lexicalEnvironment,
-                    VariableEnvironment = variableEnvironment,
-                    ThisBinding = thisBinding
-                };
-            _executionContexts.Push(executionContext);
+            var context = new ExecutionContext(
+                lexicalEnvironment,
+                variableEnvironment,
+                thisBinding);
 
-            return executionContext;
+            _executionContexts.Push(context);
         }
 
         public Engine SetValue(string name, Delegate value)
@@ -293,6 +327,14 @@ namespace Jint
             _statementsCount = 0;
         }
 
+        public void ResetMemoryUsage()
+        {
+            if (GetAllocatedBytesForCurrentThread != null)
+            {
+                _initialMemoryUsage = GetAllocatedBytesForCurrentThread();
+            }
+        }
+
         public void ResetTimeoutTicks()
         {
             var timeoutIntervalTicks = Options._TimeoutInterval.Ticks;
@@ -321,24 +363,28 @@ namespace Jint
         public Engine Execute(Program program)
         {
             ResetStatementsCount();
+            
+            if (Options._MemoryLimit > 0)
+            {
+                ResetMemoryUsage();
+            }
+            
             ResetTimeoutTicks();
             ResetLastStatement();
             ResetCallStack();
 
-            using (new StrictModeScope(Options._IsStrict || program.Strict))
+            using (new StrictModeScope(_isStrict || program.Strict))
             {
                 DeclarationBindingInstantiation(DeclarationBindingType.GlobalCode, program.HoistingScope.FunctionDeclarations, program.HoistingScope.VariableDeclarations, null, null);
 
                 var result = _statements.ExecuteProgram(program);
-                if (result.Type == Completion.Throw)
+                if (result.Type == CompletionType.Throw)
                 {
                     var ex = new JavaScriptException(result.GetValueOrDefault()).SetCallstack(this, result.Location);
-                    CompletionPool.Return(result);
                     throw ex;
                 }
 
                 _completionValue = result.GetValueOrDefault();
-                CompletionPool.Return(result);
             }
 
             return this;
@@ -359,20 +405,35 @@ namespace Jint
 
         public Completion ExecuteStatement(Statement statement)
         {
-            var maxStatements = Options._MaxStatements;
-            if (maxStatements > 0 && _statementsCount++ > maxStatements)
+            if (_maxStatements > 0 && _statementsCount++ > _maxStatements)
             {
-                throw new StatementsCountOverflowException();
+                ThrowStatementsCountOverflowException();
             }
 
             if (_timeoutTicks > 0 && _timeoutTicks < DateTime.UtcNow.Ticks)
             {
-                throw new TimeoutException();
+                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;
 
-            if (Options._IsDebugMode)
+            if (_isDebugMode)
             {
                 DebugHandler.OnStep(statement);
             }
@@ -380,67 +441,68 @@ namespace Jint
             switch (statement.Type)
             {
                 case Nodes.BlockStatement:
-                    return _statements.ExecuteBlockStatement(statement.As<BlockStatement>());
+                    return _statements.ExecuteBlockStatement((BlockStatement) statement);
+
+                case Nodes.ReturnStatement:
+                    return _statements.ExecuteReturnStatement((ReturnStatement) statement);
+
+                case Nodes.VariableDeclaration:
+                    return _statements.ExecuteVariableDeclaration((VariableDeclaration) statement);
 
                 case Nodes.BreakStatement:
-                    return _statements.ExecuteBreakStatement(statement.As<BreakStatement>());
+                    return _statements.ExecuteBreakStatement((BreakStatement) statement);
 
                 case Nodes.ContinueStatement:
-                    return _statements.ExecuteContinueStatement(statement.As<ContinueStatement>());
+                    return _statements.ExecuteContinueStatement((ContinueStatement) statement);
 
                 case Nodes.DoWhileStatement:
-                    return _statements.ExecuteDoWhileStatement(statement.As<DoWhileStatement>());
-
-                case Nodes.DebuggerStatement:
-                    return _statements.ExecuteDebuggerStatement(statement.As<DebuggerStatement>());
+                    return _statements.ExecuteDoWhileStatement((DoWhileStatement) statement);
 
                 case Nodes.EmptyStatement:
-                    return _statements.ExecuteEmptyStatement(statement.As<EmptyStatement>());
+                    return _statements.ExecuteEmptyStatement((EmptyStatement) statement);
 
                 case Nodes.ExpressionStatement:
-                    return _statements.ExecuteExpressionStatement(statement.As<ExpressionStatement>());
+                    return _statements.ExecuteExpressionStatement((ExpressionStatement) statement);
 
                 case Nodes.ForStatement:
-                    return _statements.ExecuteForStatement(statement.As<ForStatement>());
+                    return _statements.ExecuteForStatement((ForStatement) statement);
 
                 case Nodes.ForInStatement:
-                    return _statements.ExecuteForInStatement(statement.As<ForInStatement>());
-
-                case Nodes.FunctionDeclaration:
-                    return Completion.Empty;
+                    return _statements.ExecuteForInStatement((ForInStatement) statement);
 
                 case Nodes.IfStatement:
-                    return _statements.ExecuteIfStatement(statement.As<IfStatement>());
+                    return _statements.ExecuteIfStatement((IfStatement) statement);
 
                 case Nodes.LabeledStatement:
-                    return _statements.ExecuteLabeledStatement(statement.As<LabeledStatement>());
-
-                case Nodes.ReturnStatement:
-                    return _statements.ExecuteReturnStatement(statement.As<ReturnStatement>());
+                    return _statements.ExecuteLabeledStatement((LabeledStatement) statement);
 
                 case Nodes.SwitchStatement:
-                    return _statements.ExecuteSwitchStatement(statement.As<SwitchStatement>());
+                    return _statements.ExecuteSwitchStatement((SwitchStatement) statement);
+
+                case Nodes.FunctionDeclaration:
+                    return new Completion(CompletionType.Normal, null, null);
 
                 case Nodes.ThrowStatement:
-                    return _statements.ExecuteThrowStatement(statement.As<ThrowStatement>());
+                    return _statements.ExecuteThrowStatement((ThrowStatement) statement);
 
                 case Nodes.TryStatement:
-                    return _statements.ExecuteTryStatement(statement.As<TryStatement>());
-
-                case Nodes.VariableDeclaration:
-                    return _statements.ExecuteVariableDeclaration(statement.As<VariableDeclaration>());
+                    return _statements.ExecuteTryStatement((TryStatement) statement);
 
                 case Nodes.WhileStatement:
-                    return _statements.ExecuteWhileStatement(statement.As<WhileStatement>());
+                    return _statements.ExecuteWhileStatement((WhileStatement) statement);
 
                 case Nodes.WithStatement:
-                    return _statements.ExecuteWithStatement(statement.As<WithStatement>());
+                    return _statements.ExecuteWithStatement((WithStatement) statement);
+
+                case Nodes.DebuggerStatement:
+                    return _statements.ExecuteDebuggerStatement((DebuggerStatement) statement);
 
                 case Nodes.Program:
-                    return _statements.ExecuteProgram(statement.As<Program>());
+                    return _statements.ExecuteProgram((Program) statement);
 
                 default:
-                    throw new ArgumentOutOfRangeException();
+                    ThrowArgumentOutOfRange();
+                    return new Completion(CompletionType.Normal, null, null);
             }
         }
 
@@ -451,67 +513,83 @@ namespace Jint
             switch (expression.Type)
             {
                 case Nodes.AssignmentExpression:
-                    return _expressions.EvaluateAssignmentExpression(expression.As<AssignmentExpression>());
+                    return _expressions.EvaluateAssignmentExpression((AssignmentExpression) expression);
 
                 case Nodes.ArrayExpression:
-                    return _expressions.EvaluateArrayExpression(expression.As<ArrayExpression>());
+                    return _expressions.EvaluateArrayExpression((ArrayExpression) expression);
 
                 case Nodes.BinaryExpression:
-                    return _expressions.EvaluateBinaryExpression(expression.As<BinaryExpression>());
+                    return _expressions.EvaluateBinaryExpression((BinaryExpression) expression);
 
                 case Nodes.CallExpression:
-                    return _expressions.EvaluateCallExpression(expression.As<CallExpression>());
+                    return _expressions.EvaluateCallExpression((CallExpression) expression);
 
                 case Nodes.ConditionalExpression:
-                    return _expressions.EvaluateConditionalExpression(expression.As<ConditionalExpression>());
+                    return _expressions.EvaluateConditionalExpression((ConditionalExpression) expression);
 
                 case Nodes.FunctionExpression:
-                    return _expressions.EvaluateFunctionExpression(expression.As<IFunction>());
+                    return _expressions.EvaluateFunctionExpression((IFunction) expression);
 
                 case Nodes.Identifier:
-                    return _expressions.EvaluateIdentifier(expression.As<Identifier>());
+                    return _expressions.EvaluateIdentifier((Identifier) expression);
 
                 case Nodes.Literal:
-                    return _expressions.EvaluateLiteral(expression.As<Literal>());
+                    return _expressions.EvaluateLiteral((Literal) expression);
 
                 case Nodes.LogicalExpression:
-                    return _expressions.EvaluateLogicalExpression(expression.As<BinaryExpression>());
+                    return _expressions.EvaluateLogicalExpression((BinaryExpression) expression);
 
                 case Nodes.MemberExpression:
-                    return _expressions.EvaluateMemberExpression(expression.As<MemberExpression>());
+                    return _expressions.EvaluateMemberExpression((MemberExpression) expression);
 
                 case Nodes.NewExpression:
-                    return _expressions.EvaluateNewExpression(expression.As<NewExpression>());
+                    return _expressions.EvaluateNewExpression((NewExpression) expression);
 
                 case Nodes.ObjectExpression:
-                    return _expressions.EvaluateObjectExpression(expression.As<ObjectExpression>());
+                    return _expressions.EvaluateObjectExpression((ObjectExpression) expression);
 
                 case Nodes.SequenceExpression:
-                    return _expressions.EvaluateSequenceExpression(expression.As<SequenceExpression>());
+                    return _expressions.EvaluateSequenceExpression((SequenceExpression) expression);
 
                 case Nodes.ThisExpression:
-                    return _expressions.EvaluateThisExpression(expression.As<ThisExpression>());
+                    return _expressions.EvaluateThisExpression((ThisExpression) expression);
 
                 case Nodes.UpdateExpression:
-                    return _expressions.EvaluateUpdateExpression(expression.As<UpdateExpression>());
+                    return _expressions.EvaluateUpdateExpression((UpdateExpression) expression);
 
                 case Nodes.UnaryExpression:
-                    return _expressions.EvaluateUnaryExpression(expression.As<UnaryExpression>());
+                    return _expressions.EvaluateUnaryExpression((UnaryExpression) expression);
 
                 default:
-                    throw new ArgumentOutOfRangeException();
+                    ThrowArgumentOutOfRange();
+                    return null;
             }
         }
 
-
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1
         /// </summary>
-        /// <param name="value"></param>
-        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue GetValue(object value)
         {
-            return GetValue(value, false);
+            if (value is JsValue jsValue)
+            {
+                return jsValue;
+            }
+
+            return GetValueFromReference(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)]
@@ -522,6 +600,11 @@ namespace Jint
                 return jsValue;
             }
 
+            return GetValueFromReference(value, returnReferenceToPool);
+        }
+       
+        internal JsValue GetValueFromReference(object value, bool returnReferenceToPool)
+        {
             var reference = value as Reference;
             if (reference == null)
             {
@@ -533,8 +616,8 @@ namespace Jint
 
             if (reference.IsUnresolvableReference())
             {
-                if (Options._ReferenceResolver != null &&
-                    Options._ReferenceResolver.TryUnresolvableReference(this, reference, out JsValue val))
+                if (_referenceResolver != null &&
+                    _referenceResolver.TryUnresolvableReference(this, reference, out JsValue val))
                 {
                     return val;
                 }
@@ -545,8 +628,8 @@ namespace Jint
 
             if (reference.IsPropertyReference())
             {
-                if (Options._ReferenceResolver != null &&
-                    Options._ReferenceResolver.TryPropertyReference(this, reference, ref baseValue))
+                if (_referenceResolver != null &&
+                    _referenceResolver.TryPropertyReference(this, reference, ref baseValue))
                 {
                     return baseValue;
                 }
@@ -577,7 +660,7 @@ namespace Jint
                     }
 
                     var getter = desc.Get;
-                    if (ReferenceEquals(getter, Undefined.Instance))
+                    if (getter.IsUndefined())
                     {
                         return Undefined.Instance;
                     }
@@ -588,7 +671,7 @@ namespace Jint
             }
 
             var record = (EnvironmentRecord) baseValue;
-            if (record == null)
+            if (ReferenceEquals(record, null))
             {
                 throw new ArgumentException();
             }
@@ -614,7 +697,7 @@ namespace Jint
             {
                 if (reference.IsStrict())
                 {
-                    throw new JavaScriptException(ReferenceError);
+                    ThrowReferenceError();
                 }
 
                 Global.Put(reference.GetReferencedName(), value, false);
@@ -634,11 +717,10 @@ namespace Jint
             else
             {
                 var baseValue = reference.GetBase();
-                var record = baseValue as EnvironmentRecord;
-
-                if (record == null)
+                if (!(baseValue is EnvironmentRecord record))
                 {
-                    throw new ArgumentNullException();
+                    ThrowArgumentNullException();
+                    return;
                 }
 
                 record.SetMutableBinding(reference.GetReferencedName(), value, reference.IsStrict());
@@ -648,10 +730,6 @@ namespace Jint
         /// <summary>
         /// Used by PutValue when the reference has a primitive base value
         /// </summary>
-        /// <param name="b"></param>
-        /// <param name="name"></param>
-        /// <param name="value"></param>
-        /// <param name="throwOnError"></param>
         public void PutPrimitiveBase(JsValue b, string name, JsValue value, bool throwOnError)
         {
             var o = TypeConverter.ToObject(this, b);
@@ -659,9 +737,8 @@ namespace Jint
             {
                 if (throwOnError)
                 {
-                    throw new JavaScriptException(TypeError);
+                    ThrowTypeError();
                 }
-
                 return;
             }
 
@@ -671,9 +748,8 @@ namespace Jint
             {
                 if (throwOnError)
                 {
-                    throw new JavaScriptException(TypeError);
+                    ThrowTypeError();
                 }
-
                 return;
             }
 
@@ -688,7 +764,7 @@ namespace Jint
             {
                 if (throwOnError)
                 {
-                    throw new JavaScriptException(TypeError);
+                    ThrowTypeError();
                 }
             }
         }
@@ -745,12 +821,16 @@ namespace Jint
                 throw new ArgumentException("Can only invoke functions");
             }
 
-            var items = new JsValue[arguments.Length];
+            var items = JsValueArrayPool.RentArray(arguments.Length);
             for (int i = 0; i < arguments.Length; ++i)
             {
                 items[i] = JsValue.FromObject(this, arguments[i]);
             }
-            return callable.Call(JsValue.FromObject(this, thisObj), items);
+
+            var result = callable.Call(JsValue.FromObject(this, thisObj), items);
+            JsValueArrayPool.ReturnArray(items);
+
+            return result;
         }
 
         /// <summary>
@@ -777,23 +857,19 @@ namespace Jint
         /// <param name="propertyName">The name of the property to return.</param>
         public JsValue GetValue(JsValue scope, string propertyName)
         {
-            if (string.IsNullOrEmpty(propertyName))
-            {
-                throw new ArgumentException("propertyName");
-            }
+            AssertNotNullOrEmpty(nameof(propertyName), propertyName);
 
-            var reference = ReferencePool.Rent(scope, propertyName, Options._IsStrict);
+            var reference = ReferencePool.Rent(scope, propertyName, _isStrict);
             var jsValue = GetValue(reference);
             ReferencePool.Return(reference);
-
             return jsValue;
         }
 
         //  http://www.ecma-international.org/ecma-262/5.1/#sec-10.5
         internal bool DeclarationBindingInstantiation(
             DeclarationBindingType declarationBindingType,
-            IList<FunctionDeclaration> functionDeclarations,
-            IList<VariableDeclaration> variableDeclarations,
+            List<FunctionDeclaration> functionDeclarations,
+            List<VariableDeclaration> variableDeclarations,
             FunctionInstance functionInstance,
             JsValue[] arguments)
         {
@@ -803,13 +879,11 @@ namespace Jint
 
             if (declarationBindingType == DeclarationBindingType.FunctionCode)
             {
-                var argCount = arguments.Length;
-                var n = 0;
-                for (var i = 0; i < functionInstance.FormalParameters.Length; i++)
+                var parameters = functionInstance.FormalParameters;
+                for (var i = 0; i < parameters.Length; i++)
                 {
-                    var argName = functionInstance.FormalParameters[i];
-                    n++;
-                    var v = n > argCount ? Undefined.Instance : arguments[n - 1];
+                    var argName = parameters[i];
+                    var v = i + 1 > arguments.Length ? Undefined.Instance : arguments[i];
                     var argAlreadyDeclared = env.HasBinding(argName);
                     if (!argAlreadyDeclared)
                     {
@@ -820,7 +894,8 @@ namespace Jint
                 }
             }
 
-            for (var i = 0; i < functionDeclarations.Count; i++)
+            var functionDeclarationsCount = functionDeclarations.Count;
+            for (var i = 0; i < functionDeclarationsCount; i++)
             {
                 var f = functionDeclarations[i];
                 var fn = f.Id.Name;
@@ -832,23 +907,24 @@ namespace Jint
                 }
                 else
                 {
-                    if (env == GlobalEnvironment.Record)
+                    if (ReferenceEquals(env, GlobalEnvironment.Record))
                     {
                         var go = Global;
                         var existingProp = go.GetProperty(fn);
-                        if (existingProp.Configurable.Value)
+                        if (existingProp.Configurable)
                         {
-                            go.DefineOwnProperty(fn,
-                                new PropertyDescriptor(
-                                    value: Undefined.Instance,
-                                    writable: true,
-                                    enumerable: true,
-                                    configurable: configurableBindings
-                                ), true);
+                            var flags = PropertyFlag.Writable | PropertyFlag.Enumerable;
+                            if (configurableBindings)
+                            {
+                                flags |= PropertyFlag.Configurable;
+                            }
+
+                            var descriptor = new PropertyDescriptor(Undefined.Instance, flags);
+                            go.DefineOwnProperty(fn, descriptor, true);
                         }
                         else
                         {
-                            if (existingProp.IsAccessorDescriptor() || (!existingProp.Enumerable.Value))
+                            if (existingProp.IsAccessorDescriptor() || !existingProp.Enumerable)
                             {
                                 throw new JavaScriptException(TypeError);
                             }
@@ -869,7 +945,7 @@ namespace Jint
                 {
                     var declEnv = env as DeclarativeEnvironmentRecord;
 
-                    if (declEnv == null)
+                    if (ReferenceEquals(declEnv, null))
                     {
                         throw new ArgumentException();
                     }
@@ -891,7 +967,7 @@ namespace Jint
                 for (var j = 0; j < declarationsCount; j++)
                 {
                     var d = variableDeclaration.Declarations[j];
-                    var dn = d.Id.As<Identifier>().Name;
+                    var dn = ((Identifier) d.Id).Name;
                     var varAlreadyDeclared = env.HasBinding(dn);
                     if (!varAlreadyDeclared)
                     {
@@ -902,5 +978,49 @@ namespace Jint
 
             return canReleaseArgumentsInstance;
         }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void UpdateLexicalEnvironment(LexicalEnvironment 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)
+        {
+            if (string.IsNullOrEmpty(propertyValue))
+            {
+                throw new ArgumentException(propertyname);
+            }
+        }
+        
+        private void ThrowTypeError()
+        {
+            throw new JavaScriptException(TypeError);
+        }
     }
-}
+}

+ 3 - 2
Jint/Jint.csproj

@@ -4,8 +4,9 @@
     <TargetFramework>netstandard2.0</TargetFramework>
     <AssemblyOriginatorKeyFile>Jint.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
+    <LangVersion>latest</LangVersion>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Esprima" Version="1.0.0-beta-1026" />
+    <PackageReference Include="Esprima" Version="1.0.0-beta-1036" />
   </ItemGroup>
-</Project>
+</Project>

+ 65 - 0
Jint/JsValueExtensions.cs

@@ -0,0 +1,65 @@
+using System;
+using System.Runtime.CompilerServices;
+using Jint.Native;
+using Jint.Runtime;
+
+namespace Jint
+{
+    public static class JsValueExtensions
+    {
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool AsBoolean(this JsValue value)
+        {
+            if (value._type != Types.Boolean)
+            {
+                ThrowWrongTypeException("The value is not a boolean");
+            }
+
+            return ((JsBoolean) value)._value;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static double AsNumber(this JsValue value)
+        {
+            if (value._type != Types.Number)
+            {
+                ThrowWrongTypeException("The value is not a number");
+            }
+
+            return ((JsNumber) value)._value;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static string AsString(this JsValue value)
+        {
+            if (value._type != Types.String)
+            {
+                ThrowWrongTypeException("The value is not a string");
+            }
+
+            return AsStringWithoutTypeCheck(value);
+        }
+        
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static string AsStringWithoutTypeCheck(this JsValue value)
+        {
+            return value.ToString();
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static string AsSymbol(this JsValue value)
+        {
+            if (value._type != Types.Symbol)
+            {
+                ThrowWrongTypeException("The value is not a symbol");
+            }
+
+            return ((JsSymbol) value)._value;
+        }
+        
+        private static void ThrowWrongTypeException(string message)
+        {
+            throw new ArgumentException(message);
+        }
+    }
+}

+ 18 - 18
Jint/Native/Argument/ArgumentsInstance.cs

@@ -25,7 +25,7 @@ namespace Jint.Native.Argument
 
         private bool _initialized;
 
-        internal ArgumentsInstance(Engine engine) : base(engine)
+        internal ArgumentsInstance(Engine engine) : base(engine, objectClass: "Arguments")
         {
         }
 
@@ -55,7 +55,7 @@ namespace Jint.Native.Argument
 
             var self = this;
             var len = _args.Length;
-            self.SetOwnProperty("length", new NonEnumerablePropertyDescriptor(len));
+            self.SetOwnProperty("length", new PropertyDescriptor(len, PropertyFlag.NonEnumerable));
             if (_args.Length > 0)
             {
                 var map = Engine.Object.Construct(Arguments.Empty);
@@ -65,7 +65,7 @@ namespace Jint.Native.Argument
                 {
                     var indxStr = TypeConverter.ToString(indx);
                     var val = _args[indx];
-                    self.SetOwnProperty(indxStr, new ConfigurableEnumerableWritablePropertyDescriptor(val));
+                    self.SetOwnProperty(indxStr, new PropertyDescriptor(val, PropertyFlag.ConfigurableEnumerableWritable));
                     if (indx < _names.Length)
                     {
                         var name = _names[indx];
@@ -87,26 +87,25 @@ namespace Jint.Native.Argument
             // step 13
             if (!_strict)
             {
-                self.SetOwnProperty("callee", new NonEnumerablePropertyDescriptor(_func));
+                self.SetOwnProperty("callee", new PropertyDescriptor(_func, PropertyFlag.NonEnumerable));
             }
             // step 14
             else
             {
                 var thrower = Engine.Function.ThrowTypeError;
-                self.DefineOwnProperty("caller", new PropertyDescriptor(get: thrower, set: thrower, enumerable: false, configurable: false), false);
-                self.DefineOwnProperty("callee", new PropertyDescriptor(get: thrower, set: thrower, enumerable: false, configurable: false), false);
+                const PropertyFlag flags = PropertyFlag.EnumerableSet | PropertyFlag.ConfigurableSet;
+                self.DefineOwnProperty("caller", new GetSetPropertyDescriptor(get: thrower, set: thrower, flags), false);
+                self.DefineOwnProperty("callee", new GetSetPropertyDescriptor(get: thrower, set: thrower, flags), false);
             }
         }
 
         public ObjectInstance ParameterMap { get; set; }
 
-        public override string Class => "Arguments";
-
-        public override IPropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(string propertyName)
         {
             EnsureInitialized();
 
-            if (!Strict && ParameterMap != null)
+            if (!Strict && !ReferenceEquals(ParameterMap, null))
             {
                 var desc = base.GetOwnProperty(propertyName);
                 if (desc == PropertyDescriptor.Undefined)
@@ -147,7 +146,7 @@ namespace Jint.Native.Argument
 
             if (ownDesc.IsDataDescriptor())
             {
-                var valueDesc = new NullConfigurationPropertyDescriptor(value);
+                var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
                 DefineOwnProperty(propertyName, valueDesc, throwOnError);
                 return;
             }
@@ -162,16 +161,16 @@ namespace Jint.Native.Argument
             }
             else
             {
-                var newDesc = new ConfigurableEnumerableWritablePropertyDescriptor(value);
+                var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
                 DefineOwnProperty(propertyName, newDesc, throwOnError);
             }
         }
 
-        public override bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             EnsureInitialized();
 
-            if (!Strict && ParameterMap != null)
+            if (!Strict && !ReferenceEquals(ParameterMap, null))
             {
                 var map = ParameterMap;
                 var isMapped = map.GetOwnProperty(propertyName);
@@ -192,12 +191,13 @@ namespace Jint.Native.Argument
                     }
                     else
                     {
-                        if (desc.Value != null && desc.Value != Undefined)
+                        var descValue = desc.Value;
+                        if (!ReferenceEquals(descValue, null) && !descValue.IsUndefined())
                         {
-                            map.Put(propertyName, desc.Value, throwOnError);
+                            map.Put(propertyName, descValue, throwOnError);
                         }
 
-                        if (desc.Writable.HasValue && desc.Writable.Value == false)
+                        if (desc.WritableSet && !desc.Writable)
                         {
                             map.Delete(propertyName, false);
                         }
@@ -214,7 +214,7 @@ namespace Jint.Native.Argument
         {
             EnsureInitialized();
 
-            if (!Strict && ParameterMap != null)
+            if (!Strict && !ReferenceEquals(ParameterMap, null))
             {
                 var map = ParameterMap;
                 var isMapped = map.GetOwnProperty(propertyName);

+ 17 - 9
Jint/Native/Array/ArrayConstructor.cs

@@ -3,7 +3,7 @@ using System.Collections;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Array
@@ -25,17 +25,17 @@ namespace Jint.Native.Array
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = ArrayPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
+            obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));
 
             // The initial value of Array.prototype is the Array prototype object
-            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
 
             return obj;
         }
 
         public void Configure()
         {
-            SetOwnProperty("isArray", new NonEnumerablePropertyDescriptor(new ClrFunctionInstance(Engine, IsArray, 1)));
+            SetOwnProperty("isArray", new PropertyDescriptor(new ClrFunctionInstance(Engine, IsArray, 1), PropertyFlag.NonEnumerable));
         }
 
         private static JsValue IsArray(JsValue thisObj, JsValue[] arguments)
@@ -61,7 +61,7 @@ namespace Jint.Native.Array
             var capacity = arguments.Length > 0 ? (uint) arguments.Length : 0;
             if (arguments.Length == 1 && arguments[0].Type == Types.Number)
             {
-                var number = arguments[0].AsNumber();
+                var number = ((JsNumber) arguments[0])._value;
                 if (number > 0)
                 {
                     capacity = (uint) number;
@@ -98,27 +98,27 @@ namespace Jint.Native.Array
                     throw new JavaScriptException(Engine.RangeError, "Invalid array length");
                 }
 
-                instance.SetOwnProperty("length", new WritablePropertyDescriptor(length));
+                instance.SetOwnProperty("length", new PropertyDescriptor(length, PropertyFlag.OnlyWritable));
             }
             else if (arguments.Length == 1 && arguments[0] is ObjectWrapper objectWrapper)
             {
                 if (objectWrapper.Target is IEnumerable enumerable)
                 {
                     var jsArray = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
-                    var tempArray = new JsValue[1];
+                    var tempArray = Engine.JsValueArrayPool.RentArray(1);
                     foreach (var item in enumerable)
                     {
                         var jsItem = FromObject(Engine, item);
                         tempArray[0] = jsItem;
                         Engine.Array.PrototypeObject.Push(jsArray, tempArray);
                     }
-
+                    Engine.JsValueArrayPool.ReturnArray(tempArray);
                     return jsArray;
                 }
             }
             else
             {
-                instance.SetOwnProperty("length", new WritablePropertyDescriptor(0));
+                instance.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.OnlyWritable));
                 if (arguments.Length > 0)
                 {
                     PrototypeObject.Push(instance, arguments);
@@ -128,5 +128,13 @@ namespace Jint.Native.Array
             return instance;
         }
 
+        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));
+            return instance;
+        }
     }
 }

+ 263 - 83
Jint/Native/Array/ArrayInstance.cs

@@ -1,41 +1,64 @@
-using System.Collections.Generic;
+using System.Collections;
+using System.Collections.Generic;
 using System.Runtime.CompilerServices;
+
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
-using Jint.Runtime.Descriptors.Specialized;
+
 using PropertyDescriptor = Jint.Runtime.Descriptors.PropertyDescriptor;
 using TypeConverter = Jint.Runtime.TypeConverter;
 
 namespace Jint.Native.Array
 {
-    public class ArrayInstance : ObjectInstance
+    public class ArrayInstance : ObjectInstance, IEnumerable<JsValue>
     {
-        private readonly Engine _engine;
-
         private const string PropertyNameLength = "length";
-        private IPropertyDescriptor _length;
+        private const int PropertyNameLengthLength = 6;
+
+        private PropertyDescriptor _length;
 
         private const int MaxDenseArrayLength = 1024 * 10;
 
         // we have dense and sparse, we usually can start with dense and fall back to sparse when necessary
-        private IPropertyDescriptor[] _dense;
-        private Dictionary<uint, IPropertyDescriptor> _sparse;
+        private PropertyDescriptor[] _dense;
+        private Dictionary<uint, PropertyDescriptor> _sparse;
 
-        public ArrayInstance(Engine engine, uint capacity = 0) : base(engine)
+        public ArrayInstance(Engine engine, uint capacity = 0) : base(engine, objectClass: "Array")
         {
-            _engine = engine;
             if (capacity < MaxDenseArrayLength)
             {
-                _dense = capacity > 0 ? new IPropertyDescriptor[capacity] : System.Array.Empty<IPropertyDescriptor>();
+                _dense = capacity > 0 ? new PropertyDescriptor[capacity] : System.Array.Empty<PropertyDescriptor>();
             }
             else
             {
-                _sparse = new Dictionary<uint, IPropertyDescriptor>((int) (capacity <= 1024 ? capacity : 1024));
+                _sparse = new Dictionary<uint, PropertyDescriptor>((int) (capacity <= 1024 ? capacity : 1024));
+            }
+        }
+
+        public ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine, objectClass: "Array")
+        {
+            int length = 0;
+            if (items == null || items.Length == 0)
+            {
+                _dense = System.Array.Empty<PropertyDescriptor>();
+                length = 0;
             }
+            else
+            {
+                _dense = items;
+                length = items.Length;
+            }            
+            
+            _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
         }
 
-        public override string Class => "Array";
+        public ArrayInstance(Engine engine, Dictionary<uint, PropertyDescriptor> items) : base(engine, objectClass: "Array")
+        {
+            _sparse = items;
+            var length = items?.Count ?? 0;
+            _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
+        }
 
         /// Implementation from ObjectInstance official specs as the one
         /// in ObjectInstance is optimized for the general case and wouldn't work
@@ -56,7 +79,7 @@ namespace Jint.Native.Array
 
             if (ownDesc.IsDataDescriptor())
             {
-                var valueDesc = new NullConfigurationPropertyDescriptor(value);
+                var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
                 DefineOwnProperty(propertyName, valueDesc, throwOnError);
                 return;
             }
@@ -71,26 +94,27 @@ namespace Jint.Native.Array
             }
             else
             {
-                var newDesc = new ConfigurableEnumerableWritablePropertyDescriptor(value);
+                var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
                 DefineOwnProperty(propertyName, newDesc, throwOnError);
             }
         }
 
-        public override bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             var oldLenDesc = _length;
             var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc.Value);
 
-            if (propertyName == "length")
+            if (propertyName.Length == 6 && propertyName == "length")
             {
-                if (desc.Value == null)
+                var value = desc.Value;
+                if (ReferenceEquals(value, null))
                 {
                     return base.DefineOwnProperty("length", desc, throwOnError);
                 }
 
                 var newLenDesc = new PropertyDescriptor(desc);
-                uint newLen = TypeConverter.ToUint32(desc.Value);
-                if (newLen != TypeConverter.ToNumber(desc.Value))
+                uint newLen = TypeConverter.ToUint32(value);
+                if (newLen != TypeConverter.ToNumber(value))
                 {
                     throw new JavaScriptException(_engine.RangeError);
                 }
@@ -101,7 +125,7 @@ namespace Jint.Native.Array
                     return base.DefineOwnProperty("length", newLenDesc, throwOnError);
                 }
 
-                if (!oldLenDesc.Writable.Value)
+                if (!oldLenDesc.Writable)
                 {
                     if (throwOnError)
                     {
@@ -112,7 +136,7 @@ namespace Jint.Native.Array
                 }
 
                 bool newWritable;
-                if (!newLenDesc.Writable.HasValue || newLenDesc.Writable.Value)
+                if (!newLenDesc.WritableSet || newLenDesc.Writable)
                 {
                     newWritable = true;
                 }
@@ -128,22 +152,7 @@ namespace Jint.Native.Array
                     return false;
                 }
 
-                int count = 0;
-                if (_dense != null)
-                {
-                    for (int i = 0; i < _dense.Length; ++i)
-                    {
-                        if (_dense[i] != null)
-                        {
-                            count++;
-                        }
-                    }
-                }
-                else
-                {
-                    count = _sparse.Count;
-                }
-
+                var count = _dense?.Length ?? _sparse.Count;
                 if (count < oldLen - newLen)
                 {
                     if (_dense != null)
@@ -158,7 +167,7 @@ namespace Jint.Native.Array
                             // is it the index of the array
                             if (keyIndex >= newLen && keyIndex < oldLen)
                             {
-                                var deleteSucceeded = Delete(TypeConverter.ToString(keyIndex), false);
+                                var deleteSucceeded = DeleteAt(keyIndex);
                                 if (!deleteSucceeded)
                                 {
                                     newLenDesc.Value = keyIndex + 1;
@@ -188,7 +197,7 @@ namespace Jint.Native.Array
                         for (var i = 0; i < keysCount; i++)
                         {
                             var keyIndex = keys[i];
-                            
+
                             // is it the index of the array
                             if (keyIndex >= newLen && keyIndex < oldLen)
                             {
@@ -243,14 +252,14 @@ namespace Jint.Native.Array
 
                 if (!newWritable)
                 {
-                    DefineOwnProperty("length", new PropertyDescriptor(value: null, writable: false, enumerable: null, configurable: null), false);
+                    DefineOwnProperty("length", new PropertyDescriptor(value: null, PropertyFlag.WritableSet), false);
                 }
 
                 return true;
             }
             else if (IsArrayIndex(propertyName, out var index))
             {
-                if (index >= oldLen && !oldLenDesc.Writable.Value)
+                if (index >= oldLen && !oldLenDesc.Writable)
                 {
                     if (throwOnError)
                     {
@@ -283,45 +292,49 @@ namespace Jint.Native.Array
             return base.DefineOwnProperty(propertyName, desc, throwOnError);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public uint GetLength()
         {
-            return TypeConverter.ToUint32(_length.Value);
+            return (uint) ((JsNumber) _length.Value)._value;
         }
 
-        protected override void AddProperty(string propertyName, IPropertyDescriptor descriptor)
+        protected override void AddProperty(string propertyName, PropertyDescriptor descriptor)
         {
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 _length = descriptor;
                 return;
             }
+
             base.AddProperty(propertyName, descriptor);
         }
 
-        protected override bool TryGetProperty(string propertyName, out IPropertyDescriptor descriptor)
+        protected override bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
         {
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 descriptor = _length;
                 return _length != null;
             }
+
             return base.TryGetProperty(propertyName, out descriptor);
         }
 
-        public override IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
+        public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
         {
             if (_length != null)
             {
-                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameLength, _length);
+                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameLength, _length);
             }
 
             if (_dense != null)
             {
-                for (var i = 0; i < _dense.Length; i++)
+                var length = System.Math.Min(_dense.Length, GetLength());
+                for (var i = 0; i < length; i++)
                 {
                     if (_dense[i] != null)
                     {
-                        yield return new KeyValuePair<string, IPropertyDescriptor>(TypeConverter.ToString(i), _dense[i]);
+                        yield return new KeyValuePair<string, PropertyDescriptor>(TypeConverter.ToString(i), _dense[i]);
                     }
                 }
             }
@@ -329,7 +342,7 @@ namespace Jint.Native.Array
             {
                 foreach (var entry in _sparse)
                 {
-                    yield return new KeyValuePair<string, IPropertyDescriptor>(TypeConverter.ToString(entry.Key), entry.Value);
+                    yield return new KeyValuePair<string, PropertyDescriptor>(TypeConverter.ToString(entry.Key), entry.Value);
                 }
             }
 
@@ -339,7 +352,7 @@ namespace Jint.Native.Array
             }
         }
 
-        public override IPropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(string propertyName)
         {
             if (IsArrayIndex(propertyName, out var index))
             {
@@ -351,7 +364,7 @@ namespace Jint.Native.Array
                 return PropertyDescriptor.Undefined;
             }
 
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 return _length ?? PropertyDescriptor.Undefined;
             }
@@ -359,13 +372,13 @@ namespace Jint.Native.Array
             return base.GetOwnProperty(propertyName);
         }
 
-        protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
+        protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
         {
             if (IsArrayIndex(propertyName, out var index))
             {
                 WriteArrayValue(index, desc);
             }
-            else if (propertyName == PropertyNameLength)
+            else if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 _length = desc;
             }
@@ -381,7 +394,7 @@ namespace Jint.Native.Array
             {
                 return index < GetLength()
                        && (_sparse == null || _sparse.ContainsKey(index))
-                       && (_dense == null || _dense[index] != null);
+                       && (_dense == null || (index < _dense.Length && _dense[index] != null));
             }
 
             if (p == PropertyNameLength)
@@ -394,8 +407,7 @@ namespace Jint.Native.Array
 
         public override void RemoveOwnProperty(string p)
         {
-            uint index;
-            if (IsArrayIndex(p, out index))
+            if (IsArrayIndex(p, out var index))
             {
                 DeleteAt(index);
             }
@@ -438,8 +450,17 @@ namespace Jint.Native.Array
                 return uint.MaxValue;
             }
 
-            ulong result = (uint) d;
+            if (p.Length > 1)
+            {
+                return StringAsIndex(d, p);
+            }
+            
+            return (uint) d;
+        }
 
+        private static uint StringAsIndex(int d, string p)
+        {
+            ulong result = (uint) d;
             for (int i = 1; i < p.Length; i++)
             {
                 d = p[i] - '0';
@@ -460,15 +481,24 @@ namespace Jint.Native.Array
             return (uint) result;
         }
 
-        internal void SetIndexValue(uint index, JsValue value, bool throwOnError)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void SetIndexValue(uint index, JsValue value, bool updateLength)
         {
-            var length = GetLength();
-            if (index >= length)
+            if (updateLength)
             {
-                _length.Value = index + 1;
+                var length = GetLength();
+                if (index >= length)
+                {
+                    SetLength(index + 1);
+                }
             }
+            WriteArrayValue(index, new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
+        }
 
-            WriteArrayValue(index, new ConfigurableEnumerableWritablePropertyDescriptor(value));
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void SetLength(uint length)
+        {
+            _length.Value = length;
         }
 
         internal uint GetSmallestIndex()
@@ -499,7 +529,7 @@ namespace Jint.Native.Array
             if (!TryGetDescriptor(index, out var desc)
                 || desc == null
                 || desc == PropertyDescriptor.Undefined
-                || (desc.Value == null && desc.Get == null))
+                || (ReferenceEquals(desc.Value, null) && ReferenceEquals(desc.Get, null)))
             {
                 desc = GetProperty(TypeConverter.ToString(index));
             }
@@ -513,22 +543,26 @@ namespace Jint.Native.Array
             return false;
         }
 
-        internal void DeleteAt(uint index)
+        internal bool DeleteAt(uint index)
         {
             if (_dense != null)
             {
                 if (index < _dense.Length)
                 {
                     _dense[index] = null;
+                    return true;
                 }
             }
             else
             {
-                _sparse.Remove(index);
+                return _sparse.Remove(index);
             }
+
+            return false;
         }
 
-        private bool TryGetDescriptor(uint index, out IPropertyDescriptor descriptor)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private bool TryGetDescriptor(uint index, out PropertyDescriptor descriptor)
         {
             if (_dense != null)
             {
@@ -545,7 +579,8 @@ namespace Jint.Native.Array
             return _sparse.TryGetValue(index, out descriptor);
         }
 
-        internal void WriteArrayValue(uint index, IPropertyDescriptor desc)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void WriteArrayValue(uint index, PropertyDescriptor desc)
         {
             // calculate eagerly so we know if we outgrow
             var newSize = _dense != null && index >= _dense.Length
@@ -570,32 +605,177 @@ namespace Jint.Native.Array
             {
                 if (_dense != null)
                 {
-                    _sparse = new Dictionary<uint, IPropertyDescriptor>(_dense.Length <= 1024 ? _dense.Length : 0);
-                    // need to move data
-                    for (uint i = 0; i < _dense.Length; ++i)
-                    {
-                        if (_dense[i] != null)
-                        {
-                            _sparse[i] = _dense[i];
-                        }
-                    }
-
-                    _dense = null;
+                    ConvertToSparse();
                 }
-
                 _sparse[index] = desc;
             }
         }
 
+        private void ConvertToSparse()
+        {
+            _sparse = new Dictionary<uint, PropertyDescriptor>(_dense.Length <= 1024 ? _dense.Length : 0);
+            // need to move data
+            for (uint i = 0; i < _dense.Length; ++i)
+            {
+                if (_dense[i] != null)
+                {
+                    _sparse[i] = _dense[i];
+                }
+            }
+
+            _dense = null;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal void EnsureCapacity(uint capacity)
         {
             if (capacity > _dense.Length)
             {
                 // need to grow
-                var newArray = new IPropertyDescriptor[capacity];
+                var newArray = new PropertyDescriptor[capacity];
                 System.Array.Copy(_dense, newArray, _dense.Length);
                 _dense = newArray;
             }
         }
+
+        public IEnumerator<JsValue> GetEnumerator()
+        {
+            var length = GetLength();
+            for (uint i = 0; i < length; i++)
+            {
+                if (TryGetValue(i, out JsValue outValue))
+                {
+                    yield return outValue;
+                }
+            };
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        internal uint Push(JsValue[] arguments)
+        {
+            var initialLength = GetLength();
+            var newLength = initialLength + arguments.Length;
+            
+            // if we see that we are bringing more than normal growth algorithm handles, ensure capacity eagerly
+            if (_dense != null
+                && initialLength != 0
+                && arguments.Length > initialLength * 2
+                && newLength <= MaxDenseArrayLength)
+            {
+                EnsureCapacity((uint) newLength);
+            }
+
+            double n = initialLength;
+            for (var i = 0; i < arguments.Length; i++)
+            {
+                var desc = new PropertyDescriptor(arguments[i], PropertyFlag.ConfigurableEnumerableWritable);
+                if (_dense != null && n < _dense.Length)
+                {
+                    _dense[(int) n] = desc;
+                }
+                else if (n < uint.MaxValue)
+                {
+                    WriteArrayValue((uint) n, desc);
+                }
+                else
+                {
+                    DefineOwnProperty(TypeConverter.ToString(n), desc, true);
+                }
+                n++;
+            }
+
+            // check if we can set length fast without breaking ECMA specification
+            if (n < uint.MaxValue && CanPut(PropertyNameLength))
+            {
+                _length.Value = (uint) n;
+            }
+            else
+            {
+                Put(PropertyNameLength, newLength, true);
+            }
+
+            return (uint) n;
+        }
+
+        internal ArrayInstance Map(JsValue[] arguments)
+        {
+            var callbackfn = arguments.At(0);
+            var thisArg = arguments.At(1);
+
+            var len = GetLength();
+
+            var callable = GetCallable(callbackfn);
+            var a = Engine.Array.ConstructFast(len);
+            var args = Engine.JsValueArrayPool.RentArray(3);
+            for (uint k = 0; k < len; k++)
+            {
+                if (TryGetValue(k, out var kvalue))
+                {
+                    args[0] = kvalue;
+                    args[1] = k;
+                    args[2] = this;
+                    var mappedValue = callable.Call(thisArg, args);
+                    var desc = new PropertyDescriptor(mappedValue, PropertyFlag.ConfigurableEnumerableWritable);
+                    if (a._dense != null && k < a._dense.Length)
+                    {
+                        a._dense[k] = desc;
+                    }
+                    else
+                    {
+                        a.WriteArrayValue(k, desc);
+                    }
+                }
+            }
+
+            Engine.JsValueArrayPool.ReturnArray(args);
+            return a;
+        }
+        
+        /// <inheritdoc />
+        internal override bool FindWithCallback(
+            JsValue[] arguments, 
+            out uint index, 
+            out JsValue value)
+        {
+            var len = GetLength();
+            if (len == 0)
+            {
+                index = 0;
+                value = Undefined;
+                return false;
+            }
+
+            var callbackfn = arguments.At(0);
+            var thisArg = arguments.At(1);
+            var callable = GetCallable(callbackfn);
+
+            var args = Engine.JsValueArrayPool.RentArray(3);
+            for (uint k = 0; k < len; k++)
+            {
+                if (TryGetValue(k, out var kvalue))
+                {
+                    args[0] = kvalue;
+                    args[1] = k;
+                    args[2] = this;
+                    var testResult = callable.Call(thisArg, args);
+                    if (TypeConverter.ToBoolean(testResult))
+                    {
+                        index = k;
+                        value = kvalue;
+                        return true;
+                    }
+                }
+            }
+
+            Engine.JsValueArrayPool.ReturnArray(args);
+
+            index = 0;
+            value = Undefined;
+            return false;
+        }
     }
 }

+ 86 - 88
Jint/Native/Array/ArrayPrototype.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
-using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Array
@@ -26,7 +25,7 @@ namespace Jint.Native.Array
             };
 
             obj.FastAddProperty("length", 0, true, false, false);
-            obj.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(arrayConstructor));
+            obj.SetOwnProperty("constructor", new PropertyDescriptor(arrayConstructor, PropertyFlag.NonEnumerable));
 
             return obj;
         }
@@ -54,6 +53,8 @@ namespace Jint.Native.Array
             FastAddProperty("filter", new ClrFunctionInstance(Engine, Filter, 1), true, false, true);
             FastAddProperty("reduce", new ClrFunctionInstance(Engine, Reduce, 1), true, false, true);
             FastAddProperty("reduceRight", new ClrFunctionInstance(Engine, ReduceRight, 1), true, false, true);
+            FastAddProperty("find", new ClrFunctionInstance(Engine, Find, 1), true, false, true);
+            FastAddProperty("findIndex", new ClrFunctionInstance(Engine, FindIndex, 1), true, false, true);
         }
 
         private JsValue LastIndexOf(JsValue thisObj, JsValue[] arguments)
@@ -172,10 +173,10 @@ namespace Jint.Native.Array
 
             var callable = GetCallable(callbackfn);
 
-            var a = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
+            var a = Engine.Array.ConstructFast(0);
 
             uint to = 0;
-            var args = new JsValue[3];
+            var args = Engine.JsValueArrayPool.RentArray(3);
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -186,27 +187,38 @@ namespace Jint.Native.Array
                     var selected = callable.Call(thisArg, args);
                     if (TypeConverter.ToBoolean(selected))
                     {
-                        a.SetIndexValue(to, kvalue, throwOnError: false);
+                        a.SetIndexValue(to, kvalue, updateLength: false);
                         to++;
                     }
                 }
             }
 
+            a.SetLength(to);
+            Engine.JsValueArrayPool.ReturnArray(args);
+
             return a;
         }
 
         private JsValue Map(JsValue thisObj, JsValue[] arguments)
         {
-            var callbackfn = arguments.At(0);
-            var thisArg = arguments.At(1);
+            if (thisObj is ArrayInstance arrayInstance)
+            {
+                return arrayInstance.Map(arguments);
+            }
 
             var o = ArrayOperations.For(Engine, thisObj);
             var len = o.GetLength();
 
+            var callbackfn = arguments.At(0);
+            var thisArg = arguments.At(1);
             var callable = GetCallable(callbackfn);
 
-            var a = Engine.Array.Construct(new JsValue[] {len}, len);
-            var args = new JsValue[3];
+            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);
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -215,10 +227,13 @@ namespace Jint.Native.Array
                     args[1] = k;
                     args[2] = o.Target;
                     var mappedValue = callable.Call(thisArg, args);
-                    a.SetIndexValue(k, mappedValue, throwOnError: false);
+                    a.SetIndexValue(k, mappedValue, updateLength: false);
                 }
             }
 
+            a.SetLength(len);
+            Engine.JsValueArrayPool.ReturnArray(args);
+
             return a;
         }
 
@@ -232,7 +247,7 @@ namespace Jint.Native.Array
 
             var callable = GetCallable(callbackfn);
 
-            var args = new JsValue[3];
+            var args = Engine.JsValueArrayPool.RentArray(3);
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -243,37 +258,15 @@ namespace Jint.Native.Array
                     callable.Call(thisArg, args);
                 }
             }
+            Engine.JsValueArrayPool.ReturnArray(args);
 
             return Undefined;
         }
 
         private JsValue Some(JsValue thisObj, JsValue[] arguments)
         {
-            var callbackfn = arguments.At(0);
-            var thisArg = arguments.At(1);
-
-            var o = ArrayOperations.For(Engine, thisObj);
-            var len = o.GetLength();
-
-            var callable = GetCallable(callbackfn);
-
-            var args = new JsValue[3];
-            for (uint k = 0; k < len; k++)
-            {
-                if (o.TryGetValue(k, out var kvalue))
-                {
-                    args[0] = kvalue;
-                    args[1] = k;
-                    args[2] = o.Target;
-                    var testResult = callable.Call(thisArg, args);
-                    if (TypeConverter.ToBoolean(testResult))
-                    {
-                        return true;
-                    }
-                }
-            }
-
-            return false;
+            var target = TypeConverter.ToObject(Engine, thisObj);
+            return target.FindWithCallback(arguments, out _, out _);
         }
 
         private JsValue Every(JsValue thisObj, JsValue[] arguments)
@@ -286,7 +279,7 @@ namespace Jint.Native.Array
 
             var callable = GetCallable(callbackfn);
 
-            var args = new JsValue[3];
+            var args = Engine.JsValueArrayPool.RentArray(3);
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -301,6 +294,7 @@ namespace Jint.Native.Array
                     }
                 }
             }
+            Engine.JsValueArrayPool.ReturnArray(args);
 
             return JsBoolean.True;
         }
@@ -364,6 +358,23 @@ namespace Jint.Native.Array
 
             return -1;
         }
+        
+        private JsValue Find(JsValue thisObj, JsValue[] arguments)
+        {
+            var target = TypeConverter.ToObject(Engine, thisObj);
+            target.FindWithCallback(arguments, out _, out var value);
+            return value;
+        }
+        
+        private JsValue FindIndex(JsValue thisObj, JsValue[] arguments)
+        {
+            var target = TypeConverter.ToObject(Engine, thisObj);
+            if (target.FindWithCallback(arguments, out var index, out _))
+            {
+                return index;
+            }
+            return -1;
+        }
 
         private JsValue Splice(JsValue thisObj, JsValue[] arguments)
         {
@@ -385,14 +396,15 @@ namespace Jint.Native.Array
             }
 
             var actualDeleteCount = (uint) System.Math.Min(System.Math.Max(TypeConverter.ToInteger(deleteCount), 0), len - actualStart);
-            var a = Engine.Array.Construct(actualDeleteCount);
+            var a = Engine.Array.ConstructFast(actualDeleteCount);
             for (uint k = 0; k < actualDeleteCount; k++)
             {
                 if (o.TryGetValue(actualStart + k, out var fromValue))
                 {
-                    a.SetIndexValue(k, fromValue, throwOnError: false);
+                    a.SetIndexValue(k, fromValue, updateLength: false);
                 }
             }
+            a.SetLength(actualDeleteCount);
 
             var items = System.Array.Empty<JsValue>();
             if (arguments.Length > 2)
@@ -497,24 +509,26 @@ namespace Jint.Native.Array
 
             var compareArg = arguments.At(0);
             ICallable compareFn = null;
-            if (compareArg != Undefined)
+            if (!compareArg.IsUndefined())
             {
                 compareFn = compareArg.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "The sort argument must be a function"));
             }
 
             int Comparer(JsValue x, JsValue y)
             {
-                if (ReferenceEquals(x, Undefined) && ReferenceEquals(y, Undefined))
+                var xUndefined = x.IsUndefined();
+                var yUndefined = y.IsUndefined();
+                if (xUndefined && yUndefined)
                 {
                     return 0;
                 }
 
-                if (ReferenceEquals(x, Undefined))
+                if (xUndefined)
                 {
                     return 1;
                 }
 
-                if (ReferenceEquals(y, Undefined))
+                if (yUndefined)
                 {
                     return -1;
                 }
@@ -586,7 +600,7 @@ namespace Jint.Native.Array
             }
 
             uint final;
-            if (ReferenceEquals(end, Undefined))
+            if (end.IsUndefined())
             {
                 final = TypeConverter.ToUint32(len);
             }
@@ -609,7 +623,7 @@ namespace Jint.Native.Array
             {
                 if (o.TryGetValue(k, out var kValue))
                 {
-                    a.SetIndexValue(n, kValue, throwOnError: false);
+                    a.SetIndexValue(n, kValue, updateLength: true);
                 }
 
                 n++;
@@ -688,7 +702,7 @@ namespace Jint.Native.Array
             var separator = arguments.At(0);
             var o = ArrayOperations.For(Engine, thisObj);
             var len = o.GetLength();
-            if (ReferenceEquals(separator, Undefined))
+            if (separator.IsUndefined())
             {
                 separator = ",";
             }
@@ -703,7 +717,7 @@ namespace Jint.Native.Array
 
             string StringFromJsValue(JsValue value)
             {
-                return ReferenceEquals(value, Undefined) || ReferenceEquals(value, Null)
+                return value.IsUndefined() || value.IsNull()
                     ? ""
                     : TypeConverter.ToString(value);
             }
@@ -737,7 +751,7 @@ namespace Jint.Native.Array
             }
 
             JsValue r;
-            if (!array.TryGetValue(0, out var firstElement) || ReferenceEquals(firstElement, Null) || ReferenceEquals(firstElement, Undefined))
+            if (!array.TryGetValue(0, out var firstElement) || firstElement.IsNull() || firstElement.IsUndefined())
             {
                 r = "";
             }
@@ -752,7 +766,7 @@ namespace Jint.Native.Array
             for (uint k = 1; k < len; k++)
             {
                 string s = r + separator;
-                if (!array.TryGetValue(k, out var nextElement) || ReferenceEquals(nextElement, Null))
+                if (!array.TryGetValue(k, out var nextElement) || nextElement.IsNull())
                 {
                     r = "";
                 }
@@ -778,24 +792,24 @@ namespace Jint.Native.Array
 
             // try to find best capacity
             uint capacity = 0;
-            foreach (var e in items)
+            for (var i = 0; i < items.Count; i++)
             {
-                var eArray = e.TryCast<ArrayInstance>();
+                var eArray = items[i] as ArrayInstance;
                 capacity += eArray?.GetLength() ?? (uint) 1;
             }
 
-            var a = Engine.Array.Construct(Arguments.Empty, capacity);
-            foreach (var e in items)
+            var a = Engine.Array.ConstructFast(capacity);
+            for (var i = 0; i < items.Count; i++)
             {
-                var eArray = e.TryCast<ArrayInstance>();
-                if (eArray != null)
+                var e = items[i];
+                if (e is ArrayInstance eArray)
                 {
                     var len = eArray.GetLength();
                     for (uint k = 0; k < len; k++)
                     {
                         if (eArray.TryGetValue(k, out var subElement))
                         {
-                            a.SetIndexValue(n, subElement, throwOnError: false);
+                            a.SetIndexValue(n, subElement, updateLength: false);
                         }
 
                         n++;
@@ -803,14 +817,14 @@ namespace Jint.Native.Array
                 }
                 else
                 {
-                    a.SetIndexValue(n, e, throwOnError: false);
+                    a.SetIndexValue(n, e, updateLength: false);
                     n++;
                 }
             }
 
             // this is not in the specs, but is necessary in case the last element of the last
             // array doesn't exist, and thus the length would not be incremented
-            a.DefineOwnProperty("length", new NullConfigurationPropertyDescriptor(n), false);
+            a.DefineOwnProperty("length", new PropertyDescriptor(n, PropertyFlag.None), false);
 
             return a;
         }
@@ -883,29 +897,23 @@ namespace Jint.Native.Array
 
         public JsValue Push(JsValue thisObject, JsValue[] arguments)
         {
-            var o = ArrayOperations.For(Engine, thisObject);
-            var lenVal = TypeConverter.ToNumber(o.Target.Get("length"));
+            if (thisObject is ArrayInstance arrayInstance)
+            {
+                return arrayInstance.Push(arguments);
+            }
+
+            var o = TypeConverter.ToObject(Engine, thisObject);
+            var lenVal = TypeConverter.ToNumber(o.Get("length"));
 
             // cast to double as we need to prevent an overflow
             double n = TypeConverter.ToUint32(lenVal);
-            var arrayInstance = o.Target as ArrayInstance;
             for (var i = 0; i < arguments.Length; i++)
             {
-                JsValue e = arguments[i];
-                if (arrayInstance != null && n >= 0 && n < uint.MaxValue)
-                {
-                    // try to optimize a bit
-                    arrayInstance.SetIndexValue((uint) n, e, true);
-                }
-                else
-                {
-                    o.Target.Put(TypeConverter.ToString(n), e, true);
-                }
+                o.Put(TypeConverter.ToString(n), arguments[i], true);
                 n++;
             }
 
-            o.Target.Put("length", n, true);
-
+            o.Put("length", n, true);
             return n;
         }
 
@@ -929,16 +937,6 @@ namespace Jint.Native.Array
             return element;
         }
         
-        private ICallable GetCallable(JsValue source)
-        {
-            if (source is ICallable callable)
-            {
-                return callable;
-            }
-
-            throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
-        }
-
         /// <summary>
         /// Adapter to use optimized array operations when possible.
         /// Gaps the difference between ArgumensInstance and ArrayInstance.
@@ -995,13 +993,13 @@ namespace Jint.Native.Array
                 public override uint GetLength()
                 {
                     var desc = _instance.GetProperty("length");
-                    if (desc.IsDataDescriptor() && desc.Value != null)
+                    var descValue = desc.Value;
+                    if (desc.IsDataDescriptor() && !ReferenceEquals(descValue, null))
                     {
-                        return TypeConverter.ToUint32(desc.Value);
+                        return TypeConverter.ToUint32(descValue);
                     }
 
-                    var getter = desc.Get != null ? desc.Get : Undefined;
-
+                    var getter = desc.Get ?? Undefined;
                     if (getter.IsUndefined())
                     {
                         return 0;
@@ -1021,7 +1019,7 @@ namespace Jint.Native.Array
                 {
                     var property = TypeConverter.ToString(index);
                     var kPresent = _instance.HasProperty(property);
-                    value = kPresent ? _instance.Get(property) : JsValue.Undefined;
+                    value = kPresent ? _instance.Get(property) : Undefined;
                     return kPresent;
                 }
 

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

@@ -1,7 +1,7 @@
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 
 namespace Jint.Native.Boolean
 {
@@ -20,10 +20,10 @@ namespace Jint.Native.Boolean
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = BooleanPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
+            obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));
 
             // The initial value of Boolean.prototype is the Boolean prototype object
-            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
 
             return obj;
         }

+ 4 - 18
Jint/Native/Boolean/BooleanInstance.cs

@@ -6,28 +6,14 @@ namespace Jint.Native.Boolean
     public class BooleanInstance : ObjectInstance, IPrimitiveInstance
     {
         public BooleanInstance(Engine engine)
-            : base(engine)
+            : base(engine, objectClass: "Boolean")
         {
         }
 
-        public override string Class
-        {
-            get
-            {
-                return "Boolean";
-            }
-        }
+        Types IPrimitiveInstance.Type => Types.Boolean;
 
-        Types IPrimitiveInstance.Type
-        {
-            get { return Types.Boolean; }
-        }
-
-        JsValue IPrimitiveInstance.PrimitiveValue
-        {
-            get { return PrimitiveValue; }
-        }
+        JsValue IPrimitiveInstance.PrimitiveValue => PrimitiveValue;
 
         public JsValue PrimitiveValue { get; set; }
     }
-}
+}

+ 9 - 13
Jint/Native/Boolean/BooleanPrototype.cs

@@ -1,5 +1,5 @@
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Boolean
@@ -20,7 +20,7 @@ namespace Jint.Native.Boolean
             obj.PrimitiveValue = false;
             obj.Extensible = true;
 
-            obj.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(booleanConstructor));
+            obj.SetOwnProperty("constructor", new PropertyDescriptor(booleanConstructor, PropertyFlag.NonEnumerable));
 
             return obj;
         }
@@ -38,24 +38,20 @@ namespace Jint.Native.Boolean
             {
                 return B;
             }
-            else
+
+            var o = B.TryCast<BooleanInstance>();
+            if (!ReferenceEquals(o, null))
             {
-                var o = B.TryCast<BooleanInstance>();
-                if (o != null)
-                {
-                    return o.PrimitiveValue;
-                }
-                else
-                {
-                    throw new JavaScriptException(Engine.TypeError);
-                }
+                return o.PrimitiveValue;
             }
+
+            throw new JavaScriptException(Engine.TypeError);
         }
 
         private JsValue ToBooleanString(JsValue thisObj, JsValue[] arguments)
         {
             var b = ValueOf(thisObj, Arguments.Empty);
-            return b.AsBoolean() ? "true" : "false";
+            return ((JsBoolean) b)._value ? "true" : "false";
         }
     }
 }

+ 48 - 47
Jint/Native/Date/DateConstructor.cs

@@ -3,7 +3,7 @@ using System.Globalization;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Date
@@ -12,6 +12,45 @@ namespace Jint.Native.Date
     {
         internal static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
 
+        private static readonly string[] DefaultFormats = {
+            "yyyy-MM-ddTHH:mm:ss.FFF",
+            "yyyy-MM-ddTHH:mm:ss",
+            "yyyy-MM-ddTHH:mm",
+            "yyyy-MM-dd",
+            "yyyy-MM",
+            "yyyy"
+        };
+
+        private static readonly string[] SecondaryFormats = {
+            // Formats used in DatePrototype toString methods
+            "ddd MMM dd yyyy HH:mm:ss 'GMT'K",
+            "ddd MMM dd yyyy",
+            "HH:mm:ss 'GMT'K",
+
+            // standard formats
+            "yyyy-M-dTH:m:s.FFFK",
+            "yyyy/M/dTH:m:s.FFFK",
+            "yyyy-M-dTH:m:sK",
+            "yyyy/M/dTH:m:sK",
+            "yyyy-M-dTH:mK",
+            "yyyy/M/dTH:mK",
+            "yyyy-M-d H:m:s.FFFK",
+            "yyyy/M/d H:m:s.FFFK",
+            "yyyy-M-d H:m:sK",
+            "yyyy/M/d H:m:sK",
+            "yyyy-M-d H:mK",
+            "yyyy/M/d H:mK",
+            "yyyy-M-dK",
+            "yyyy/M/dK",
+            "yyyy-MK",
+            "yyyy/MK",
+            "yyyyK",
+            "THH:mm:ss.FFFK",
+            "THH:mm:ssK",
+            "THH:mmK",
+            "THHK"
+        };
+
         public DateConstructor(Engine engine) : base(engine, null, null, false)
         {
         }
@@ -25,10 +64,10 @@ namespace Jint.Native.Date
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = DatePrototype.CreatePrototypeObject(engine, obj);
 
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(7));
+            obj.SetOwnProperty("length", new PropertyDescriptor(7, PropertyFlag.AllForbidden));
 
             // The initial value of Date.prototype is the Date prototype object
-            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
 
             return obj;
         }
@@ -42,49 +81,11 @@ namespace Jint.Native.Date
 
         private JsValue Parse(JsValue thisObj, JsValue[] arguments)
         {
-            DateTime result;
             var date = TypeConverter.ToString(arguments.At(0));
 
-            if (!DateTime.TryParseExact(date, new[]
-            {
-                "yyyy-MM-ddTHH:mm:ss.FFF",
-                "yyyy-MM-ddTHH:mm:ss",
-                "yyyy-MM-ddTHH:mm",
-                "yyyy-MM-dd",
-                "yyyy-MM",
-                "yyyy"
-            }, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out result))
+            if (!DateTime.TryParseExact(date, DefaultFormats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result))
             {
-                if (!DateTime.TryParseExact(date, new[]
-                {
-                    // Formats used in DatePrototype toString methods
-                    "ddd MMM dd yyyy HH:mm:ss 'GMT'K",
-                    "ddd MMM dd yyyy",
-                    "HH:mm:ss 'GMT'K",
-
-                    // standard formats
-                    "yyyy-M-dTH:m:s.FFFK",
-                    "yyyy/M/dTH:m:s.FFFK",
-                    "yyyy-M-dTH:m:sK",
-                    "yyyy/M/dTH:m:sK",
-                    "yyyy-M-dTH:mK",
-                    "yyyy/M/dTH:mK",
-                    "yyyy-M-d H:m:s.FFFK",
-                    "yyyy/M/d H:m:s.FFFK",
-                    "yyyy-M-d H:m:sK",
-                    "yyyy/M/d H:m:sK",
-                    "yyyy-M-d H:mK",
-                    "yyyy/M/d H:mK",
-                    "yyyy-M-dK",
-                    "yyyy/M/dK",
-                    "yyyy-MK",
-                    "yyyy/MK",
-                    "yyyyK",
-                    "THH:mm:ss.FFFK",
-                    "THH:mm:ssK",
-                    "THH:mmK",
-                    "THHK"
-                }, 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))
                     {
@@ -105,7 +106,7 @@ namespace Jint.Native.Date
             return TimeClip(ConstructTimeValue(arguments, useUtc: true));
         }
 
-        private JsValue Now(JsValue thisObj, JsValue[] arguments)
+        private static JsValue Now(JsValue thisObj, JsValue[] arguments)
         {
             return System.Math.Floor((DateTime.UtcNow - Epoch).TotalMilliseconds);
         }
@@ -131,7 +132,7 @@ namespace Jint.Native.Date
                 var v = TypeConverter.ToPrimitive(arguments[0]);
                 if (v.IsString())
                 {
-                    return Construct(Parse(Undefined, Arguments.From(v)).AsNumber());
+                    return Construct(((JsNumber) Parse(Undefined, Arguments.From(v)))._value);
                 }
 
                 return Construct(TypeConverter.ToNumber(v));
@@ -146,7 +147,7 @@ namespace Jint.Native.Date
         {
             if (arguments.Length < 2)
             {
-                throw new ArgumentOutOfRangeException("arguments", "There must be at least two arguments.");
+                throw new ArgumentOutOfRangeException(nameof(arguments), "There must be at least two arguments.");
             }
 
             var y = TypeConverter.ToNumber(arguments[0]);
@@ -165,7 +166,7 @@ namespace Jint.Native.Date
                 }
             }
 
-            if ((!double.IsNaN(y)) && (0 <= TypeConverter.ToInteger(y)) && (TypeConverter.ToInteger(y) <= 99))
+            if (!double.IsNaN(y) && 0 <= TypeConverter.ToInteger(y) && TypeConverter.ToInteger(y) <= 99)
             {
                 y += 1900;
             }

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

@@ -13,18 +13,10 @@ namespace Jint.Native.Date
         internal static readonly double Min = -(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) - DateTime.MinValue).TotalMilliseconds;
 
         public DateInstance(Engine engine)
-            : base(engine)
+            : base(engine, objectClass: "Date")
         {
         }
 
-        public override string Class
-        {
-            get
-            {
-                return "Date";
-            }
-        }
-
         public DateTime ToDateTime()
         {
             if (double.IsNaN(PrimitiveValue) || PrimitiveValue > Max || PrimitiveValue < Min)

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

@@ -1,7 +1,7 @@
 using System;
 using System.Globalization;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Date
@@ -25,7 +25,7 @@ namespace Jint.Native.Date
                 PrimitiveValue = double.NaN
             };
 
-            obj.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(dateConstructor));
+            obj.SetOwnProperty("constructor", new PropertyDescriptor(dateConstructor, PropertyFlag.NonEnumerable));
 
             return obj;
         }
@@ -562,9 +562,9 @@ namespace Jint.Native.Date
         {
             var o = TypeConverter.ToObject(Engine, thisObj);
             var tv = TypeConverter.ToPrimitive(o, Types.Number);
-            if (tv.IsNumber() && double.IsInfinity(tv.AsNumber()))
+            if (tv.IsNumber() && double.IsInfinity(((JsNumber) tv)._value))
             {
-                return JsValue.Null;
+                return Null;
             }
 
             var toIso = o.Get("toISOString");
@@ -1025,7 +1025,7 @@ namespace Jint.Native.Date
                 case 1:
                     return 28 + leap;
                 default:
-                    throw new ArgumentOutOfRangeException("month");
+                    throw new ArgumentOutOfRangeException(nameof(month));
 
             }
         }

+ 6 - 5
Jint/Native/Error/ErrorConstructor.cs

@@ -1,7 +1,7 @@
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 
 namespace Jint.Native.Error
 {
@@ -23,10 +23,10 @@ namespace Jint.Native.Error
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = ErrorPrototype.CreatePrototypeObject(engine, obj, name);
 
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
+            obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));
 
             // The initial value of Error.prototype is the Error prototype object
-            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
 
             return obj;
         }
@@ -47,9 +47,10 @@ namespace Jint.Native.Error
             instance.Prototype = PrototypeObject;
             instance.Extensible = true;
 
-            if (arguments.At(0) != Undefined)
+            var jsValue = arguments.At(0);
+            if (!jsValue.IsUndefined())
             {
-                instance.Put("message", TypeConverter.ToString(arguments.At(0)), false);
+                instance.Put("message", TypeConverter.ToString(jsValue), false);
             }
 
             return instance;

+ 1 - 9
Jint/Native/Error/ErrorInstance.cs

@@ -6,19 +6,11 @@ namespace Jint.Native.Error
     public class ErrorInstance : ObjectInstance
     {
         public ErrorInstance(Engine engine, string name)
-            : base(engine)
+            : base(engine, objectClass: "Error")
         {
             FastAddProperty("name", name, true, false, true);
         }
 
-        public override string Class
-        {
-            get
-            {
-                return "Error";
-            }
-        }
-
         public override string ToString()
         {
             return Engine.Error.PrototypeObject.ToString(this, Arguments.Empty).ToObject().ToString();

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

@@ -1,6 +1,6 @@
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Error
@@ -18,7 +18,7 @@ namespace Jint.Native.Error
         public static ErrorPrototype CreatePrototypeObject(Engine engine, ErrorConstructor errorConstructor, string name)
         {
             var obj = new ErrorPrototype(engine, name) { Extensible = true };
-            obj.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(errorConstructor));
+            obj.SetOwnProperty("constructor", new PropertyDescriptor(errorConstructor, PropertyFlag.NonEnumerable));
             obj.FastAddProperty("message", "", true, false, true);
 
             if (name != "Error")
@@ -42,7 +42,7 @@ namespace Jint.Native.Error
         public JsValue ToString(JsValue thisObject, JsValue[] arguments)
         {
             var o = thisObject.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -51,7 +51,7 @@ namespace Jint.Native.Error
 
             var msgProp = o.Get("message");
             string msg;
-            if (ReferenceEquals(msgProp, Undefined))
+            if (msgProp.IsUndefined())
             {
                 msg = "";
             }

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

@@ -1,7 +1,7 @@
 using Esprima;
 using Jint.Native.Argument;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Environments;
 
 namespace Jint.Native.Function
@@ -10,13 +10,10 @@ namespace Jint.Native.Function
     {
         private static readonly ParserOptions ParserOptions = new ParserOptions { AdaptRegexp = true, Tolerant = false };
 
-        private readonly Engine _engine;
-
         public EvalFunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict) : base(engine, parameters, scope, strict)
         {
-            _engine = engine;
             Prototype = Engine.Function.PrototypeObject;
-            SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
+            SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
@@ -74,15 +71,13 @@ namespace Jint.Native.Function
                                 der.ReleaseArguments();
                             }
 
-                            if (result.Type == Completion.Throw)
+                            if (result.Type == CompletionType.Throw)
                             {
                                 var ex = new JavaScriptException(value).SetCallstack(_engine, result.Location);
-                                _engine.CompletionPool.Return(result);
                                 throw ex;
                             }
                             else
                             {
-                                _engine.CompletionPool.Return(result);
                                 return value;
                             }
                         }

+ 7 - 8
Jint/Native/Function/FunctionConstructor.cs

@@ -1,10 +1,9 @@
 using System;
-using System.Linq;
 using Esprima;
 using Esprima.Ast;
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Environments;
 
 namespace Jint.Native.Function
@@ -28,8 +27,8 @@ namespace Jint.Native.Function
             // The value of the [[Prototype]] internal property of the Function constructor is the standard built-in Function prototype object
             obj.Prototype = obj.PrototypeObject;
 
-            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
+            obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));
 
             return obj;
         }
@@ -74,7 +73,7 @@ namespace Jint.Native.Function
             {
                 var functionExpression = "function f(" + p + ") { " + body + "}";
                 var parser = new JavaScriptParser(functionExpression, ParserOptions);
-                function = parser.ParseProgram().Body.First().As<IFunction>();
+                function = (IFunction) parser.ParseProgram().Body[0];
             }
             catch (ParserException)
             {
@@ -116,7 +115,7 @@ namespace Jint.Native.Function
         {
             get
             {
-                if (_throwTypeError != null)
+                if (!ReferenceEquals(_throwTypeError, null))
                 {
                     return _throwTypeError;
                 }
@@ -142,13 +141,13 @@ namespace Jint.Native.Function
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            if (ReferenceEquals(argArray, Undefined) || ReferenceEquals(argArray, Undefined))
+            if (argArray.IsNull() || argArray.IsUndefined())
             {
                 return func.Call(thisArg, Arguments.Empty);
             }
 
             var argArrayObj = argArray.TryCast<ObjectInstance>();
-            if (argArrayObj == null)
+            if (ReferenceEquals(argArrayObj, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }

+ 37 - 27
Jint/Native/Function/FunctionInstance.cs

@@ -9,15 +9,26 @@ namespace Jint.Native.Function
     public abstract class FunctionInstance : ObjectInstance, ICallable
     {
         private const string PropertyNamePrototype = "prototype";
-        private IPropertyDescriptor _prototype;
-        private const string PropertyNameLength = "length";
-        private IPropertyDescriptor _length;
+        private const int PropertyNamePrototypeLength = 9;
+        private PropertyDescriptor _prototype;
 
-        private readonly Engine _engine;
+        private const string PropertyNameLength = "length";
+        private const int PropertyNameLengthLength = 6;
+        private PropertyDescriptor _length;
+        
+        protected FunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict)
+            : this(engine, parameters, scope, strict, objectClass: "Function")
+        {
+        }
 
-        protected FunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict) : base(engine)
+        protected FunctionInstance(
+            Engine engine, 
+            string[] parameters, 
+            LexicalEnvironment scope, 
+            bool strict, 
+            in string objectClass)
+            : base(engine, objectClass)
         {
-            _engine = engine;
             FormalParameters = parameters;
             Scope = scope;
             Strict = strict;
@@ -39,7 +50,7 @@ namespace Jint.Native.Function
         public virtual bool HasInstance(JsValue v)
         {
             var vObj = v.TryCast<ObjectInstance>();
-            if (vObj == null)
+            if (ReferenceEquals(vObj, null))
             {
                 return false;
             }
@@ -47,12 +58,12 @@ namespace Jint.Native.Function
             var po = Get("prototype");
             if (!po.IsObject())
             {
-                throw new JavaScriptException(_engine.TypeError, string.Format("Function has non-object prototype '{0}' in instanceof check", TypeConverter.ToString(po)));
+                throw new JavaScriptException(_engine.TypeError, $"Function has non-object prototype '{TypeConverter.ToString(po)}' in instanceof check");
             }
 
             var o = po.AsObject();
 
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(_engine.TypeError);
             }
@@ -61,7 +72,7 @@ namespace Jint.Native.Function
             {
                 vObj = vObj.Prototype;
 
-                if (vObj == null)
+                if (ReferenceEquals(vObj, null))
                 {
                     return false;
                 }
@@ -73,8 +84,6 @@ namespace Jint.Native.Function
             }
         }
 
-        public override string Class => "Function";
-
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5.4
         /// </summary>
@@ -84,8 +93,9 @@ namespace Jint.Native.Function
         {
             var v = base.Get(propertyName);
 
-            var f = v.As<FunctionInstance>();
-            if (propertyName == "caller" && f != null && f.Strict)
+            if (propertyName.Length == 6
+                && propertyName == "caller"
+                && ((v.As<FunctionInstance>()?.Strict).GetValueOrDefault()))
             {
                 throw new JavaScriptException(_engine.TypeError);
             }
@@ -93,15 +103,15 @@ namespace Jint.Native.Function
             return v;
         }
 
-        public override IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
+        public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
         {
             if (_prototype != null)
             {
-                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNamePrototype, _prototype);
+                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNamePrototype, _prototype);
             }
             if (_length != null)
             {
-                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameLength, _length);
+                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameLength, _length);
             }
 
             foreach (var entry in base.GetOwnProperties())
@@ -110,13 +120,13 @@ namespace Jint.Native.Function
             }
         }
 
-        public override IPropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNamePrototype)
+            if (propertyName.Length == PropertyNamePrototypeLength && propertyName == PropertyNamePrototype)
             {
                 return _prototype ?? PropertyDescriptor.Undefined;
             }
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 return _length ?? PropertyDescriptor.Undefined;
             }
@@ -124,13 +134,13 @@ namespace Jint.Native.Function
             return base.GetOwnProperty(propertyName);
         }
 
-        protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
+        protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
         {
-            if (propertyName == PropertyNamePrototype)
+            if (propertyName.Length == PropertyNamePrototypeLength && propertyName == PropertyNamePrototype)
             {
                 _prototype = desc;
             }
-            else if (propertyName == PropertyNameLength)
+            else if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 _length = desc;
             }
@@ -142,11 +152,11 @@ namespace Jint.Native.Function
 
         public override bool HasOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNamePrototype)
+            if (propertyName.Length == PropertyNamePrototypeLength && propertyName == PropertyNamePrototype)
             {
                 return _prototype != null;
             }
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 return _length != null;
             }
@@ -156,11 +166,11 @@ namespace Jint.Native.Function
 
         public override void RemoveOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNamePrototype)
+            if (propertyName.Length == PropertyNamePrototypeLength && propertyName == PropertyNamePrototype)
             {
                 _prototype = null;
             }
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 _prototype = null;
             }

+ 23 - 15
Jint/Native/Function/FunctionPrototype.cs

@@ -23,14 +23,14 @@ namespace Jint.Native.Function
             // The value of the [[Prototype]] internal property of the Function prototype object is the standard built-in Object prototype object
             obj.Prototype = engine.Object.PrototypeObject;
 
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(0));
+            obj.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden));
 
             return obj;
         }
 
         public void Configure()
         {
-            SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(Engine.Function));
+            SetOwnProperty("constructor", new PropertyDescriptor(Engine.Function, PropertyFlag.NonEnumerable));
             FastAddProperty("toString", new ClrFunctionInstance(Engine, ToString), true, false, true);
             FastAddProperty("apply", new ClrFunctionInstance(Engine, Apply, 2), true, false, true);
             FastAddProperty("call", new ClrFunctionInstance(Engine, CallImpl, 1), true, false, true);
@@ -52,21 +52,21 @@ namespace Jint.Native.Function
             f.Prototype = Engine.Function.PrototypeObject;
 
             var o = target as FunctionInstance;
-            if (o != null)
+            if (!ReferenceEquals(o, null))
             {
                 var l = TypeConverter.ToNumber(o.Get("length")) - (arguments.Length - 1);
-                f.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(System.Math.Max(l, 0)));
+                f.SetOwnProperty("length", new PropertyDescriptor(System.Math.Max(l, 0), PropertyFlag.AllForbidden));
             }
             else
             {
-                f.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(0));
+                f.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden));
             }
 
 
             var thrower = Engine.Function.ThrowTypeError;
-            f.DefineOwnProperty("caller", new PropertyDescriptor(thrower, thrower, false, false), false);
-            f.DefineOwnProperty("arguments", new PropertyDescriptor(thrower, thrower, false, false), false);
-
+            const PropertyFlag flags = PropertyFlag.EnumerableSet | PropertyFlag.ConfigurableSet;
+            f.DefineOwnProperty("caller", new GetSetPropertyDescriptor(thrower, thrower, flags), false);
+            f.DefineOwnProperty("arguments", new GetSetPropertyDescriptor(thrower, thrower, flags), false);
 
             return f;
         }
@@ -75,12 +75,12 @@ namespace Jint.Native.Function
         {
             var func = thisObj.TryCast<FunctionInstance>();
 
-            if (func == null)
+            if (ReferenceEquals(func, null))
             {
                 throw new JavaScriptException(Engine.TypeError, "Function object expected.");
             }
 
-            return System.String.Format("function() {{ ... }}");
+            return "function() {{ ... }}";
         }
 
         public JsValue Apply(JsValue thisObject, JsValue[] arguments)
@@ -94,27 +94,35 @@ namespace Jint.Native.Function
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            if (ReferenceEquals(argArray, Null) || ReferenceEquals(argArray, Undefined))
+            if (argArray.IsNull() || argArray.IsUndefined())
             {
                 return func.Call(thisArg, Arguments.Empty);
             }
 
             var argArrayObj = argArray.TryCast<ObjectInstance>();
-            if (argArrayObj == null)
+            if (ReferenceEquals(argArrayObj, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            var len = argArrayObj.Get("length").AsNumber();
+            var len = ((JsNumber) argArrayObj.Get("length"))._value;
             uint n = TypeConverter.ToUint32(len);
-            var argList = new JsValue[n];
+
+            var argList = n < 10 
+                ? Engine.JsValueArrayPool.RentArray((int) n)
+                : new JsValue[n];
+            
             for (int index = 0; index < n; index++)
             {
                 string indexName = TypeConverter.ToString(index);
                 var nextArg = argArrayObj.Get(indexName);
                 argList[index] = nextArg;
             }
-            return func.Call(thisArg, argList);
+
+            var result = func.Call(thisArg, argList);
+            Engine.JsValueArrayPool.ReturnArray(argList);
+            
+            return result;
         }
 
         public JsValue CallImpl(JsValue thisObject, JsValue[] arguments)

+ 44 - 45
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -1,5 +1,4 @@
 using System.Collections.Generic;
-using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Jint.Native.Argument;
 using Jint.Native.Object;
@@ -16,8 +15,9 @@ namespace Jint.Native.Function
     public sealed class ScriptFunctionInstance : FunctionInstance, IConstructor
     {
         private const string PropertyNameName = "name";
+        private const int PropertyNameNameLength = 4;
 
-        private IPropertyDescriptor _name;
+        private PropertyDescriptor _name;
 
         private readonly IFunction _functionDeclaration;
 
@@ -34,36 +34,37 @@ namespace Jint.Native.Function
             _functionDeclaration = functionDeclaration;
 
             Extensible = true;
-            Prototype = engine.Function.PrototypeObject;
+            Prototype = _engine.Function.PrototypeObject;
 
-            DefineOwnProperty("length", new AllForbiddenPropertyDescriptor(JsNumber.Create(FormalParameters.Length)), false);
+            DefineOwnProperty("length", new PropertyDescriptor(JsNumber.Create(FormalParameters.Length), PropertyFlag.AllForbidden), false);
 
             var proto = new ObjectInstanceWithConstructor(engine, this)
             {
                 Extensible = true,
-                Prototype = Engine.Object.PrototypeObject
+                Prototype = _engine.Object.PrototypeObject
             };
 
-            SetOwnProperty("prototype", new WritablePropertyDescriptor(proto));
+            SetOwnProperty("prototype", new PropertyDescriptor(proto, PropertyFlag.OnlyWritable));
 
             if (_functionDeclaration.Id != null)
             {
-                _name = new NullConfigurationPropertyDescriptor(_functionDeclaration.Id.Name);
+                _name = new PropertyDescriptor(_functionDeclaration.Id.Name, PropertyFlag.None);
             }
 
             if (strict)
             {
                 var thrower = engine.Function.ThrowTypeError;
-                DefineOwnProperty("caller", new PropertyDescriptor(thrower, thrower, false, false), false);
-                DefineOwnProperty("arguments", new PropertyDescriptor(thrower, thrower, false, false), false);
+                const PropertyFlag flags = PropertyFlag.EnumerableSet | PropertyFlag.ConfigurableSet;
+                DefineOwnProperty("caller", new GetSetPropertyDescriptor(thrower, thrower, flags), false);
+                DefineOwnProperty("arguments", new GetSetPropertyDescriptor(thrower, thrower, flags), false);
             }
         }
 
-        public override IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
+        public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
         {
             if (_name != null)
             {
-                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameName, _name);
+                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameName, _name);
             }
 
             foreach (var entry in base.GetOwnProperties())
@@ -72,9 +73,9 @@ namespace Jint.Native.Function
             }
         }
 
-        public override IPropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNameName)
+            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
             {
                 return _name ?? PropertyDescriptor.Undefined;
             }
@@ -82,9 +83,9 @@ namespace Jint.Native.Function
             return base.GetOwnProperty(propertyName);
         }
 
-        protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
+        protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
         {
-            if (propertyName == PropertyNameName)
+            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
             {
                 _name = desc;
             }
@@ -96,7 +97,7 @@ namespace Jint.Native.Function
 
         public override bool HasOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNameName)
+            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
             {
                 return _name != null;
             }
@@ -106,7 +107,7 @@ namespace Jint.Native.Function
 
         public override void RemoveOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNameName)
+            if (propertyName.Length == PropertyNameNameLength && propertyName == PropertyNameName)
             {
                 _name = null;
             }
@@ -114,7 +115,6 @@ namespace Jint.Native.Function
             base.RemoveOwnProperty(propertyName);
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static string[] GetParameterNames(IFunction functionDeclaration)
         {
             var list = functionDeclaration.Params;
@@ -150,60 +150,58 @@ namespace Jint.Native.Function
                 {
                     thisBinding = thisArg;
                 }
-                else if (ReferenceEquals(thisArg, Undefined) || ReferenceEquals(thisArg, Null))
+                else if (thisArg.IsUndefined() || thisArg.IsNull())
                 {
-                    thisBinding = Engine.Global;
+                    thisBinding = _engine.Global;
                 }
                 else if (!thisArg.IsObject())
                 {
-                    thisBinding = TypeConverter.ToObject(Engine, thisArg);
+                    thisBinding = TypeConverter.ToObject(_engine, thisArg);
                 }
                 else
                 {
                     thisBinding = thisArg;
                 }
 
-                var localEnv = LexicalEnvironment.NewDeclarativeEnvironment(Engine, Scope);
+                var localEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, Scope);
 
-                Engine.EnterExecutionContext(localEnv, localEnv, thisBinding);
+                _engine.EnterExecutionContext(localEnv, localEnv, thisBinding);
 
-                Completion result = null;
                 try
                 {
-                    var argumentInstanceRented = Engine.DeclarationBindingInstantiation(
+                    var argumentInstanceRented = _engine.DeclarationBindingInstantiation(
                         DeclarationBindingType.FunctionCode,
                         _functionDeclaration.HoistingScope.FunctionDeclarations,
                         _functionDeclaration.HoistingScope.VariableDeclarations,
                         this,
                         arguments);
 
-                    result = Engine.ExecuteStatement(_functionDeclaration.Body);
+                    var result = _engine.ExecuteStatement(_functionDeclaration.Body);
                     
                     var value = result.GetValueOrDefault();
                     
                     // we can safely release arguments if they don't escape the scope
                     if (argumentInstanceRented
-                        && Engine.ExecutionContext.LexicalEnvironment?.Record is DeclarativeEnvironmentRecord der
+                        && _engine.ExecutionContext.LexicalEnvironment?.Record is DeclarativeEnvironmentRecord der
                         && !(result.Value is ArgumentsInstance))
                     {
                         der.ReleaseArguments();
                     }
 
-                    if (result.Type == Completion.Throw)
+                    if (result.Type == CompletionType.Throw)
                     {
-                        var ex = new JavaScriptException(value).SetCallstack(Engine, result.Location);
+                        var ex = new JavaScriptException(value).SetCallstack(_engine, result.Location);
                         throw ex;
                     }
 
-                    if (result.Type == Completion.Return)
+                    if (result.Type == CompletionType.Return)
                     {
                         return value;
                     }
                 }
                 finally
                 {
-                    Engine.CompletionPool.Return(result);
-                    Engine.LeaveExecutionContext();
+                    _engine.LeaveExecutionContext();
                 }
 
                 return Undefined;
@@ -218,14 +216,14 @@ namespace Jint.Native.Function
         public ObjectInstance Construct(JsValue[] arguments)
         {
             var proto = Get("prototype").TryCast<ObjectInstance>();
-            var obj = new ObjectInstance(Engine)
+            var obj = new ObjectInstance(_engine)
             {
                 Extensible = true,
-                Prototype = proto ?? Engine.Object.PrototypeObject
+                Prototype = proto ?? _engine.Object.PrototypeObject
             };
 
             var result = Call(obj, arguments).TryCast<ObjectInstance>();
-            if (result != null)
+            if (!ReferenceEquals(result, null))
             {
                 return result;
             }
@@ -236,18 +234,19 @@ namespace Jint.Native.Function
         private class ObjectInstanceWithConstructor : ObjectInstance
         {
             private const string PropertyNameConstructor = "constructor";
-            private IPropertyDescriptor _constructor;
+            private const int PropertyNameConstructorLength = 11;
+            private PropertyDescriptor _constructor;
 
             public ObjectInstanceWithConstructor(Engine engine, ObjectInstance thisObj) : base(engine)
             {
-                _constructor = new NonEnumerablePropertyDescriptor(thisObj);
+                _constructor = new PropertyDescriptor(thisObj, PropertyFlag.NonEnumerable);
             }
 
-            public override IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
+            public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
             {
                 if (_constructor != null)
                 {
-                    yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameConstructor, _constructor);
+                    yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameConstructor, _constructor);
                 }
 
                 foreach (var entry in base.GetOwnProperties())
@@ -256,9 +255,9 @@ namespace Jint.Native.Function
                 }
             }
 
-            public override IPropertyDescriptor GetOwnProperty(string propertyName)
+            public override PropertyDescriptor GetOwnProperty(string propertyName)
             {
-                if (propertyName == PropertyNameConstructor)
+                if (propertyName.Length == PropertyNameConstructorLength && propertyName == PropertyNameConstructor)
                 {
                     return _constructor ?? PropertyDescriptor.Undefined;
                 }
@@ -266,9 +265,9 @@ namespace Jint.Native.Function
                 return base.GetOwnProperty(propertyName);
             }
 
-            protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
+            protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
             {
-                if (propertyName == PropertyNameConstructor)
+                if (propertyName.Length == PropertyNameConstructorLength && propertyName == PropertyNameConstructor)
                 {
                     _constructor = desc;
                 }
@@ -280,7 +279,7 @@ namespace Jint.Native.Function
 
             public override bool HasOwnProperty(string propertyName)
             {
-                if (propertyName == PropertyNameConstructor)
+                if (propertyName.Length == PropertyNameConstructorLength && propertyName == PropertyNameConstructor)
                 {
                     return _constructor != null;
                 }
@@ -290,7 +289,7 @@ namespace Jint.Native.Function
 
             public override void RemoveOwnProperty(string propertyName)
             {
-                if (propertyName == PropertyNameConstructor)
+                if (propertyName.Length == PropertyNameConstructorLength && propertyName == PropertyNameConstructor)
                 {
                     _constructor = null;
                 }

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

@@ -1,16 +1,13 @@
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 
 namespace Jint.Native.Function
 {
     public sealed class ThrowTypeError : FunctionInstance
     {
-        private readonly Engine _engine;
-
         public ThrowTypeError(Engine engine): base(engine, System.Array.Empty<string>(), engine.GlobalEnvironment, false)
         {
-            _engine = engine;
-            DefineOwnProperty("length", new AllForbiddenPropertyDescriptor(0), false);
+            DefineOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden), false);
             Extensible = false;
         }
 

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

@@ -120,7 +120,7 @@ namespace Jint.Native.Global
 
             try
             {
-                return sign * Parse(s, radix).AsNumber();
+                return sign * Parse(s, radix);
             }
             catch
             {
@@ -129,7 +129,7 @@ namespace Jint.Native.Global
 
         }
 
-        private static JsValue Parse(string number, int radix)
+        private static double Parse(string number, int radix)
         {
             if (number == "")
             {

+ 6 - 12
Jint/Native/JsBoolean.cs

@@ -1,32 +1,26 @@
 using System;
-using System.Diagnostics.Contracts;
 using Jint.Runtime;
 
 namespace Jint.Native
 {
     public sealed class JsBoolean : JsValue, IEquatable<JsBoolean>
     {
-        private readonly bool _value;
-
         public static readonly JsValue False = new JsBoolean(false);
         public static readonly JsValue True = new JsBoolean(true);
 
-        public JsBoolean(bool value)
-        {
-            _value = value;
-        }
+        internal static readonly object BoxedTrue = true;
+        internal static readonly object BoxedFalse = false;
 
-        public override Types Type => Types.Boolean;
+        internal readonly bool _value;
 
-        [Pure]
-        public override bool AsBoolean()
+        public JsBoolean(bool value) : base(Types.Boolean)
         {
-            return _value;
+            _value = value;
         }
 
         public override object ToObject()
         {
-            return _value;
+            return _value ? BoxedTrue : BoxedFalse;
         }
 
         public override string ToString()

+ 1 - 3
Jint/Native/JsNull.cs

@@ -5,12 +5,10 @@ namespace Jint.Native
 {
     public sealed class JsNull : JsValue, IEquatable<JsNull>
     {
-        internal JsNull()
+        internal JsNull() : base(Types.Null)
         {
         }
 
-        public override Types Type => Types.Null;
-
         public override object ToObject()
         {
             return null;

+ 4 - 13
Jint/Native/JsNumber.cs

@@ -1,12 +1,11 @@
 using System;
-using System.Diagnostics.Contracts;
 using Jint.Runtime;
 
 namespace Jint.Native
 {
     public sealed class JsNumber : JsValue, IEquatable<JsNumber>
     {
-        private readonly double _value;
+        internal readonly double _value;
 
         // how many decimals to check when determining if double is actually an int
         private const double DoubleIsIntegerTolerance = double.Epsilon * 100;
@@ -34,29 +33,21 @@ namespace Jint.Native
             }
         }
 
-        public JsNumber(double value)
+        public JsNumber(double value) : base(Types.Number)
         {
             _value = value;
         }
 
-        public JsNumber(int value)
+        public JsNumber(int value) : base(Types.Number)
         {
             _value = value;
         }
 
-        public JsNumber(uint value)
+        public JsNumber(uint value) : base(Types.Number)
         {
             _value = value;
         }
 
-        public override Types Type => Types.Number;
-
-        [Pure]
-        public override double AsNumber()
-        {
-            return _value;
-        }
-
         public override object ToObject()
         {
             return _value;

+ 25 - 39
Jint/Native/JsString.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Diagnostics.Contracts;
 using System.Text;
 using Jint.Runtime;
 
@@ -14,7 +13,7 @@ namespace Jint.Native
         private static readonly JsString Empty = new JsString("");
         private static readonly JsString NullString = new JsString("null");
 
-        private string _value;
+        internal string _value;
 
         static JsString()
         {
@@ -28,7 +27,7 @@ namespace Jint.Native
             }
         }
 
-        public JsString(string value)
+        public JsString(string value) : base(Types.String)
         {
             _value = value;
         }
@@ -38,24 +37,11 @@ namespace Jint.Native
             return _value;
         }
 
-        public JsString(char value)
+        public JsString(char value) : base(Types.String)
         {
             _value = value.ToString();
         }
 
-        public override Types Type => Types.String;
-
-        [Pure]
-        public override string AsString()
-        {
-            if (_value == null)
-            {
-                throw new ArgumentException("The value is not defined");
-            }
-
-            return _value;
-        }
-
         public virtual JsString Append(JsValue jsValue)
         {
             return new ConcatenatedString(string.Concat(_value, TypeConverter.ToString(jsValue)));
@@ -66,29 +52,24 @@ namespace Jint.Native
             return new ConcatenatedString(_value, capacity);
         }
 
+        internal virtual bool IsNullOrEmpty()
+        {
+            return string.IsNullOrEmpty(_value);
+        }
+
         internal static JsString Create(string value)
         {
-            if (value.Length <= 1)
+            switch (value.Length)
             {
-                if (value == "")
-                {
+                case 0:
                     return Empty;
-                }
-
-                if (value.Length == 1)
-                {
-                    if (value[0] >= 0 && value[0] <= AsciiMax)
-                    {
-                        return _charToStringJsValue[value[0]];
-                    }
-                }
-            }
-            else if (value == Native.Null.Text)
-            {
-                return NullString;
+                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 new JsString(value);
         }
 
         internal static JsString Create(char value)
@@ -153,8 +134,7 @@ namespace Jint.Native
                 }
             }
 
-            [Pure]
-            public override string AsString()
+            public override string ToString()
             {
                 if (_dirty)
                 {
@@ -191,6 +171,12 @@ namespace Jint.Native
                 return this;
             }
 
+            internal override bool IsNullOrEmpty()
+            {
+                return _stringBuilder == null && string.IsNullOrEmpty(_value)
+                    || _stringBuilder != null && _stringBuilder.Length == 0;
+            }
+
             public override bool Equals(JsValue other)
             {
                 if (other is ConcatenatedString cs)
@@ -200,13 +186,13 @@ namespace Jint.Native
 
                 if (other.Type == Types.String)
                 {
-                    var otherString = other.AsString();
+                    var otherString = other.AsStringWithoutTypeCheck();
                     if (otherString.Length != _stringBuilder.Length)
                     {
                         return false;
                     }
 
-                    return AsString().Equals(otherString);
+                    return ToString().Equals(otherString);
                 }
 
                 return base.Equals(other);

+ 2 - 14
Jint/Native/JsSymbol.cs

@@ -8,30 +8,18 @@ namespace Jint.Native
     /// </summary>
     public sealed class JsSymbol : JsValue, IEquatable<JsSymbol>
     {
-        private readonly string _value;
+        internal readonly string _value;
 
-        public JsSymbol(string value)
+        public JsSymbol(string value) : base(Types.Symbol)
         {
             _value = value;
         }
 
-        public override Types Type => Types.Symbol;
-
         public override object ToObject()
         {
             return _value;
         }
 
-        public override string AsSymbol()
-        {
-            if (_value == null)
-            {
-                throw new ArgumentException("The value is not defined");
-            }
-
-            return _value;
-        }
-
         public override bool Equals(JsValue obj)
         {
             if (ReferenceEquals(null, obj))

+ 1 - 3
Jint/Native/JsUndefined.cs

@@ -5,12 +5,10 @@ namespace Jint.Native
 {
     public sealed class JsUndefined : JsValue, IEquatable<JsUndefined>
     {
-        internal JsUndefined()
+        internal JsUndefined() : base(Types.Undefined)
         {
         }
 
-        public override Types Type => Types.Undefined;
-
         public override object ToObject()
         {
             return null;

+ 124 - 85
Jint/Native/JsValue.cs

@@ -2,13 +2,14 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
 using System.Threading;
 using Jint.Native.Array;
 using Jint.Native.Date;
 using Jint.Native.Object;
 using Jint.Native.RegExp;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native
@@ -18,131 +19,173 @@ namespace Jint.Native
     {
         public static readonly JsValue Undefined = new JsUndefined();
         public static readonly JsValue Null = new JsNull();
+        internal readonly Types _type;
+
+        protected JsValue(Types type)
+        {
+            _type = type;
+        }
 
         [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsPrimitive()
         {
-            return Type != Types.Object && Type != Types.None;
+            return _type != Types.Object && _type != Types.None;
         }
 
         [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsUndefined()
         {
-            return Type == Types.Undefined;
+            return _type == Types.Undefined;
         }
 
         [Pure]
-        public virtual bool IsArray()
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool IsArray()
         {
-            return false;
+            return this is ArrayInstance;
         }
 
         [Pure]
-        public virtual bool IsDate()
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool IsDate()
         {
-            return false;
+            return this is DateInstance;
         }
 
         [Pure]
-        public virtual bool IsRegExp()
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool IsRegExp()
         {
-            return false;
+            return this is RegExpInstance;
         }
 
         [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsObject()
         {
-            return Type == Types.Object;
+            return _type == Types.Object;
         }
 
         [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsString()
         {
-            return Type == Types.String;
+            return _type == Types.String;
         }
 
         [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsNumber()
         {
-            return Type == Types.Number;
+            return _type == Types.Number;
         }
 
         [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsBoolean()
         {
-            return Type == Types.Boolean;
+            return _type == Types.Boolean;
         }
 
         [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsNull()
         {
-            return Type == Types.Null;
+            return _type == Types.Null;
         }
 
         [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsCompletion()
         {
-            return Type == Types.Completion;
+            return _type == Types.Completion;
         }
 
         [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsSymbol()
         {
-            return Type == Types.Symbol;
+            return _type == Types.Symbol;
         }
 
         [Pure]
-        public virtual ObjectInstance AsObject()
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public ObjectInstance AsObject()
         {
-            throw new ArgumentException("The value is not an object");
+            if (!IsObject())
+            {
+                ThrowArgumentException("The value is not an object");
+            }
+            return this as ObjectInstance;
         }
 
         [Pure]
-        public virtual TInstance AsInstance<TInstance>() where TInstance : class
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public TInstance AsInstance<TInstance>() where TInstance : class
         {
-            throw new ArgumentException("The value is not an object");
+            if (!IsObject())
+            {
+                ThrowArgumentException("The value is not an object");
+            }
+            return this as TInstance;
         }
 
         [Pure]
-        public virtual ArrayInstance AsArray()
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public ArrayInstance AsArray()
         {
-            throw new ArgumentException("The value is not an array");
+            if (!IsArray())
+            {
+                ThrowArgumentException("The value is not an array");
+            }
+            return this as ArrayInstance;
         }
 
         [Pure]
-        public virtual DateInstance AsDate()
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public DateInstance AsDate()
         {
-            throw new ArgumentException("The value is not a date");
+            if (!IsDate())
+            {
+                ThrowArgumentException("The value is not a date");
+            }
+            return this as DateInstance;
         }
 
         [Pure]
-        public virtual RegExpInstance AsRegExp()
+        public RegExpInstance AsRegExp()
         {
-            throw new ArgumentException("The value is not a date");
+            if (!IsRegExp())
+            {
+                ThrowArgumentException("The value is not a regex");
+            }
+
+            return this as RegExpInstance;
         }
 
         [Pure]
-        public virtual Completion AsCompletion()
+        public Completion AsCompletion()
         {
-            if (Type != Types.Completion)
+            if (_type != Types.Completion)
             {
-                throw new ArgumentException("The value is not a completion record");
+                ThrowArgumentException("The value is not a completion record");
             }
 
             // TODO not implemented
-            return null;
+            return new Completion(CompletionType.Normal, Native.Undefined.Instance, null);
         }
 
         [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public T TryCast<T>(Action<JsValue> fail = null) where T : class
         {
             if (IsObject())
             {
-                var o = AsObject();
-                var t = o as T;
-                if (t != null)
+                if (this is T o)
                 {
-                    return t;
+                    return o;
                 }
             }
 
@@ -151,42 +194,31 @@ namespace Jint.Native
             return null;
         }
 
-        public virtual bool Is<T>()
-        {
-            return false;
-        }
-
-        public virtual T As<T>() where T : ObjectInstance
-        {
-            return null;
-        }
-
         [Pure]
-        public virtual bool AsBoolean()
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool Is<T>()
         {
-            throw new ArgumentException("The value is not a boolean");
+            return IsObject() && this is T;
         }
 
         [Pure]
-        public virtual string AsString()
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public T As<T>() where T : ObjectInstance
         {
-            throw new ArgumentException("The value is not a string");
-        }
-
-        [Pure]
-        public virtual string AsSymbol()
-        {
-            throw new ArgumentException("The value is not a symbol");
+            if (IsObject())
+            {
+                return this as T;
+            }
+            return null;
         }
 
-        [Pure]
-        public virtual double AsNumber()
+        // ReSharper disable once ConvertToAutoPropertyWhenPossible // PERF
+        public Types Type
         {
-            throw new ArgumentException("The value is not a number");
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get { return _type; }
         }
 
-        public abstract Types Type { get; }
-
         /// <summary>
         /// Creates a valid <see cref="JsValue"/> instance from any <see cref="Object"/> instance
         /// </summary>
@@ -205,10 +237,11 @@ namespace Jint.Native
                 return jsValue;
             }
 
-            var objectConvertersCount = engine.Options._ObjectConverters.Count;
-            for (var i = 0; i < objectConvertersCount; i++)
+            var converters = engine.Options._ObjectConverters;
+            var convertersCount = converters.Count;
+            for (var i = 0; i < convertersCount; i++)
             {
-                var converter = engine.Options._ObjectConverters[i];
+                var converter = converters[i];
                 if (converter.TryConvert(value, out var result))
                 {
                     return result;
@@ -233,25 +266,6 @@ namespace Jint.Native
 
             if (value is System.Array a)
             {
-                JsValue Convert(Engine e, object v)
-                {
-                    var array = (System.Array) v;
-                    var arrayLength = (uint) array.Length;
-
-                    var jsArray = new ArrayInstance(e, arrayLength);
-                    jsArray.Prototype = e.Array.PrototypeObject;
-                    jsArray.Extensible = true;
-                    
-                    for (uint i = 0; i < arrayLength; ++i)
-                    {
-                        var jsItem = FromObject(e, array.GetValue(i));
-                        jsArray.WriteArrayValue(i, new ConfigurableEnumerableWritablePropertyDescriptor(jsItem));
-                    }
-                    jsArray.SetOwnProperty("length", new WritablePropertyDescriptor(arrayLength));
-
-                    return jsArray;
-                }
-
                 // racy, we don't care, worst case we'll catch up later
                 Interlocked.CompareExchange(ref Engine.TypeMappers, new Dictionary<Type, Func<Engine, object, JsValue>>(typeMappers)
                 {
@@ -275,6 +289,26 @@ namespace Jint.Native
             return new ObjectWrapper(engine, value);
         }
 
+        private static JsValue Convert(Engine e, object v)
+        {
+            var array = (System.Array) v;
+            var arrayLength = (uint) array.Length;
+
+            var jsArray = new ArrayInstance(e, arrayLength);
+            jsArray.Prototype = e.Array.PrototypeObject;
+            jsArray.Extensible = true;
+
+            for (uint i = 0; i < arrayLength; ++i)
+            {
+                var jsItem = FromObject(e, array.GetValue(i));
+                jsArray.WriteArrayValue(i, new PropertyDescriptor(jsItem, PropertyFlag.ConfigurableEnumerableWritable));
+            }
+
+            jsArray.SetOwnProperty("length", new PropertyDescriptor(arrayLength, PropertyFlag.OnlyWritable));
+
+            return jsArray;
+        }
+
         /// <summary>
         /// Converts a <see cref="JsValue"/> to its underlying CLR value.
         /// </summary>
@@ -327,6 +361,11 @@ namespace Jint.Native
             return false;
         }
 
+        private static void ThrowArgumentException(string message)
+        {
+            throw new ArgumentException(message);
+        }
+
         public override string ToString()
         {
             return "None";
@@ -426,7 +465,7 @@ namespace Jint.Native
 
         public override int GetHashCode()
         {
-            return Type.GetHashCode();
+            return _type.GetHashCode();
         }
 
         internal class JsValueDebugView
@@ -447,13 +486,13 @@ namespace Jint.Native
                         Value = "null";
                         break;
                     case Types.Boolean:
-                        Value = value.AsBoolean() + " (bool)";
+                        Value = ((JsBoolean) value)._value + " (bool)";
                         break;
                     case Types.String:
-                        Value = value.AsString() + " (string)";
+                        Value = value.AsStringWithoutTypeCheck() + " (string)";
                         break;
                     case Types.Number:
-                        Value = value.AsNumber() + " (number)";
+                        Value = ((JsNumber) value)._value + " (number)";
                         break;
                     case Types.Object:
                         Value = value.AsObject().GetType().Name;

+ 5 - 21
Jint/Native/Json/JsonInstance.cs

@@ -7,24 +7,14 @@ namespace Jint.Native.Json
 {
     public sealed class JsonInstance : ObjectInstance
     {
-        private readonly Engine _engine;
         private JsValue _reviver;
 
         private JsonInstance(Engine engine)
-            : base(engine)
+            : base(engine, objectClass: "JSON")
         {
-            _engine = engine;
             Extensible = true;
         }
 
-        public override string Class
-        {
-            get
-            {
-                return "JSON";
-            }
-        }
-
         public static JsonInstance CreateJsonObject(Engine engine)
         {
             var json = new JsonInstance(engine);
@@ -64,9 +54,7 @@ namespace Jint.Native.Json
                                 new PropertyDescriptor
                                 (
                                     value: newValue,
-                                    writable: true,
-                                    enumerable: true,
-                                    configurable: true
+                                    PropertyFlag.ConfigurableEnumerableWritable
                                 ),
                                 false
                             );
@@ -91,9 +79,7 @@ namespace Jint.Native.Json
                                 new PropertyDescriptor
                                 (
                                     value: newElement,
-                                    writable: true,
-                                    enumerable: true,
-                                    configurable: true
+                                    PropertyFlag.ConfigurableEnumerableWritable
                                 ),
                                 false
                             );
@@ -116,9 +102,7 @@ namespace Jint.Native.Json
                     "",
                     new PropertyDescriptor(
                         value: res,
-                        writable: true,
-                        enumerable: true,
-                        configurable: true
+                        PropertyFlag.ConfigurableEnumerableWritable
                     ),
                     false
                 );
@@ -150,7 +134,7 @@ namespace Jint.Native.Json
             }
 
             var serializer = new JsonSerializer(_engine);
-            if (ReferenceEquals(value, Undefined) && ReferenceEquals(replacer, Undefined)) {
+            if (value.IsUndefined() && replacer.IsUndefined()) {
                 return Undefined;
             }
 

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

@@ -757,7 +757,7 @@ namespace Jint.Native.Json
             return obj;
         }
 
-        private bool PropertyNameContainsInvalidChar0To31(string s)
+        private static bool PropertyNameContainsInvalidChar0To31(string s)
         {
             const int max = 31;
 

+ 20 - 21
Jint/Native/Json/JsonSerializer.cs

@@ -5,7 +5,7 @@ using Jint.Native.Array;
 using Jint.Native.Global;
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 
 namespace Jint.Native.Json
 {
@@ -54,7 +54,7 @@ namespace Jint.Native.Json
                         string item = null;
                         if (v.IsString())
                         {
-                            item = v.AsString();
+                            item = v.AsStringWithoutTypeCheck();
                         }
                         else if (v.IsNumber())
                         {
@@ -94,8 +94,10 @@ namespace Jint.Native.Json
             // defining the gap
             if (space.IsNumber())
             {
-                if (space.AsNumber() > 0) {
-                    _gap = new System.String(' ', (int)System.Math.Min(10, space.AsNumber()));
+                var number = ((JsNumber) space)._value;
+                if (number > 0)
+                {
+                    _gap = new string(' ', (int) System.Math.Min(10, number));
                 }
                 else
                 {
@@ -104,7 +106,7 @@ namespace Jint.Native.Json
             }
             else if (space.IsString())
             {
-                var stringSpace = space.AsString();
+                var stringSpace = space.AsStringWithoutTypeCheck();
                 _gap = stringSpace.Length <= 10 ? stringSpace : stringSpace.Substring(0, 10);
             }
             else
@@ -113,7 +115,7 @@ namespace Jint.Native.Json
             }
 
             var wrapper = _engine.Object.Construct(Arguments.Empty);
-            wrapper.DefineOwnProperty("", new ConfigurableEnumerableWritablePropertyDescriptor(value), false);
+            wrapper.DefineOwnProperty("", new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable), false);
 
             return Str("", wrapper);
         }
@@ -135,7 +137,7 @@ namespace Jint.Native.Json
                 }
             }
 
-            if (_replacerFunction != Undefined.Instance)
+            if (!ReferenceEquals(_replacerFunction, Undefined.Instance))
             {
                 var replacerFunctionCallable = (ICallable)_replacerFunction.AsObject();
                 value = replacerFunctionCallable.Call(holder, Arguments.From(key, value));
@@ -170,24 +172,20 @@ namespace Jint.Native.Json
                 return "null";
             }
 
-            if (value.IsBoolean() && value.AsBoolean())
-            {
-                return "true";
-            }
-
-            if (value.IsBoolean() && !value.AsBoolean())
+            if (value.IsBoolean())
             {
-                return "false";
+                return ((JsBoolean) value)._value ? "true" : "false";
             }
 
             if (value.IsString())
             {
-                return Quote(value.AsString());
+                return Quote(value.AsStringWithoutTypeCheck());
             }
 
             if (value.IsNumber())
             {
-                if (GlobalObject.IsFinite(Undefined.Instance, Arguments.From(value)).AsBoolean())
+                var isFinite = GlobalObject.IsFinite(Undefined.Instance, Arguments.From(value));
+                if (((JsBoolean) isFinite)._value)
                 {
                     return TypeConverter.ToString(value);
                 }
@@ -210,7 +208,7 @@ namespace Jint.Native.Json
             return JsValue.Undefined;
         }
 
-        private string Quote(string value)
+        private static string Quote(string value)
         {
             var sb = new System.Text.StringBuilder("\"");
 
@@ -266,12 +264,13 @@ namespace Jint.Native.Json
             for (int i = 0; i < len; i++)
             {
                 var strP = Str(TypeConverter.ToString(i), value);
-                if (strP == JsValue.Undefined)
+                if (strP.IsUndefined())
                     strP = "null";
-                partial.Add(strP.AsString());
+                partial.Add(strP.AsStringWithoutTypeCheck());
             }
             if (partial.Count == 0)
             {
+                _stack.Pop();
                 return "[]";
             }
 
@@ -317,7 +316,7 @@ namespace Jint.Native.Json
             _indent += _gap;
 
             var k = _propertyList ?? value.GetOwnProperties()
-                .Where(x => x.Value.Enumerable.HasValue && x.Value.Enumerable.Value == true)
+                .Where(x => x.Value.Enumerable)
                 .Select(x => x.Key)
                 .ToList();
 
@@ -325,7 +324,7 @@ namespace Jint.Native.Json
             foreach (var p in k)
             {
                 var strP = Str(p, value);
-                if (strP != JsValue.Undefined)
+                if (!strP.IsUndefined())
                 {
                     var member = Quote(p) + ":";
                     if (_gap != "")

+ 188 - 11
Jint/Native/Math/MathInstance.cs

@@ -8,18 +8,10 @@ namespace Jint.Native.Math
 {
     public sealed class MathInstance : ObjectInstance
     {
-        private static Random _random = new Random();
-        
-        private MathInstance(Engine engine):base(engine)
-        {
-        }
+        private static readonly Random _random = new Random();
 
-        public override string Class
+        private MathInstance(Engine engine) : base(engine, "Math")
         {
-            get
-            {
-                return "Math";
-            }
         }
 
         public static MathInstance CreateMathObject(Engine engine)
@@ -67,24 +59,76 @@ namespace Jint.Native.Math
         private static JsValue Abs(JsValue thisObject, JsValue[] arguments)
         {
             var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x))
+            {
+                return double.NaN;
+            }
+            else if (NumberInstance.IsNegativeZero(x))
+            {
+                return +0;
+            }
+            else if (double.IsInfinity(x))
+            {
+                return double.PositiveInfinity;
+            }
+
             return System.Math.Abs(x);
         }
 
         private static JsValue Acos(JsValue thisObject, JsValue[] arguments)
         {
             var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x) || (x > 1) || (x < -1))
+            {
+                return double.NaN;
+            }
+            else if (x == 1)
+            {
+                return 0;
+            }
+
             return System.Math.Acos(x);
         }
 
         private static JsValue Asin(JsValue thisObject, JsValue[] arguments)
         {
             var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x) || (x > 1) || (x < -1))
+            {
+                return double.NaN;
+            }
+            else if (NumberInstance.IsPositiveZero(x) || NumberInstance.IsNegativeZero(x))
+            {
+                return x;
+            }
+
             return System.Math.Asin(x);
         }
 
         private static JsValue Atan(JsValue thisObject, JsValue[] arguments)
         {
             var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x))
+            {
+                return double.NaN;
+            }
+            else if (NumberInstance.IsPositiveZero(x) || NumberInstance.IsNegativeZero(x))
+            {
+                return x;
+            }
+            else if (double.IsPositiveInfinity(x))
+            {
+                return System.Math.PI / 2;
+            }
+            else if (double.IsNegativeInfinity(x))
+            {
+                return -System.Math.PI / 2;
+            }
+
             return System.Math.Atan(x);
         }
 
@@ -239,30 +283,132 @@ namespace Jint.Native.Math
         private static JsValue Ceil(JsValue thisObject, JsValue[] arguments)
         {
             var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x))
+            {
+                return double.NaN;
+            }
+            else if (NumberInstance.IsPositiveZero(x))
+            {
+                return +0;
+            }
+            else if (NumberInstance.IsNegativeZero(x))
+            {
+                return -0;
+            }
+            else if (double.IsPositiveInfinity(x))
+            {
+                return double.PositiveInfinity;
+            }
+            else if (double.IsNegativeInfinity(x))
+            {
+                return double.NegativeInfinity;
+            }
+
             return System.Math.Ceiling(x);
         }
 
         private static JsValue Cos(JsValue thisObject, JsValue[] arguments)
         {
             var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x))
+            {
+                return double.NaN;
+            }
+            else if (NumberInstance.IsPositiveZero(x))
+            {
+                return 1;
+            }
+            else if (NumberInstance.IsNegativeZero(x))
+            {
+                return 1;
+            }
+            else if (double.IsInfinity(x))
+            {
+                return double.NaN;
+            }
+
             return System.Math.Cos(x);
         }
 
         private static JsValue Exp(JsValue thisObject, JsValue[] arguments)
         {
             var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x))
+            {
+                return double.NaN;
+            }
+            else if (NumberInstance.IsPositiveZero(x) || NumberInstance.IsNegativeZero(x))
+            {
+                return 1;
+            }
+            else if (double.IsPositiveInfinity(x))
+            {
+                return double.PositiveInfinity;
+            }
+            else if (double.IsNegativeInfinity(x))
+            {
+                return +0;
+            }
+
             return System.Math.Exp(x);
         }
 
         private static JsValue Floor(JsValue thisObject, JsValue[] arguments)
         {
             var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x))
+            {
+                return double.NaN;
+            }
+            else if (NumberInstance.IsPositiveZero(x))
+            {
+                return +0;
+            }
+            else if (NumberInstance.IsNegativeZero(x))
+            {
+                return -0;
+            }
+            else if (double.IsPositiveInfinity(x))
+            {
+                return double.PositiveInfinity;
+            }
+            else if (double.IsNegativeInfinity(x))
+            {
+                return double.NegativeInfinity;
+            }
+
             return System.Math.Floor(x);
         }
 
         private static JsValue Log(JsValue thisObject, JsValue[] arguments)
         {
             var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x))
+            {
+                return double.NaN;
+            }
+            if (x < 0)
+            {
+                return double.NaN;
+            }
+            else if (x == 0)
+            {
+                return double.NegativeInfinity;
+            }
+            else if (double.IsPositiveInfinity(x))
+            {
+                return double.PositiveInfinity;
+            }
+            else if (x == 1)
+            {
+                return +0;
+            }
+
             return System.Math.Log(x);
         }
 
@@ -274,9 +420,22 @@ namespace Jint.Native.Math
             }
 
             double max = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(max))
+            {
+                return double.NaN;
+            }
+
             for (int i = 0; i < arguments.Length; i++)
             {
-                max = System.Math.Max(max, TypeConverter.ToNumber(arguments[i]));
+                var value = TypeConverter.ToNumber(arguments[i]);
+
+                if (double.IsNaN(value))
+                {
+                    return double.NaN;
+                }
+
+                max = System.Math.Max(max, value);
             }
             return max;
         }
@@ -458,6 +617,24 @@ namespace Jint.Native.Math
         private static JsValue Sin(JsValue thisObject, JsValue[] arguments)
         {
             var x = TypeConverter.ToNumber(arguments.At(0));
+
+            if (double.IsNaN(x))
+            {
+                return double.NaN;
+            }
+            else if (NumberInstance.IsPositiveZero(x))
+            {
+                return +0;
+            }
+            else if (NumberInstance.IsNegativeZero(x))
+            {
+                return -0;
+            }
+            else if (double.IsInfinity(x))
+            {
+                return double.NaN;
+            }
+
             return System.Math.Sin(x);
         }
 

+ 1 - 4
Jint/Native/Number/Dtoa/FastDtoaBuilder.cs

@@ -2,8 +2,6 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-using System.Runtime.CompilerServices;
-
 namespace Jint.Native.Number.Dtoa
 {
     internal class FastDtoaBuilder
@@ -131,8 +129,7 @@ namespace Jint.Native.Number.Dtoa
             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
         };
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private void Fill<T>(T[] array, int fromIndex, int toIndex, T val)
+        private static void Fill(char[] array, int fromIndex, int toIndex, char val)
         {
             for (int i = fromIndex; i < toIndex; i++)
             {

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

@@ -1,7 +1,7 @@
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 
 namespace Jint.Native.Number
 {
@@ -22,21 +22,21 @@ namespace Jint.Native.Number
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = NumberPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
+            obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));
 
             // The initial value of Number.prototype is the Number prototype object
-            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
 
             return obj;
         }
 
         public void Configure()
         {
-            SetOwnProperty("MAX_VALUE", new AllForbiddenPropertyDescriptor(double.MaxValue));
-            SetOwnProperty("MIN_VALUE", new AllForbiddenPropertyDescriptor(double.Epsilon));
-            SetOwnProperty("NaN", new AllForbiddenPropertyDescriptor(double.NaN));
-            SetOwnProperty("NEGATIVE_INFINITY", new AllForbiddenPropertyDescriptor(double.NegativeInfinity));
-            SetOwnProperty("POSITIVE_INFINITY", new AllForbiddenPropertyDescriptor(double.PositiveInfinity));
+            SetOwnProperty("MAX_VALUE", new PropertyDescriptor(double.MaxValue, PropertyFlag.AllForbidden));
+            SetOwnProperty("MIN_VALUE", new PropertyDescriptor(double.Epsilon, PropertyFlag.AllForbidden));
+            SetOwnProperty("NaN", new PropertyDescriptor(double.NaN, PropertyFlag.AllForbidden));
+            SetOwnProperty("NEGATIVE_INFINITY", new PropertyDescriptor(double.NegativeInfinity, PropertyFlag.AllForbidden));
+            SetOwnProperty("POSITIVE_INFINITY", new PropertyDescriptor(double.PositiveInfinity, PropertyFlag.AllForbidden));
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)

+ 7 - 19
Jint/Native/Number/NumberInstance.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Runtime.CompilerServices;
 using Jint.Native.Object;
 using Jint.Runtime;
 
@@ -9,39 +10,26 @@ namespace Jint.Native.Number
         private static readonly long NegativeZeroBits = BitConverter.DoubleToInt64Bits(-0.0);
 
         public NumberInstance(Engine engine)
-            : base(engine)
+            : base(engine, "Number")
         {
         }
 
-        public override string Class
-        {
-            get
-            {
-                return "Number";
-            }
-        }
-
-        Types IPrimitiveInstance.Type
-        {
-            get { return Types.Number; }
-        }
+        Types IPrimitiveInstance.Type => Types.Number;
 
-        JsValue IPrimitiveInstance.PrimitiveValue
-        {
-            get { return NumberData; }
-        }
+        JsValue IPrimitiveInstance.PrimitiveValue => NumberData;
 
         public JsValue NumberData { get; set; }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static bool IsNegativeZero(double x)
         {
             return x == 0 && BitConverter.DoubleToInt64Bits(x) == NegativeZeroBits;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static bool IsPositiveZero(double x)
         {
             return x == 0 && BitConverter.DoubleToInt64Bits(x) != NegativeZeroBits;
         }
-
     }
-}
+}

+ 7 - 5
Jint/Native/Number/NumberPrototype.cs

@@ -43,7 +43,7 @@ namespace Jint.Native.Number
 
         private JsValue ToLocaleString(JsValue thisObject, JsValue[] arguments)
         {
-            if (!thisObject.IsNumber() && (thisObject.TryCast<NumberInstance>() == null))
+            if (!thisObject.IsNumber() && ReferenceEquals(thisObject.TryCast<NumberInstance>(), null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -81,7 +81,7 @@ namespace Jint.Native.Number
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
         {
             var number = thisObj.TryCast<NumberInstance>();
-            if (number == null)
+            if (ReferenceEquals(number, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -137,7 +137,7 @@ namespace Jint.Native.Number
         {
             var x = TypeConverter.ToNumber(thisObj);
 
-            if (ReferenceEquals(arguments.At(0), Undefined))
+            if (arguments.At(0).IsUndefined())
             {
                 return TypeConverter.ToString(x);
             }
@@ -167,12 +167,14 @@ namespace Jint.Native.Number
 
         private JsValue ToNumberString(JsValue thisObject, JsValue[] arguments)
         {
-            if (!thisObject.IsNumber() && (thisObject.TryCast<NumberInstance>() == null))
+            if (!thisObject.IsNumber() && (ReferenceEquals(thisObject.TryCast<NumberInstance>(), null)))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            var radix = arguments.At(0) == JsValue.Undefined ? 10 : (int) TypeConverter.ToInteger(arguments.At(0));
+            var radix = arguments.At(0).IsUndefined() 
+                ? 10 
+                : (int) TypeConverter.ToInteger(arguments.At(0));
 
             if (radix < 2 || radix > 36)
             {

+ 46 - 44
Jint/Native/Object/ObjectConstructor.cs

@@ -5,18 +5,14 @@ using Jint.Native.Function;
 using Jint.Native.String;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
-using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Object
 {
     public sealed class ObjectConstructor : FunctionInstance, IConstructor
     {
-        private readonly Engine _engine;
-
         private ObjectConstructor(Engine engine) : base(engine, null, null, false)
         {
-            _engine = engine;
         }
 
         public static ObjectConstructor CreateObjectConstructor(Engine engine)
@@ -26,8 +22,8 @@ namespace Jint.Native.Object
 
             obj.PrototypeObject = ObjectPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
-            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
+            obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
 
             return obj;
         }
@@ -66,7 +62,7 @@ namespace Jint.Native.Object
                 return Construct(arguments);
             }
 
-            if(ReferenceEquals(arguments[0], Null) || ReferenceEquals(arguments[0], Undefined))
+            if(arguments[0].IsNull() || arguments[0].IsUndefined())
             {
                 return Construct(arguments);
             }
@@ -85,7 +81,7 @@ namespace Jint.Native.Object
             {
                 var value = arguments[0];
                 var valueObj = value.TryCast<ObjectInstance>();
-                if (valueObj != null)
+                if (!ReferenceEquals(valueObj, null))
                 {
                     return valueObj;
                 }
@@ -109,7 +105,7 @@ namespace Jint.Native.Object
         {
             var oArg = arguments.At(0);
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -121,7 +117,7 @@ namespace Jint.Native.Object
         {
             var oArg = arguments.At(0);
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -137,7 +133,7 @@ namespace Jint.Native.Object
         {
             var oArg = arguments.At(0);
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -148,22 +144,24 @@ namespace Jint.Native.Object
             var ownProperties = o.GetOwnProperties().ToList();
             if (o is StringInstance s)
             {
-                var length = s.PrimitiveValue.AsString().Length;
+                var length = s.PrimitiveValue.AsStringWithoutTypeCheck().Length;
                 array = Engine.Array.Construct(ownProperties.Count + length);
                 for (var i = 0; i < length; i++)
                 {
-                    array.SetIndexValue(n, TypeConverter.ToString(i), throwOnError: false);
+                    array.SetIndexValue(n, TypeConverter.ToString(i), updateLength: false);
                     n++;
                 }
             }
 
-            array = array ?? Engine.Array.Construct(ownProperties.Count);
-            foreach (var p in ownProperties)
+            array = array ?? Engine.Array.ConstructFast((uint) ownProperties.Count);
+            for (var i = 0; i < ownProperties.Count; i++)
             {
+                var p = ownProperties[i];
                 array.SetIndexValue(n, p.Key, false);
                 n++;
             }
 
+            array.SetLength(n);
             return array;
         }
 
@@ -172,7 +170,7 @@ namespace Jint.Native.Object
             var oArg = arguments.At(0);
 
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null && oArg != Null)
+            if (ReferenceEquals(o, null) && !oArg.IsNull())
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -181,7 +179,7 @@ namespace Jint.Native.Object
             obj.Prototype = o;
 
             var properties = arguments.At(1);
-            if (properties != Undefined)
+            if (!properties.IsUndefined())
             {
                 DefineProperties(thisObject, new [] {obj, properties});
             }
@@ -193,7 +191,7 @@ namespace Jint.Native.Object
         {
             var oArg = arguments.At(0);
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -212,7 +210,7 @@ namespace Jint.Native.Object
         {
             var oArg = arguments.At(0);
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -222,7 +220,7 @@ namespace Jint.Native.Object
             var descriptors = new List<KeyValuePair<string, PropertyDescriptor>>();
             foreach (var p in props.GetOwnProperties())
             {
-                if (!p.Value.Enumerable.HasValue || !p.Value.Enumerable.Value)
+                if (!p.Value.Enumerable)
                 {
                     continue;
                 }
@@ -243,21 +241,19 @@ namespace Jint.Native.Object
         {
             var oArg = arguments.At(0);
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            var properties = new List<KeyValuePair<string, IPropertyDescriptor>>(o.GetOwnProperties());
+            var properties = new List<KeyValuePair<string, PropertyDescriptor>>(o.GetOwnProperties());
             foreach (var prop in properties)
             {
                 var propertyDescriptor = prop.Value;
-                if (propertyDescriptor.Configurable.HasValue && propertyDescriptor.Configurable.Value)
+                if (propertyDescriptor.Configurable)
                 {
-                    var mutable = propertyDescriptor as PropertyDescriptor ?? new PropertyDescriptor(propertyDescriptor);
-                    mutable.Configurable = false;
-                    propertyDescriptor = mutable;
-                    FastSetProperty(prop.Key, mutable);
+                    propertyDescriptor.Configurable = false;
+                    FastSetProperty(prop.Key, propertyDescriptor);
                 }
 
                 o.DefineOwnProperty(prop.Key, propertyDescriptor, true);
@@ -272,25 +268,25 @@ namespace Jint.Native.Object
         {
             var oArg = arguments.At(0);
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            var properties = new List<KeyValuePair<string, IPropertyDescriptor>>(o.GetOwnProperties());
+            var properties = new List<KeyValuePair<string, PropertyDescriptor>>(o.GetOwnProperties());
             foreach (var p in properties)
             {
                 var desc = o.GetOwnProperty(p.Key);
                 if (desc.IsDataDescriptor())
                 {
-                    if (desc.Writable.HasValue && desc.Writable.Value)
+                    if (desc.Writable)
                     {
                         var mutable = desc as PropertyDescriptor ?? new PropertyDescriptor(desc);
                         mutable.Writable = false;
                         desc = mutable;
                     }
                 }
-                if (desc.Configurable.HasValue && desc.Configurable.Value)
+                if (desc.Configurable)
                 {
                     var mutable = desc as PropertyDescriptor ?? new PropertyDescriptor(desc);
                     mutable.Configurable = false;
@@ -308,7 +304,7 @@ namespace Jint.Native.Object
         {
             var oArg = arguments.At(0);
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -322,14 +318,14 @@ namespace Jint.Native.Object
         {
             var oArg = arguments.At(0);
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
 
             foreach (var prop in o.GetOwnProperties())
             {
-                if (prop.Value.Configurable.Value == true)
+                if (prop.Value.Configurable)
                 {
                     return false;
                 }
@@ -347,22 +343,22 @@ namespace Jint.Native.Object
         {
             var oArg = arguments.At(0);
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            foreach (var p in o.GetOwnProperties().Select(x => x.Key))
+            foreach (var pair in o.GetOwnProperties())
             {
-                var desc = o.GetOwnProperty(p);
+                var desc = pair.Value;
                 if (desc.IsDataDescriptor())
                 {
-                    if (desc.Writable.HasValue && desc.Writable.Value)
+                    if (desc.Writable)
                     {
                         return false;
                     }
                 }
-                if (desc.Configurable.HasValue && desc.Configurable.Value)
+                if (desc.Configurable)
                 {
                     return false;
                 }
@@ -380,7 +376,7 @@ namespace Jint.Native.Object
         {
             var oArg = arguments.At(0);
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -392,23 +388,29 @@ namespace Jint.Native.Object
         {
             var oArg = arguments.At(0);
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
 
             var enumerableProperties = o.GetOwnProperties()
-                .Where(x => x.Value.Enumerable.HasValue && x.Value.Enumerable.Value)
+                .Where(x => x.Value.Enumerable)
                 .ToArray();
             var n = enumerableProperties.Length;
-            var array = Engine.Array.Construct(new JsValue[] {n}, (uint) n);
+
+            var args = _engine.JsValueArrayPool.RentArray(1);
+            args[0] = n;
+            var array = Engine.Array.Construct(args, (uint) n);
+            _engine.JsValueArrayPool.ReturnArray(args);
+
             uint index = 0;
             foreach (var prop in enumerableProperties)
             {
                 var p = prop.Key;
-                array.SetIndexValue(index, p, throwOnError: false);
+                array.SetIndexValue(index, p, updateLength: false);
                 index++;
             }
+            array.SetLength(index);
             return array;
         }
     }

+ 195 - 185
Jint/Native/Object/ObjectInstance.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Diagnostics.Contracts;
 using System.Dynamic;
 using System.Runtime.CompilerServices;
 using Jint.Native.Array;
@@ -19,27 +18,34 @@ namespace Jint.Native.Object
 {
     public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     {
-        private Dictionary<string, IPropertyDescriptor> _intrinsicProperties;
-        private MruPropertyCache2<IPropertyDescriptor> _properties;
-
-        public ObjectInstance(Engine engine)
+        private MruPropertyCache2<PropertyDescriptor> _intrinsicProperties;
+        private MruPropertyCache2<PropertyDescriptor> _properties;
+        
+        private readonly string _class;
+        protected readonly Engine _engine;
+        
+        public ObjectInstance(Engine engine) : this(engine, "Object")
+        {
+            _engine = engine;
+        }
+        
+        protected ObjectInstance(Engine engine, in string objectClass) : base(Types.Object)
         {
-            Engine = engine;
+            _engine = engine;
+            _class = objectClass;
         }
 
-        public Engine Engine { get; }
+        public Engine Engine => _engine;
 
         protected bool TryGetIntrinsicValue(JsSymbol symbol, out JsValue value)
         {
-            IPropertyDescriptor descriptor;
-
-            if (_intrinsicProperties != null && _intrinsicProperties.TryGetValue(symbol.AsSymbol(), out descriptor))
+            if (_intrinsicProperties != null && _intrinsicProperties.TryGetValue(symbol.AsSymbol(), out var descriptor))
             {
                 value = descriptor.Value;
                 return true;
             }
 
-            if (Prototype == null)
+            if (ReferenceEquals(Prototype, null))
             {
                 value = Undefined;
                 return false;
@@ -57,7 +63,7 @@ namespace Jint.Native.Object
         {
             if (_intrinsicProperties == null)
             {
-                _intrinsicProperties = new Dictionary<string, IPropertyDescriptor>();
+                _intrinsicProperties = new MruPropertyCache2<PropertyDescriptor>();
             }
 
             _intrinsicProperties[symbol.AsSymbol()] = new PropertyDescriptor(value, writable, enumerable, configurable);
@@ -78,9 +84,9 @@ namespace Jint.Native.Object
         /// A String value indicating a specification defined
         /// classification of objects.
         /// </summary>
-        public virtual string Class => "Object";
+        public ref readonly string Class => ref _class;
 
-        public virtual IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
+        public virtual IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
         {
             EnsureInitialized();
 
@@ -93,17 +99,17 @@ namespace Jint.Native.Object
             }
         }
 
-        protected virtual void AddProperty(string propertyName, IPropertyDescriptor descriptor)
+        protected virtual void AddProperty(string propertyName, PropertyDescriptor descriptor)
         {
             if (_properties == null)
             {
-                _properties = new MruPropertyCache2<IPropertyDescriptor>();
+                _properties = new MruPropertyCache2<PropertyDescriptor>();
             }
 
             _properties.Add(propertyName, descriptor);
         }
 
-        protected virtual bool TryGetProperty(string propertyName, out IPropertyDescriptor descriptor)
+        protected virtual bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
         {
             if (_properties == null)
             {
@@ -141,7 +147,7 @@ namespace Jint.Native.Object
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal JsValue UnwrapJsValue(IPropertyDescriptor desc)
+        internal JsValue UnwrapJsValue(PropertyDescriptor desc)
         {
             if (desc == PropertyDescriptor.Undefined)
             {
@@ -151,10 +157,10 @@ namespace Jint.Native.Object
             if (desc.IsDataDescriptor())
             {
                 var val = desc.Value;
-                return !ReferenceEquals(val, null) ? val : Undefined;
+                return val ?? Undefined;
             }
 
-            var getter = !ReferenceEquals(desc.Get, null) ? desc.Get : Undefined;
+            var getter = desc.Get ?? Undefined;
             if (getter.IsUndefined())
             {
                 return Undefined;
@@ -173,7 +179,7 @@ namespace Jint.Native.Object
         /// </summary>
         /// <param name="propertyName"></param>
         /// <returns></returns>
-        public virtual IPropertyDescriptor GetOwnProperty(string propertyName)
+        public virtual PropertyDescriptor GetOwnProperty(string propertyName)
         {
             EnsureInitialized();
 
@@ -185,13 +191,13 @@ namespace Jint.Native.Object
             return PropertyDescriptor.Undefined;
         }
 
-        protected internal virtual void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
+        protected internal virtual void SetOwnProperty(string propertyName, PropertyDescriptor desc)
         {
             EnsureInitialized();
 
             if (_properties == null)
             {
-                _properties = new MruPropertyCache2<IPropertyDescriptor>();
+                _properties = new MruPropertyCache2<PropertyDescriptor>();
             }
 
             _properties[propertyName] = desc;
@@ -203,7 +209,7 @@ namespace Jint.Native.Object
         /// <param name="propertyName"></param>
         /// <returns></returns>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public IPropertyDescriptor GetProperty(string propertyName)
+        public PropertyDescriptor GetProperty(string propertyName)
         {
             var prop = GetOwnProperty(propertyName);
 
@@ -212,7 +218,7 @@ namespace Jint.Native.Object
                 return prop;
             }
 
-            if (Prototype == null)
+            if (ReferenceEquals(Prototype, null))
             {
                 return PropertyDescriptor.Undefined;
             }
@@ -231,14 +237,14 @@ namespace Jint.Native.Object
                     return false;
                 }
 
-                if (desc.IsDataDescriptor() && desc.Value != null)
+                var descValue = desc.Value;
+                if (desc.WritableSet && !ReferenceEquals(descValue, null))
                 {
-                    value = desc.Value;
+                    value = descValue;
                     return true;
                 }
 
-                var getter = desc.Get != null ? desc.Get : Undefined;
-
+                var getter = desc.Get ??  Undefined;
                 if (getter.IsUndefined())
                 {
                     value = Undefined;
@@ -251,7 +257,7 @@ namespace Jint.Native.Object
                 return true;
             }
 
-            if (Prototype == null)
+            if (ReferenceEquals(Prototype, null))
             {
                 return false;
             }
@@ -302,7 +308,7 @@ namespace Jint.Native.Object
             }
             else
             {
-                var newDesc = new ConfigurableEnumerableWritablePropertyDescriptor(value);
+                var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
                 DefineOwnProperty(propertyName, newDesc, throwOnError);
             }
         }
@@ -323,7 +329,8 @@ namespace Jint.Native.Object
             {
                 if (desc.IsAccessorDescriptor())
                 {
-                    if (desc.Set == null || desc.Set.IsUndefined())
+                    var set = desc.Set;
+                    if (ReferenceEquals(set, null) || set.IsUndefined())
                     {
                         return false;
                     }
@@ -331,10 +338,10 @@ namespace Jint.Native.Object
                     return true;
                 }
 
-                return desc.Writable.HasValue && desc.Writable.Value;
+                return desc.Writable;
             }
 
-            if (Prototype == null)
+            if (ReferenceEquals(Prototype, null))
             {
                 return Extensible;
             }
@@ -348,7 +355,8 @@ namespace Jint.Native.Object
 
             if (inherited.IsAccessorDescriptor())
             {
-                if (inherited.Set == null || inherited.Set.IsUndefined())
+                var set = inherited.Set;
+                if (ReferenceEquals(set, null) || set.IsUndefined())
                 {
                     return false;
                 }
@@ -360,10 +368,8 @@ namespace Jint.Native.Object
             {
                 return false;
             }
-            else
-            {
-                return inherited.Writable.HasValue && inherited.Writable.Value;
-            }
+
+            return inherited.Writable;
         }
 
         /// <summary>
@@ -396,7 +402,7 @@ namespace Jint.Native.Object
                 return true;
             }
 
-            if (desc.Configurable.HasValue && desc.Configurable.Value)
+            if (desc.Configurable)
             {
                 RemoveOwnProperty(propertyName);
                 return true;
@@ -484,7 +490,7 @@ namespace Jint.Native.Object
         /// <param name="desc"></param>
         /// <param name="throwOnError"></param>
         /// <returns></returns>
-        public virtual bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
+        public virtual bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             var current = GetOwnProperty(propertyName);
 
@@ -493,6 +499,7 @@ namespace Jint.Native.Object
                 return true;
             }
 
+            var descValue = desc.Value;
             if (current == PropertyDescriptor.Undefined)
             {
                 if (!Extensible)
@@ -508,23 +515,20 @@ namespace Jint.Native.Object
                 {
                     if (desc.IsGenericDescriptor() || desc.IsDataDescriptor())
                     {
-                        IPropertyDescriptor propertyDescriptor;
-                        if (desc.Configurable.GetValueOrDefault() && desc.Enumerable.GetValueOrDefault() && desc.Writable.GetValueOrDefault())
+                        PropertyDescriptor propertyDescriptor;
+                        if (desc.Configurable && desc.Enumerable && desc.Writable)
                         {
-                            propertyDescriptor = new ConfigurableEnumerableWritablePropertyDescriptor(desc.Value != null ? desc.Value : Undefined);
+                            propertyDescriptor = new PropertyDescriptor(descValue ?? Undefined, PropertyFlag.ConfigurableEnumerableWritable);
                         }
-                        else if (!desc.Configurable.GetValueOrDefault() && !desc.Enumerable.GetValueOrDefault() && !desc.Writable.GetValueOrDefault())
+                        else if (!desc.Configurable && !desc.Enumerable && !desc.Writable)
                         {
-                            propertyDescriptor = new AllForbiddenPropertyDescriptor(desc.Value != null ? desc.Value : Undefined);
+                            propertyDescriptor = new PropertyDescriptor(descValue ?? Undefined, PropertyFlag.AllForbidden);
                         }
                         else
                         {
                             propertyDescriptor = new PropertyDescriptor(desc)
                             {
-                                Value = desc.Value != null ? desc.Value : Undefined,
-                                Writable = desc.Writable.HasValue ? desc.Writable.Value : false,
-                                Enumerable = desc.Enumerable.HasValue ? desc.Enumerable.Value : false,
-                                Configurable = desc.Configurable.HasValue ? desc.Configurable.Value : false
+                                Value = descValue ?? Undefined
                             };
                         }
 
@@ -532,13 +536,7 @@ namespace Jint.Native.Object
                     }
                     else
                     {
-                        SetOwnProperty(propertyName, new PropertyDescriptor(desc)
-                        {
-                            Get = desc.Get,
-                            Set = desc.Set,
-                            Enumerable = desc.Enumerable.HasValue ? desc.Enumerable : false,
-                            Configurable = desc.Configurable.HasValue ? desc.Configurable : false,
-                        });
+                        SetOwnProperty(propertyName, new GetSetPropertyDescriptor(desc));
                     }
                 }
 
@@ -546,32 +544,38 @@ namespace Jint.Native.Object
             }
 
             // Step 5
-            if (!current.Configurable.HasValue &&
-                !current.Enumerable.HasValue &&
-                !current.Writable.HasValue &&
-                current.Get == null &&
-                current.Set == null &&
-                current.Value == null)
+            var currentGet = current.Get;
+            var currentSet = current.Set;
+            var currentValue = current.Value;
+            
+            if (!current.ConfigurableSet &&
+                !current.EnumerableSet &&
+                !current.WritableSet &&
+                ReferenceEquals(currentGet, null) &&
+                ReferenceEquals(currentSet, null) &&
+                ReferenceEquals(currentValue, null))
             {
                 return true;
             }
 
             // Step 6
+            var descGet = desc.Get;
+            var descSet = desc.Set;
             if (
-                current.Configurable == desc.Configurable &&
-                current.Writable == desc.Writable &&
-                current.Enumerable == desc.Enumerable &&
-                ((current.Get == null && desc.Get == null) || (current.Get != null && desc.Get != null && ExpressionInterpreter.SameValue(current.Get, desc.Get))) &&
-                ((current.Set == null && desc.Set == null) || (current.Set != null && desc.Set != null && ExpressionInterpreter.SameValue(current.Set, desc.Set))) &&
-                ((current.Value == null && desc.Value == null) || (current.Value != null && desc.Value != null && ExpressionInterpreter.StrictlyEqual(current.Value, desc.Value)))
+                current.Configurable == desc.Configurable && current.ConfigurableSet == desc.ConfigurableSet &&
+                current.Writable == desc.Writable && current.WritableSet == desc.WritableSet &&
+                current.Enumerable == desc.Enumerable && current.EnumerableSet == desc.EnumerableSet &&
+                ((ReferenceEquals(currentGet, null) && ReferenceEquals(descGet, null)) || (!ReferenceEquals(currentGet, null) && !ReferenceEquals(descGet, null) && ExpressionInterpreter.SameValue(currentGet, descGet))) &&
+                ((ReferenceEquals(currentSet, null) && ReferenceEquals(descSet, null)) || (!ReferenceEquals(currentSet, null) && !ReferenceEquals(descSet, null) && ExpressionInterpreter.SameValue(currentSet, descSet))) &&
+                ((ReferenceEquals(currentValue, null) && ReferenceEquals(descValue, null)) || (!ReferenceEquals(currentValue, null) && !ReferenceEquals(descValue, null) && ExpressionInterpreter.StrictlyEqual(currentValue, descValue)))
             )
             {
                 return true;
             }
 
-            if (!current.Configurable.HasValue || !current.Configurable.Value)
+            if (!current.Configurable)
             {
-                if (desc.Configurable.HasValue && desc.Configurable.Value)
+                if (desc.Configurable)
                 {
                     if (throwOnError)
                     {
@@ -581,7 +585,7 @@ namespace Jint.Native.Object
                     return false;
                 }
 
-                if (desc.Enumerable.HasValue && (!current.Enumerable.HasValue || desc.Enumerable.Value != current.Enumerable.Value))
+                if (desc.EnumerableSet && (desc.Enumerable != current.Enumerable))
                 {
                     if (throwOnError)
                     {
@@ -596,7 +600,7 @@ namespace Jint.Native.Object
             {
                 if (current.IsDataDescriptor() != desc.IsDataDescriptor())
                 {
-                    if (!current.Configurable.HasValue || !current.Configurable.Value)
+                    if (!current.Configurable)
                     {
                         if (throwOnError)
                         {
@@ -608,28 +612,27 @@ namespace Jint.Native.Object
 
                     if (current.IsDataDescriptor())
                     {
-                        SetOwnProperty(propertyName, current = new PropertyDescriptor(
+                        var flags = current.Flags & ~(PropertyFlag.Writable | PropertyFlag.WritableSet);
+                        SetOwnProperty(propertyName, current = new GetSetPropertyDescriptor(
                             get: JsValue.Undefined,
                             set: JsValue.Undefined,
-                            enumerable: current.Enumerable,
-                            configurable: current.Configurable
+                            flags
                         ));
                     }
                     else
                     {
+                        var flags = current.Flags & ~(PropertyFlag.Writable | PropertyFlag.WritableSet);
                         SetOwnProperty(propertyName, current = new PropertyDescriptor(
                             value: JsValue.Undefined,
-                            writable: null,
-                            enumerable: current.Enumerable,
-                            configurable: current.Configurable
+                            flags
                         ));
                     }
                 }
                 else if (current.IsDataDescriptor() && desc.IsDataDescriptor())
                 {
-                    if (!current.Configurable.HasValue || current.Configurable.Value == false)
+                    if (!current.Configurable)
                     {
-                        if (!current.Writable.HasValue || !current.Writable.Value && desc.Writable.HasValue && desc.Writable.Value)
+                        if (!current.Writable && desc.Writable)
                         {
                             if (throwOnError)
                             {
@@ -639,9 +642,9 @@ namespace Jint.Native.Object
                             return false;
                         }
 
-                        if (!current.Writable.Value)
+                        if (!current.Writable)
                         {
-                            if (desc.Value != null && !ExpressionInterpreter.SameValue(desc.Value, current.Value))
+                            if (!ReferenceEquals(descValue, null) && !ExpressionInterpreter.SameValue(descValue, currentValue))
                             {
                                 if (throwOnError)
                                 {
@@ -655,11 +658,11 @@ namespace Jint.Native.Object
                 }
                 else if (current.IsAccessorDescriptor() && desc.IsAccessorDescriptor())
                 {
-                    if (!current.Configurable.HasValue || !current.Configurable.Value)
+                    if (!current.Configurable)
                     {
-                        if ((desc.Set != null && !ExpressionInterpreter.SameValue(desc.Set, current.Set != null ? current.Set : Undefined))
+                        if ((!ReferenceEquals(descSet, null) && !ExpressionInterpreter.SameValue(descSet, currentSet ?? Undefined))
                             ||
-                            (desc.Get != null && !ExpressionInterpreter.SameValue(desc.Get, current.Get != null ? current.Get : Undefined)))
+                            (!ReferenceEquals(descGet, null) && !ExpressionInterpreter.SameValue(descGet, currentGet ?? Undefined)))
                         {
                             if (throwOnError)
                             {
@@ -672,44 +675,42 @@ namespace Jint.Native.Object
                 }
             }
 
-            if (desc.Value != null)
+            if (!ReferenceEquals(descValue, null))
             {
-                current.Value = desc.Value;
+                current.Value = descValue;
             }
 
-            PropertyDescriptor mutable = null;
-            if (desc.Writable.HasValue)
+            if (desc.WritableSet)
             {
-                current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
-                mutable.Writable = desc.Writable;
+                current.Writable = desc.Writable;
             }
 
-            if (desc.Enumerable.HasValue)
+            if (desc.EnumerableSet)
             {
-                current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
-                mutable.Enumerable = desc.Enumerable;
+                current.Enumerable = desc.Enumerable;
             }
 
-            if (desc.Configurable.HasValue)
+            if (desc.ConfigurableSet)
             {
-                current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
-                mutable.Configurable = desc.Configurable;
+                current.Configurable = desc.Configurable;
             }
 
-            if (desc.Get != null)
+            PropertyDescriptor mutable = null;
+            if (!ReferenceEquals(descGet, null))
             {
-                current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
-                mutable.Get = desc.Get;
+                mutable = new GetSetPropertyDescriptor(mutable ?? current);
+                ((GetSetPropertyDescriptor) mutable).SetGet(descGet);
             }
 
-            if (desc.Set != null)
+            if (!ReferenceEquals(descSet, null))
             {
-                mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
-                mutable.Set = desc.Set;
+                mutable = new GetSetPropertyDescriptor(mutable ?? current);
+                ((GetSetPropertyDescriptor) mutable).SetSet(descSet);
             }
 
             if (mutable != null)
-            {
+            { 
+                // replace old with new type that supports get and set
                 FastSetProperty(propertyName, mutable);
             }
 
@@ -734,7 +735,7 @@ namespace Jint.Native.Object
         /// </summary>
         /// <param name="name"></param>
         /// <param name="value"></param>
-        public void FastSetProperty(string name, IPropertyDescriptor value)
+        public void FastSetProperty(string name, PropertyDescriptor value)
         {
             SetOwnProperty(name, value);
         }
@@ -748,81 +749,6 @@ namespace Jint.Native.Object
             return TypeConverter.ToString(this);
         }
 
-        public override Types Type => Types.Object;
-
-        [Pure]
-        public override bool IsArray()
-        {
-            return this is ArrayInstance;
-        }
-
-        [Pure]
-        public override bool IsDate()
-        {
-            return this is DateInstance;
-        }
-
-        [Pure]
-        public override bool IsRegExp()
-        {
-            return this is RegExpInstance;
-        }
-
-        [Pure]
-        public override ObjectInstance AsObject()
-        {
-            return this;
-        }
-
-        [Pure]
-        public override TInstance AsInstance<TInstance>()
-        {
-            return this as TInstance;
-        }
-
-        [Pure]
-        public override ArrayInstance AsArray()
-        {
-            if (!IsArray())
-            {
-                throw new ArgumentException("The value is not an array");
-            }
-
-            return this as ArrayInstance;
-        }
-
-        [Pure]
-        public override DateInstance AsDate()
-        {
-            if (!IsDate())
-            {
-                throw new ArgumentException("The value is not a date");
-            }
-
-            return this as DateInstance;
-        }
-
-        [Pure]
-        public override RegExpInstance AsRegExp()
-        {
-            if (!IsRegExp())
-            {
-                throw new ArgumentException("The value is not a regex");
-            }
-
-            return this as RegExpInstance;
-        }
-
-        public override bool Is<T>()
-        {
-            return this is T;
-        }
-
-        public override T As<T>()
-        {
-            return this as T;
-        }
-
         public override object ToObject()
         {
             if (this is IObjectWrapper wrapper)
@@ -860,7 +786,7 @@ namespace Jint.Native.Object
                 case "String":
                     if (this is StringInstance stringInstance)
                     {
-                        return stringInstance.PrimitiveValue.AsString();
+                        return stringInstance.PrimitiveValue.AsStringWithoutTypeCheck();
                     }
 
                     break;
@@ -876,7 +802,9 @@ namespace Jint.Native.Object
                 case "Boolean":
                     if (this is BooleanInstance booleanInstance)
                     {
-                        return booleanInstance.PrimitiveValue.AsBoolean();
+                        return ((JsBoolean) booleanInstance.PrimitiveValue)._value
+                             ? JsBoolean.BoxedTrue
+                             : JsBoolean.BoxedFalse;
                     }
 
                     break;
@@ -892,7 +820,7 @@ namespace Jint.Native.Object
                 case "Number":
                     if (this is NumberInstance numberInstance)
                     {
-                        return numberInstance.NumberData.AsNumber();
+                        return ((JsNumber) numberInstance.NumberData)._value;
                     }
 
                     break;
@@ -915,7 +843,7 @@ namespace Jint.Native.Object
 
                     foreach (var p in GetOwnProperties())
                     {
-                        if (!p.Value.Enumerable.HasValue || p.Value.Enumerable.Value == false)
+                        if (!p.Value.Enumerable)
                         {
                             continue;
                         }
@@ -929,6 +857,88 @@ namespace Jint.Native.Object
 
             return this;
         }
+        
+        /// <summary>
+        /// Handles the generic find of (callback[, thisArg])
+        /// </summary>
+        internal virtual bool FindWithCallback(
+            JsValue[] arguments, 
+            out uint index, 
+            out JsValue value)
+        {
+            uint GetLength()
+            {
+                var desc = GetProperty("length");
+                var descValue = desc.Value;
+                if (desc.IsDataDescriptor() && !ReferenceEquals(descValue, null))
+                {
+                    return TypeConverter.ToUint32(descValue);
+                }
+
+                var getter = desc.Get ?? Undefined;
+                if (getter.IsUndefined())
+                {
+                    return 0;
+                }
+
+                // if getter is not undefined it must be ICallable
+                return TypeConverter.ToUint32(((ICallable) getter).Call(this, Arguments.Empty));
+            }
+           
+            bool TryGetValue(uint idx, out JsValue jsValue)
+            {
+                var property = TypeConverter.ToString(idx);
+                var kPresent = HasProperty(property);
+                jsValue = kPresent ? Get(property) : Undefined;
+                return kPresent;
+            }
+
+            var len = GetLength();
+            if (len == 0)
+            {
+                index = 0;
+                value = Undefined;
+                return false;
+            }
+
+            var callbackfn = arguments.At(0);
+            var thisArg = arguments.At(1);
+            var callable = GetCallable(callbackfn);
+
+            var args = Engine.JsValueArrayPool.RentArray(3);
+            for (uint k = 0; k < len; k++)
+            {
+                if (TryGetValue(k, out var kvalue))
+                {
+                    args[0] = kvalue;
+                    args[1] = k;
+                    args[2] = this;
+                    var testResult = callable.Call(thisArg, args);
+                    if (TypeConverter.ToBoolean(testResult))
+                    {
+                        index = k;
+                        value = kvalue;
+                        return true;
+                    }
+                }
+            }
+
+            Engine.JsValueArrayPool.ReturnArray(args);
+
+            index = 0;
+            value = Undefined;
+            return false;
+        }
+
+        protected ICallable GetCallable(JsValue source)
+        {
+            if (source is ICallable callable)
+            {
+                return callable;
+            }
+
+            throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
+        }
 
         public override bool Equals(JsValue obj)
         {

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

@@ -38,7 +38,7 @@ namespace Jint.Native.Object
             {
                 return false;
             }
-            return desc.Enumerable.HasValue && desc.Enumerable.Value;
+            return desc.Enumerable;
         }
 
         private JsValue ValueOf(JsValue thisObject, JsValue[] arguments)
@@ -62,7 +62,7 @@ namespace Jint.Native.Object
             {
                 v = v.Prototype;
 
-                if (v == null)
+                if (ReferenceEquals(v, null))
                 {
                     return false;
                 }
@@ -94,12 +94,12 @@ namespace Jint.Native.Object
         /// <returns></returns>
         public JsValue ToObjectString(JsValue thisObject, JsValue[] arguments)
         {
-            if (ReferenceEquals(thisObject, Undefined))
+            if (thisObject.IsUndefined())
             {
                 return "[object Undefined]";
             }
 
-            if (ReferenceEquals(thisObject, Null))
+            if (thisObject.IsNull())
             {
                 return "[object Null]";
             }

+ 23 - 24
Jint/Native/RegExp/RegExpConstructor.cs

@@ -4,7 +4,7 @@ using Esprima;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 
 namespace Jint.Native.RegExp
 {
@@ -24,10 +24,10 @@ namespace Jint.Native.RegExp
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = RegExpPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(2));
+            obj.SetOwnProperty("length", new PropertyDescriptor(2, PropertyFlag.AllForbidden));
 
             // The initial value of RegExp.prototype is the RegExp prototype object
-            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
 
             return obj;
         }
@@ -41,8 +41,8 @@ namespace Jint.Native.RegExp
             var pattern = arguments.At(0);
             var flags = arguments.At(1);
 
-            if (!ReferenceEquals(pattern, Undefined)
-                && ReferenceEquals(flags, Undefined)
+            if (!pattern.IsUndefined()
+                && flags.IsUndefined()
                 && TypeConverter.ToObject(Engine, pattern).Class == "Regex")
             {
                 return pattern;
@@ -66,28 +66,27 @@ namespace Jint.Native.RegExp
             var flags = arguments.At(1);
 
             var r = pattern.TryCast<RegExpInstance>();
-            if (ReferenceEquals(flags, Undefined) && r != null)
+            if (flags.IsUndefined() && !ReferenceEquals(r, null))
             {
                 return r;
             }
-            else if (flags != Undefined && r != null)
+
+            if (!flags.IsUndefined() && !ReferenceEquals(r, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
+
+            if (pattern.IsUndefined())
+            {
+                p = "";
+            }
             else
             {
-                if (ReferenceEquals(pattern, Undefined))
-                {
-                    p = "";
-                }
-                else
-                {
-                    p = TypeConverter.ToString(pattern);
-                }
-
-                f = !ReferenceEquals(flags, Undefined) ? TypeConverter.ToString(flags) : "";
+                p = TypeConverter.ToString(pattern);
             }
 
+            f = !flags.IsUndefined() ? TypeConverter.ToString(flags) : "";
+
             r = new RegExpInstance(Engine);
             r.Prototype = PrototypeObject;
             r.Extensible = true;
@@ -105,7 +104,7 @@ namespace Jint.Native.RegExp
             string s;
             s = p;
 
-            if (System.String.IsNullOrEmpty(s))
+            if (string.IsNullOrEmpty(s))
             {
                 s = "(?:)";
             }
@@ -158,14 +157,14 @@ namespace Jint.Native.RegExp
 
         private static void SetRegexProperties(RegExpInstance r)
         {
-            r.SetOwnProperty("global", new AllForbiddenPropertyDescriptor(r.Global));
-            r.SetOwnProperty("ignoreCase", new AllForbiddenPropertyDescriptor(r.IgnoreCase));
-            r.SetOwnProperty("multiline", new AllForbiddenPropertyDescriptor(r.Multiline));
-            r.SetOwnProperty("source", new AllForbiddenPropertyDescriptor(r.Source));
-            r.SetOwnProperty("lastIndex", new WritablePropertyDescriptor(0));
+            r.SetOwnProperty("global", new PropertyDescriptor(r.Global, PropertyFlag.AllForbidden));
+            r.SetOwnProperty("ignoreCase", new PropertyDescriptor(r.IgnoreCase, PropertyFlag.AllForbidden));
+            r.SetOwnProperty("multiline", new PropertyDescriptor(r.Multiline, PropertyFlag.AllForbidden));
+            r.SetOwnProperty("source", new PropertyDescriptor(r.Source, PropertyFlag.AllForbidden));
+            r.SetOwnProperty("lastIndex", new PropertyDescriptor(0, PropertyFlag.OnlyWritable));
         }
 
-        private void AssignFlags(RegExpInstance r, string flags)
+        private static void AssignFlags(RegExpInstance r, string flags)
         {
             for(var i=0; i < flags.Length; i++)
             {

+ 1 - 10
Jint/Native/RegExp/RegExpInstance.cs

@@ -6,20 +6,11 @@ namespace Jint.Native.RegExp
     public class RegExpInstance : ObjectInstance
     {
         public RegExpInstance(Engine engine)
-            : base(engine)
+            : base(engine, objectClass: "RegExp")
         {
         }
 
-        public override string Class
-        {
-            get
-            {
-                return "RegExp";
-            }
-        }
-
         public Regex Value { get; set; }
-        //public string Pattern { get; set; }
         public string Source { get; set; }
         public string Flags { get; set; }
         public bool Global { get; set; }

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

@@ -2,7 +2,6 @@
 using Jint.Native.Array;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
-using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.RegExp
@@ -37,7 +36,7 @@ namespace Jint.Native.RegExp
             FastAddProperty("lastIndex", 0, true, false, false);
         }
 
-        private JsValue ToRegExpString(JsValue thisObj, JsValue[] arguments)
+        private static JsValue ToRegExpString(JsValue thisObj, JsValue[] arguments)
         {
             var regExp = thisObj.TryCast<RegExpInstance>();
 
@@ -57,13 +56,13 @@ namespace Jint.Native.RegExp
             }
 
             var match = Exec(r, arguments);
-            return match != Null;
+            return !match.IsNull();
         }
 
         internal JsValue Exec(JsValue thisObj, JsValue[] arguments)
         {
             var R = TypeConverter.ToObject(Engine, thisObj) as RegExpInstance;
-            if (R == null)
+            if (ReferenceEquals(R, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -83,7 +82,7 @@ namespace Jint.Native.RegExp
             {
                 // "aaa".match() => [ '', index: 0, input: 'aaa' ]
                 var aa = InitReturnValueArray((ArrayInstance) Engine.Array.Construct(Arguments.Empty), s, 1, 0);
-                aa.DefineOwnProperty("0", new PropertyDescriptor("", true, true, true), true);
+                aa.DefineOwnProperty("0", new PropertyDescriptor("", PropertyFlag.ConfigurableEnumerableWritable), true);
                 return aa;
             }
 
@@ -117,17 +116,18 @@ namespace Jint.Native.RegExp
             {
                 var group = r.Groups[(int) k];
                 var value = group.Success ? group.Value : Undefined;
-                a.SetIndexValue(k, value, throwOnError: true);
+                a.SetIndexValue(k, value, updateLength: false);
             }
 
+            a.SetLength((uint) n);
             return a;
         }
 
         private static ArrayInstance InitReturnValueArray(ArrayInstance array, string inputValue, int lengthValue, int indexValue)
         {
-            array.SetOwnProperty("index", new ConfigurableEnumerableWritablePropertyDescriptor(indexValue));
-            array.SetOwnProperty("input", new ConfigurableEnumerableWritablePropertyDescriptor(inputValue));
-            array.SetOwnProperty("length", new WritablePropertyDescriptor(lengthValue));
+            array.SetOwnProperty("index", new PropertyDescriptor(indexValue, PropertyFlag.ConfigurableEnumerableWritable));
+            array.SetOwnProperty("input", new PropertyDescriptor(inputValue, PropertyFlag.ConfigurableEnumerableWritable));
+            array.SetOwnProperty("length", new PropertyDescriptor(lengthValue, PropertyFlag.OnlyWritable));
             return array;
         }
     }

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

@@ -1,7 +1,7 @@
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.String
@@ -22,17 +22,17 @@ namespace Jint.Native.String
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = StringPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
+            obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));
 
             // The initial value of String.prototype is the String prototype object
-            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
 
             return obj;
         }
 
         public void Configure()
         {
-            SetOwnProperty("fromCharCode", new NonEnumerablePropertyDescriptor(new ClrFunctionInstance(Engine, FromCharCode, 1)));
+            SetOwnProperty("fromCharCode", new PropertyDescriptor(new ClrFunctionInstance(Engine, FromCharCode, 1), PropertyFlag.NonEnumerable));
         }
 
         private static JsValue FromCharCode(JsValue thisObj, JsValue[] arguments)
@@ -75,7 +75,7 @@ namespace Jint.Native.String
             instance.PrimitiveValue = value;
             instance.Extensible = true;
 
-            instance.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(value.Length));
+            instance.SetOwnProperty("length", new PropertyDescriptor(value.Length, PropertyFlag.AllForbidden));
 
             return instance;
         }

+ 16 - 17
Jint/Native/String/StringInstance.cs

@@ -2,22 +2,21 @@
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
-using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Native.String
 {
     public class StringInstance : ObjectInstance, IPrimitiveInstance
     {
         private const string PropertyNameLength = "length";
-        private IPropertyDescriptor _length;
+        private const int PropertyNameLengthLength = 6;
+
+        private PropertyDescriptor _length;
 
         public StringInstance(Engine engine)
-            : base(engine)
+            : base(engine, objectClass: "String")
         {
         }
 
-        public override string Class => "String";
-
         Types IPrimitiveInstance.Type => Types.String;
 
         JsValue IPrimitiveInstance.PrimitiveValue => PrimitiveValue;
@@ -35,14 +34,14 @@ namespace Jint.Native.String
             return false;
         }
 
-        public override IPropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(string propertyName)
         {
-            if (propertyName == "Infinity")
+            if (propertyName.Length == 8 && propertyName == "Infinity")
             {
                 return PropertyDescriptor.Undefined;
             }
 
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 return _length ?? PropertyDescriptor.Undefined;
             }
@@ -65,21 +64,21 @@ namespace Jint.Native.String
                 return PropertyDescriptor.Undefined;
 
             var index = (int) dIndex;
-            var len = str.AsString().Length;
+            var len = str.AsStringWithoutTypeCheck().Length;
             if (len <= index || index < 0)
             {
                 return PropertyDescriptor.Undefined;
             }
 
-            var resultStr = TypeConverter.ToString(str.AsString()[index]);
-            return new EnumerablePropertyDescriptor(resultStr);
+            var resultStr = TypeConverter.ToString(str.AsStringWithoutTypeCheck()[index]);
+            return new PropertyDescriptor(resultStr, PropertyFlag.OnlyEnumerable);
         }
 
-        public override IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
+        public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
         {
             if (_length != null)
             {
-                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameLength, _length);
+                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameLength, _length);
             }
 
             foreach (var entry in base.GetOwnProperties())
@@ -88,9 +87,9 @@ namespace Jint.Native.String
             }
         }
 
-        protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
+        protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
         {
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 _length = desc;
             }
@@ -102,7 +101,7 @@ namespace Jint.Native.String
 
         public override bool HasOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 return _length != null;
             }
@@ -112,7 +111,7 @@ namespace Jint.Native.String
 
         public override void RemoveOwnProperty(string propertyName)
         {
-            if (propertyName == PropertyNameLength)
+            if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
             {
                 _length = null;
             }

+ 60 - 41
Jint/Native/String/StringPrototype.cs

@@ -6,7 +6,7 @@ using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Native.RegExp;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.String
@@ -27,8 +27,8 @@ namespace Jint.Native.String
             obj.Prototype = engine.Object.PrototypeObject;
             obj.PrimitiveValue = "";
             obj.Extensible = true;
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(0));
-            obj.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(stringConstructor));
+            obj.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden));
+            obj.SetOwnProperty("constructor", new PropertyDescriptor(stringConstructor, PropertyFlag.NonEnumerable));
 
             return obj;
         }
@@ -63,7 +63,7 @@ namespace Jint.Native.String
         private JsValue ToStringString(JsValue thisObj, JsValue[] arguments)
         {
             var s = TypeConverter.ToObject(Engine, thisObj) as StringInstance;
-            if (s == null)
+            if (ReferenceEquals(s, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -77,7 +77,7 @@ namespace Jint.Native.String
         const char MONGOLIAN_VOWEL_SEPARATOR = '\u180E';
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static bool IsWhiteSpaceEx(char c)
+        internal static bool IsWhiteSpaceEx(char c)
         {
             return
                 char.IsWhiteSpace(c) ||
@@ -86,6 +86,7 @@ namespace Jint.Native.String
                 c == MONGOLIAN_VOWEL_SEPARATOR;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static string TrimEndEx(string s)
         {
             if (s.Length == 0)
@@ -94,6 +95,11 @@ namespace Jint.Native.String
             if (!IsWhiteSpaceEx(s[s.Length - 1]))
                 return s;
 
+            return TrimEnd(s);
+        }
+
+        private static string TrimEnd(string s)
+        {
             var i = s.Length - 1;
             while (i >= 0)
             {
@@ -102,12 +108,11 @@ namespace Jint.Native.String
                 else
                     break;
             }
-            if (i >= 0)
-                return s.Substring(0, i + 1);
-            else
-                return string.Empty;
+
+            return i >= 0 ? s.Substring(0, i + 1) : string.Empty;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static string TrimStartEx(string s)
         {
             if (s.Length == 0)
@@ -116,6 +121,11 @@ namespace Jint.Native.String
             if (!IsWhiteSpaceEx(s[0]))
                 return s;
 
+            return TrimStart(s);
+        }
+
+        private static string TrimStart(string s)
+        {
             var i = 0;
             while (i < s.Length)
             {
@@ -124,17 +134,17 @@ namespace Jint.Native.String
                 else
                     break;
             }
-            if (i >= s.Length)
-                return string.Empty;
-            else
-                return s.Substring(i);
+
+            return i >= s.Length ? string.Empty : s.Substring(i);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static string TrimEx(string s)
         {
             return TrimEndEx(TrimStartEx(s));
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private JsValue Trim(JsValue thisObj, JsValue[] arguments)
         {
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
@@ -200,7 +210,7 @@ namespace Jint.Native.String
             var len = s.Length;
             var intStart = ToIntegerSupportInfinity(start);
 
-            var intEnd = ReferenceEquals(arguments.At(1), Undefined) ? len : ToIntegerSupportInfinity(end);
+            var intEnd = arguments.At(1).IsUndefined() ? len : ToIntegerSupportInfinity(end);
             var finalStart = System.Math.Min(len, System.Math.Max(intStart, 0));
             var finalEnd = System.Math.Min(len, System.Math.Max(intEnd, 0));
             // Swap value if finalStart < finalEnd
@@ -221,11 +231,11 @@ namespace Jint.Native.String
             return s.Substring(from, length);
         }
 
-        private JsValue Substr(JsValue thisObj, JsValue[] arguments)
+        private static JsValue Substr(JsValue thisObj, JsValue[] arguments)
         {
             var s = TypeConverter.ToString(thisObj);
             var start = TypeConverter.ToInteger(arguments.At(0));
-            var length = arguments.At(1) == JsValue.Undefined
+            var length = arguments.At(1).IsUndefined()
                 ? double.PositiveInfinity
                 : TypeConverter.ToInteger(arguments.At(1));
 
@@ -254,7 +264,7 @@ namespace Jint.Native.String
 
             // Coerce into a number, true will become 1
             var l = arguments.At(1);
-            var limit = ReferenceEquals(l, Undefined) ? uint.MaxValue : TypeConverter.ToUint32(l);
+            var limit = l.IsUndefined() ? uint.MaxValue : TypeConverter.ToUint32(l);
             var len = s.Length;
 
             if (limit == 0)
@@ -262,13 +272,17 @@ namespace Jint.Native.String
                 return Engine.Array.Construct(Arguments.Empty);
             }
 
-            if (ReferenceEquals(separator, Null))
+            if (separator.IsNull())
             {
                 separator = Native.Null.Text;
             }
-            else if (ReferenceEquals(separator, Undefined))
+            else if (separator.IsUndefined())
             {
-                return (ArrayInstance)Engine.Array.Construct(Arguments.From(s));
+                var jsValues = Engine.JsValueArrayPool.RentArray(1);
+                jsValues[0] = s;
+                var arrayInstance = (ArrayInstance)Engine.Array.Construct(jsValues);
+                Engine.JsValueArrayPool.ReturnArray(jsValues);
+                return arrayInstance;
             }
             else
             {
@@ -282,7 +296,7 @@ namespace Jint.Native.String
 
             const string regExpForMatchingAllCharactere = "(?:)";
 
-            if (rx != null &&
+            if (!ReferenceEquals(rx, null) &&
                 rx.Source != regExpForMatchingAllCharactere // We need pattern to be defined -> for s.split(new RegExp)
                 )
             {
@@ -291,7 +305,7 @@ namespace Jint.Native.String
 
                 if (!match.Success) // No match at all return the string in an array
                 {
-                    a.SetIndexValue(0, s, throwOnError: false);
+                    a.SetIndexValue(0, s, updateLength: true);
                     return a;
                 }
 
@@ -306,7 +320,7 @@ namespace Jint.Native.String
                     }
 
                     // Add the match results to the array.
-                    a.SetIndexValue(index++, s.Substring(lastIndex, match.Index - lastIndex), throwOnError: false);
+                    a.SetIndexValue(index++, s.Substring(lastIndex, match.Index - lastIndex), updateLength: true);
 
                     if (index >= limit)
                     {
@@ -323,7 +337,7 @@ namespace Jint.Native.String
                             item = match.Groups[i].Value;
                         }
 
-                        a.SetIndexValue(index++, item, throwOnError: false);
+                        a.SetIndexValue(index++, item, updateLength: true);
 
                         if (index >= limit)
                         {
@@ -334,7 +348,7 @@ namespace Jint.Native.String
                     match = match.NextMatch();
                     if (!match.Success) // Add the last part of the split
                     {
-                        a.SetIndexValue(index++, s.Substring(lastIndex), throwOnError: false);
+                        a.SetIndexValue(index++, s.Substring(lastIndex), updateLength: true);
                     }
                 }
 
@@ -346,7 +360,7 @@ namespace Jint.Native.String
                 segments.Clear();
                 var sep = TypeConverter.ToString(separator);
 
-                if (sep == string.Empty || (rx != null && rx.Source == regExpForMatchingAllCharactere)) // for s.split(new RegExp)
+                if (sep == string.Empty || (!ReferenceEquals(rx, null) && rx.Source == regExpForMatchingAllCharactere)) // for s.split(new RegExp)
                 {
                     if (s.Length > segments.Capacity)
                     {
@@ -365,11 +379,13 @@ namespace Jint.Native.String
                     segments.AddRange(s.Split(array, StringSplitOptions.None));
                 }
 
-                var a = Engine.Array.Construct(Arguments.Empty, (uint) segments.Count);
-                for (int i = 0; i < segments.Count && i < limit; i++)
+                var length = (uint) System.Math.Min(segments.Count, limit);
+                var a = Engine.Array.ConstructFast(length);
+                for (int i = 0; i < length; i++)
                 {
-                    a.SetIndexValue((uint) i, segments[i], throwOnError: false);
+                    a.SetIndexValue((uint) i, segments[i], updateLength: false);
                 }
+                a.SetLength(length);
                 return a;
             }
         }
@@ -397,7 +413,7 @@ namespace Jint.Native.String
 
             var len = s.Length;
             var intStart = (int)TypeConverter.ToInteger(start);
-            var intEnd = ReferenceEquals(arguments.At(1), Undefined) ? len : (int)TypeConverter.ToInteger(end);
+            var intEnd = arguments.At(1).IsUndefined() ? len : (int)TypeConverter.ToInteger(end);
             var from = intStart < 0 ? System.Math.Max(len + intStart, 0) : System.Math.Min(intStart, len);
             var to = intEnd < 0 ? System.Math.Max(len + intEnd, 0) : System.Math.Min(intEnd, len);
             var span = System.Math.Max(to - from, 0);
@@ -452,7 +468,7 @@ namespace Jint.Native.String
 
             // If the second parameter is not a function we create one
             var replaceFunction = replaceValue.TryCast<FunctionInstance>();
-            if (replaceFunction == null)
+            if (ReferenceEquals(replaceFunction, null))
             {
                 replaceFunction = new ClrFunctionInstance(Engine, (self, args) =>
                 {
@@ -544,7 +560,7 @@ namespace Jint.Native.String
             }
 
             var rx = TypeConverter.ToObject(Engine, searchValue) as RegExpInstance;
-            if (rx != null)
+            if (!ReferenceEquals(rx, null))
             {
                 // Replace the input string with replaceText, recording the last match found.
                 string result = rx.Value.Replace(thisString, match =>
@@ -581,12 +597,14 @@ namespace Jint.Native.String
                     return thisString;
                 int end = start + substr.Length;
 
-                var args = new JsValue[3];
+                var args = Engine.JsValueArrayPool.RentArray(3);
                 args[0] = substr;
                 args[1] = start;
                 args[2] = thisString;
 
                 var replaceString = TypeConverter.ToString(replaceFunction.Call(Undefined, args));
+                
+                Engine.JsValueArrayPool.ReturnArray(args);
 
                 // Replace only the first match.
                 var result = StringExecutionContext.Current.GetStringBuilder(thisString.Length + (substr.Length - substr.Length));
@@ -609,7 +627,7 @@ namespace Jint.Native.String
 
             rx = rx ?? (RegExpInstance) Engine.RegExp.Construct(new[] {regex});
 
-            var global = rx.Get("global").AsBoolean();
+            var global = ((JsBoolean) rx.Get("global"))._value;
             if (!global)
             {
                 return Engine.RegExp.PrototypeObject.Exec(rx, Arguments.From(s));
@@ -624,13 +642,13 @@ namespace Jint.Native.String
                 while (lastMatch)
                 {
                     var result = Engine.RegExp.PrototypeObject.Exec(rx, Arguments.From(s)).TryCast<ObjectInstance>();
-                    if (result == null)
+                    if (ReferenceEquals(result, null))
                     {
                         lastMatch = false;
                     }
                     else
                     {
-                        var thisIndex = rx.Get("lastIndex").AsNumber();
+                        var thisIndex = ((JsNumber) rx.Get("lastIndex"))._value;
                         if (thisIndex == previousLastIndex)
                         {
                             rx.Put("lastIndex", thisIndex + 1, false);
@@ -638,7 +656,7 @@ namespace Jint.Native.String
                         }
 
                         var matchStr = result.Get("0");
-                        a.SetIndexValue(n, matchStr, throwOnError: false);
+                        a.SetIndexValue(n, matchStr, updateLength: false);
                         n++;
                     }
                 }
@@ -646,6 +664,7 @@ namespace Jint.Native.String
                 {
                     return Null;
                 }
+                a.SetLength(n);
                 return a;
             }
 
@@ -668,7 +687,7 @@ namespace Jint.Native.String
             var s = TypeConverter.ToString(thisObj);
             var searchStr = TypeConverter.ToString(arguments.At(0));
             double numPos = double.NaN;
-            if (arguments.Length > 1 && arguments[1] != Undefined)
+            if (arguments.Length > 1 && !arguments[1].IsUndefined())
             {
                 numPos = TypeConverter.ToNumber(arguments[1]);
             }
@@ -715,7 +734,7 @@ namespace Jint.Native.String
             var s = TypeConverter.ToString(thisObj);
             var searchStr = TypeConverter.ToString(arguments.At(0));
             double pos = 0;
-            if (arguments.Length > 1 && arguments[1] != Undefined)
+            if (arguments.Length > 1 && !arguments[1].IsUndefined())
             {
                 pos = TypeConverter.ToInteger(arguments[1]);
             }
@@ -743,7 +762,7 @@ namespace Jint.Native.String
             {
                 if (arguments[i].Type == Types.String)
                 {
-                    capacity += arguments[i].AsString().Length;
+                    capacity += arguments[i].AsStringWithoutTypeCheck().Length;
                 }
             }
 
@@ -797,7 +816,7 @@ namespace Jint.Native.String
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
         {
             var s = thisObj.TryCast<StringInstance>();
-            if (s == null)
+            if (ReferenceEquals(s, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }

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

@@ -1,7 +1,7 @@
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Symbol
@@ -26,10 +26,10 @@ namespace Jint.Native.Symbol
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = SymbolPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(0));
+            obj.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden));
 
             // The initial value of String.prototype is the String prototype object
-            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
 
 
             return obj;
@@ -60,7 +60,7 @@ namespace Jint.Native.Symbol
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
             var description = arguments.At(0);
-            var descString = ReferenceEquals(description, Undefined)
+            var descString = description.IsUndefined()
                 ? Undefined
                 : TypeConverter.ToString(description);
 
@@ -80,8 +80,7 @@ namespace Jint.Native.Symbol
 
             // 2. ReturnIfAbrupt(stringKey).
 
-            JsSymbol symbol;
-            if (!Engine.GlobalSymbolRegistry.TryGetValue(stringKey, out symbol))
+            if (!Engine.GlobalSymbolRegistry.TryGetValue(stringKey, out var symbol))
             {
                 symbol = new JsSymbol(stringKey);
                 Engine.GlobalSymbolRegistry.Add(stringKey, symbol);

+ 3 - 17
Jint/Native/Symbol/SymbolInstance.cs

@@ -6,27 +6,13 @@ namespace Jint.Native.Symbol
     public class SymbolInstance : ObjectInstance, IPrimitiveInstance
     {
         public SymbolInstance(Engine engine)
-            : base(engine)
+            : base(engine, objectClass: "Symbol")
         {
         }
 
-        public override string Class
-        {
-            get
-            {
-                return "Symbol";
-            }
-        }
+        Types IPrimitiveInstance.Type => Types.Symbol;
 
-        Types IPrimitiveInstance.Type
-        {
-            get { return Types.Symbol; }
-        }
-
-        JsValue IPrimitiveInstance.PrimitiveValue
-        {
-            get { return SymbolData; }
-        }
+        JsValue IPrimitiveInstance.PrimitiveValue => SymbolData;
 
         public JsSymbol SymbolData { get; set; }
     }

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

@@ -1,6 +1,6 @@
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Symbol
@@ -20,7 +20,7 @@ namespace Jint.Native.Symbol
             var obj = new SymbolPrototype(engine);
             obj.Prototype = engine.Object.PrototypeObject;
             obj.Extensible = true;
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(0));
+            obj.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden));
             obj.FastAddProperty("constructor", symbolConstructor, true, false, true);
 
             return obj;
@@ -54,7 +54,7 @@ namespace Jint.Native.Symbol
         private JsValue ValueOf(JsValue thisObject, JsValue[] arguments)
         {
             var sym = thisObject.TryCast<SymbolInstance>();
-            if (sym == null)
+            if (ReferenceEquals(sym, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -71,7 +71,7 @@ namespace Jint.Native.Symbol
 
             // Steps 3. and 4.
             var o = thisObject.AsInstance<SymbolInstance>();
-            if (o == null)
+            if (ReferenceEquals(o, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }

+ 19 - 7
Jint/Options.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 using Jint.Native;
 using Jint.Runtime.Interop;
 
@@ -13,10 +14,10 @@ namespace Jint
         private bool _discardGlobal;
         private bool _strict;
         private bool _allowDebuggerStatement;
-        private bool _debugMode;
         private bool _allowClr;
         private readonly List<IObjectConverter> _objectConverters = new List<IObjectConverter>();
         private int _maxStatements;
+        private long _memoryLimit;
         private int _maxRecursionDepth = -1;
         private TimeSpan _timeoutInterval;
         private CultureInfo _culture = CultureInfo.CurrentCulture;
@@ -62,7 +63,7 @@ namespace Jint
         /// </summary>
         public Options DebugMode(bool debugMode = true)
         {
-            _debugMode = debugMode;
+            IsDebugMode = debugMode;
             return this;
         }
 
@@ -113,6 +114,11 @@ namespace Jint
             _maxStatements = maxStatements;
             return this;
         }
+        public Options LimitMemory(long memoryLimit)
+        {
+            _memoryLimit = memoryLimit;
+            return this;
+        }
 
         public Options TimeoutInterval(TimeSpan timeoutInterval)
         {
@@ -155,11 +161,11 @@ namespace Jint
 
         internal bool _IsGlobalDiscarded => _discardGlobal;
 
-        internal bool _IsStrict => _strict;
+        internal bool IsStrict => _strict;
 
         internal bool _IsDebuggerStatementAllowed => _allowDebuggerStatement;
 
-        internal bool _IsDebugMode => _debugMode;
+        internal bool IsDebugMode { get; private set; }
 
         internal bool _IsClrAllowed => _allowClr;
 
@@ -169,9 +175,15 @@ namespace Jint
 
         internal List<IObjectConverter> _ObjectConverters => _objectConverters;
 
-        internal int _MaxStatements => _maxStatements;
+        internal long _MemoryLimit => _memoryLimit;
+
+        internal int _MaxStatements
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get { return _maxStatements; }
+        }
 
-        internal int _MaxRecursionDepth => _maxRecursionDepth;
+        internal int MaxRecursionDepth => _maxRecursionDepth;
 
         internal TimeSpan _TimeoutInterval => _timeoutInterval;
 
@@ -179,7 +191,7 @@ namespace Jint
 
         internal TimeZoneInfo _LocalTimeZone => _localTimeZone;
 
-        internal IReferenceResolver  _ReferenceResolver => _referenceResolver;
+        internal IReferenceResolver  ReferenceResolver => _referenceResolver;
 
     }
 }

+ 1 - 1
Jint/Pooling/ArgumentsInstancePool.cs

@@ -47,7 +47,7 @@ namespace Jint.Pooling
 
         public void Return(ArgumentsInstance instance)
         {
-            if (instance == null)
+            if (ReferenceEquals(instance, null))
             {
                 return;
             }

+ 0 - 41
Jint/Pooling/CompletionPool.cs

@@ -1,41 +0,0 @@
-using Esprima;
-using Jint.Native;
-using Jint.Runtime;
-
-namespace Jint.Pooling
-{
-    /// <summary>
-    /// Cache reusable <see cref="Completion" /> instances as we allocate them a lot.
-    /// </summary>
-    internal sealed class CompletionPool
-    {
-        private const int PoolSize = 15;
-        private readonly ObjectPool<Completion> _pool;
-
-        public CompletionPool()
-        {
-            _pool = new ObjectPool<Completion>(Factory, PoolSize);
-        }
-
-        private static Completion Factory()
-        {
-            return new Completion(string.Empty, JsValue.Undefined, string.Empty);
-        }
-        
-        public Completion Rent(string type, JsValue value, string identifier, Location location = null)
-        {
-            return _pool.Allocate().Reassign(type, value, identifier, location);
-        }
-
-        public void Return(Completion completion)
-        {
-            if (completion == null
-                || completion == Completion.Empty
-                || completion == Completion.EmptyUndefined)
-            {
-                return;
-            }
-            _pool.Free(completion);;
-        }
-    }
-}

+ 79 - 0
Jint/Pooling/JsValueArrayPool.cs

@@ -0,0 +1,79 @@
+using System;
+using System.Runtime.CompilerServices;
+using Jint.Native;
+
+namespace Jint.Pooling
+{
+    /// <summary>
+    /// Cache reusable <see cref="JsValue" /> array instances as we allocate them a lot.
+    /// </summary>
+    internal sealed class JsValueArrayPool
+    {
+        private const int PoolSize = 15;
+        private readonly ObjectPool<JsValue[]> _poolArray1;
+        private readonly ObjectPool<JsValue[]> _poolArray2;
+        private readonly ObjectPool<JsValue[]> _poolArray3;
+
+        public JsValueArrayPool()
+        {
+            _poolArray1 = new ObjectPool<JsValue[]>(Factory1, PoolSize);
+            _poolArray2 = new ObjectPool<JsValue[]>(Factory2, PoolSize);
+            _poolArray3 = new ObjectPool<JsValue[]>(Factory3, PoolSize);
+        }
+
+        private static JsValue[] Factory1()
+        {
+            return new JsValue[1];
+        }
+
+        private static JsValue[] Factory2()
+        {
+            return new JsValue[2];
+        }
+
+        private static JsValue[] Factory3()
+        {
+            return new JsValue[3];
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public JsValue[] RentArray(int size)
+        {
+            if (size == 0)
+            {
+                return Array.Empty<JsValue>();
+            }
+            if (size == 1)
+            {
+                return _poolArray1.Allocate();
+            }
+            if (size == 2)
+            {
+                return _poolArray2.Allocate();
+            }
+            if (size == 3)
+            {
+                return _poolArray3.Allocate();
+            }
+
+            return new JsValue[size];
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void ReturnArray(JsValue[] array)
+        {
+            if (array.Length == 1)
+            {
+                _poolArray1.Free(array);
+            }
+            else if (array.Length == 2)
+            {
+                _poolArray2.Free(array);
+            }
+            else if (array.Length == 3)
+            {
+                _poolArray3.Free(array);
+            }
+        }
+    }
+}

+ 1 - 1
Jint/Runtime/Arguments.cs

@@ -6,7 +6,7 @@ namespace Jint.Runtime
 {
     public static class Arguments
     {
-        public static JsValue[] Empty = new JsValue[0];
+        public static readonly JsValue[] Empty = Array.Empty<JsValue>();
 
         public static JsValue[] From(params JsValue[] o)
         {

+ 20 - 41
Jint/Runtime/Completion.cs

@@ -1,26 +1,24 @@
-using System;
+using System.Runtime.CompilerServices;
 using Esprima;
 using Jint.Native;
 
 namespace Jint.Runtime
 {
+    public enum CompletionType
+    {
+        Normal,
+        Break,
+        Continue,
+        Return,
+        Throw
+    }
+
     /// <summary>
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.9
     /// </summary>
-    public sealed class Completion
+    public readonly struct Completion
     {
-        public const string Normal = "normal";
-        public const string Break = "break";
-        public const string Continue = "continue";
-        public const string Return = "return";
-        public const string Throw = "throw";
-
-        internal static readonly Completion Empty = new Completion(Normal, null, null).Freeze();
-        internal static readonly Completion EmptyUndefined = new Completion(Normal, Undefined.Instance, null).Freeze();
-
-        private bool _frozen;
-
-        public Completion(string type, JsValue value, string identifier, Location location = null)
+        public Completion(CompletionType type, JsValue value, string identifier, Location location = null)
         {
             Type = type;
             Value = value;
@@ -28,40 +26,21 @@ namespace Jint.Runtime
             Location = location;
         }
 
-        public string Type { get; private set; }
-        public JsValue Value { get; private set; }
-        public string Identifier { get; private set; }
+        public readonly CompletionType Type;
+        public readonly JsValue Value;
+        public readonly string Identifier;
+        public readonly Location Location;
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue GetValueOrDefault()
         {
-            return Value != null ? Value : Undefined.Instance;
+            return Value ?? Undefined.Instance;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsAbrupt()
         {
-            return Type != Normal;
-        }
-
-        public Location Location { get; private set; }
-
-        private Completion Freeze()
-        {
-            _frozen = true;
-            return this;
-        }
-
-        public Completion Reassign(string type, JsValue value, string identifier, Location location = null)
-        {
-            if (_frozen)
-            {
-                throw new InvalidOperationException("object is frozen");
-            }
-            Type = type;
-            Value = value;
-            Identifier = identifier;
-            Location = location;
-
-            return this;
+            return Type != CompletionType.Normal;
         }
     }
 }

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

@@ -138,7 +138,8 @@ namespace Jint.Runtime.Debugger
 
             if (!string.IsNullOrEmpty(breakpoint.Condition))
             {
-                return _engine.Execute(breakpoint.Condition).GetCompletionValue().AsBoolean();
+                var completionValue = _engine.Execute(breakpoint.Condition).GetCompletionValue();
+                return ((JsBoolean) completionValue)._value;
             }
 
             return true;
@@ -148,7 +149,7 @@ namespace Jint.Runtime.Debugger
         {
             var info = new DebugInformation { CurrentStatement = statement, CallStack = _debugCallStack };
 
-            if (_engine.ExecutionContext != null && _engine.ExecutionContext.LexicalEnvironment != null)
+            if (_engine.ExecutionContext.LexicalEnvironment != null)
             {
                 var lexicalEnvironment = _engine.ExecutionContext.LexicalEnvironment;
                 info.Locals = GetLocalVariables(lexicalEnvironment);
@@ -161,7 +162,7 @@ namespace Jint.Runtime.Debugger
         private static Dictionary<string, JsValue> GetLocalVariables(LexicalEnvironment lex)
         {
             Dictionary<string, JsValue> locals = new Dictionary<string, JsValue>();
-            if (lex != null && lex.Record != null)
+            if (!ReferenceEquals(lex?.Record, null))
             {
                 AddRecordsFromEnvironment(lex, locals);
             }
@@ -173,7 +174,7 @@ namespace Jint.Runtime.Debugger
             Dictionary<string, JsValue> globals = new Dictionary<string, JsValue>();
             LexicalEnvironment tempLex = lex;
 
-            while (tempLex != null && tempLex.Record != null)
+            while (tempLex != null && !ReferenceEquals(tempLex.Record, null))
             {
                 AddRecordsFromEnvironment(tempLex, globals);
                 tempLex = tempLex.Outer;

+ 0 - 16
Jint/Runtime/Descriptors/IPropertyDescriptor .cs

@@ -1,16 +0,0 @@
-using Jint.Native;
-
-namespace Jint.Runtime.Descriptors
-{
-    public interface IPropertyDescriptor
-    {
-        JsValue Get { get; }
-        JsValue Set { get; }
-
-        bool? Enumerable { get; }
-        bool? Writable { get; }
-        bool? Configurable { get; }
-
-        JsValue Value { get; set; }
-    }
-}

+ 271 - 57
Jint/Runtime/Descriptors/PropertyDescriptor.cs

@@ -1,130 +1,290 @@
-using Jint.Native;
+using System;
+using System.Runtime.CompilerServices;
+using Jint.Native;
 using Jint.Native.Object;
 using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Runtime.Descriptors
 {
-    public class PropertyDescriptor : IPropertyDescriptor
+    public class PropertyDescriptor
     {
-        public static readonly IPropertyDescriptor Undefined = new PropertyDescriptor();
+        public static readonly PropertyDescriptor Undefined = new PropertyDescriptor(PropertyFlag.None);
 
-        protected PropertyDescriptor()
+        private PropertyFlag _flags;
+        private JsValue _value;
+
+        protected PropertyDescriptor(PropertyFlag flags)
+        {
+            _flags = flags;
+        }
+        
+        
+        protected internal PropertyDescriptor(JsValue value, PropertyFlag flags) : this(flags)
         {
+            Value = value;
         }
 
         public PropertyDescriptor(JsValue value, bool? writable, bool? enumerable, bool? configurable)
         {
             Value = value;
 
-            if (writable.HasValue)
+            if (writable != null)
             {
                 Writable = writable.Value;
+                WritableSet = true;
             }
 
-            if (enumerable.HasValue)
+            if (enumerable != null)
             {
                 Enumerable = enumerable.Value;
+                EnumerableSet = true;
             }
 
-            if (configurable.HasValue)
+            if (configurable != null)
             {
                 Configurable = configurable.Value;
+                ConfigurableSet = true;
             }
         }
 
-        public PropertyDescriptor(JsValue get, JsValue set, bool? enumerable = null, bool? configurable = null)
+        public PropertyDescriptor(PropertyDescriptor descriptor)
         {
-            Get = get;
-            Set = set;
+            Value = descriptor.Value;
 
-            if (enumerable.HasValue)
+            Enumerable = descriptor.Enumerable;
+            EnumerableSet = descriptor.EnumerableSet;
+            
+            Configurable = descriptor.Configurable;
+            ConfigurableSet = descriptor.ConfigurableSet;
+
+            Writable = descriptor.Writable;
+            WritableSet = descriptor.WritableSet;
+        }
+
+        public virtual JsValue Get => null;
+        public virtual JsValue Set => null;
+
+        public bool Enumerable
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => (_flags & PropertyFlag.Enumerable) != 0;
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            set
             {
-                Enumerable = enumerable.Value;
+                _flags |= PropertyFlag.EnumerableSet;
+                if (value)
+                {
+                    _flags |= PropertyFlag.Enumerable;
+                }
+                else
+                {
+                    _flags &= ~(PropertyFlag.Enumerable);
+                }
+            }
+        }
+                
+        public bool EnumerableSet
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => (_flags & (PropertyFlag.EnumerableSet | PropertyFlag.Enumerable)) != 0;
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            private set
+            {
+                if (value)
+                {
+                    _flags |= PropertyFlag.EnumerableSet;
+                }
+                else
+                {
+                    _flags &= ~(PropertyFlag.EnumerableSet);
+                }
             }
+        }
 
-            if (configurable.HasValue)
+        public bool Writable
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => (_flags & PropertyFlag.Writable) != 0;
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            set
             {
-                Configurable = configurable.Value;
+                _flags |= PropertyFlag.WritableSet;
+                if (value)
+                {
+                    _flags |= PropertyFlag.Writable;
+                }
+                else
+                {
+                    _flags &= ~(PropertyFlag.Writable);
+                }
             }
         }
 
-        public PropertyDescriptor(IPropertyDescriptor descriptor)
+        public bool WritableSet
         {
-            Get = descriptor.Get;
-            Set = descriptor.Set;
-            Value = descriptor.Value;
-            Enumerable = descriptor.Enumerable;
-            Configurable = descriptor.Configurable;
-            Writable = descriptor.Writable;
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => (_flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != 0;
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            private set
+            {
+                if (value)
+                {
+                    _flags |= PropertyFlag.WritableSet;
+                }
+                else
+                {
+                    _flags &= ~(PropertyFlag.WritableSet);
+                }
+            }
+        }
+
+        public bool Configurable
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => (_flags & PropertyFlag.Configurable) != 0;
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            set
+            {
+                _flags |= PropertyFlag.ConfigurableSet;
+                if (value)
+                {
+                    _flags |= PropertyFlag.Configurable;
+                }
+                else
+                {
+                    _flags &= ~(PropertyFlag.Configurable);
+                }
+            }
+        }
+        
+        public bool ConfigurableSet
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => (_flags & (PropertyFlag.ConfigurableSet | PropertyFlag.Configurable)) != 0;
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            private set
+            {
+                if (value)
+                {
+                    _flags |= PropertyFlag.ConfigurableSet;
+                }
+                else
+                {
+                    _flags &= ~(PropertyFlag.ConfigurableSet);
+                }
+            }
+        }
+
+        public JsValue Value
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get
+            {
+                if ((_flags & PropertyFlag.CustomJsValue) != 0)
+                {
+                    return CustomValue;
+                }
+                
+                return _value;
+            }
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            set
+            {
+                if ((_flags & PropertyFlag.CustomJsValue) != 0)
+                {
+                    CustomValue = value;
+                }
+                _value = value;
+            }
         }
 
-        public JsValue Get { get; set; }
-        public JsValue Set { get; set; }
-        public bool? Enumerable { get; set; }
-        public bool? Writable { get; set; }
-        public bool? Configurable { get; set; }
-        public virtual JsValue Value { get; set; }
+        protected virtual JsValue CustomValue
+        {
+            get => null;
+            set => throw new NotImplementedException();
+        }
+
+        internal PropertyFlag Flags
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get { return _flags; }
+        }
 
         public static PropertyDescriptor ToPropertyDescriptor(Engine engine, JsValue o)
         {
             var obj = o.TryCast<ObjectInstance>();
-            if (obj == null)
+            if (ReferenceEquals(obj, null))
             {
                 throw new JavaScriptException(engine.TypeError);
             }
 
+            var getProperty = obj.GetProperty("get");
+            var hasGetProperty = getProperty != Undefined;
+            var setProperty = obj.GetProperty("set");
+            var hasSetProperty = setProperty != Undefined;
+            
             if ((obj.HasProperty("value") || obj.HasProperty("writable")) &&
-                (obj.HasProperty("get") || obj.HasProperty("set")))
+                (hasGetProperty || hasSetProperty))
             {
                 throw new JavaScriptException(engine.TypeError);
             }
 
-            var desc = new PropertyDescriptor();
+            var desc = hasGetProperty || hasSetProperty
+                ? new GetSetPropertyDescriptor(null, null, PropertyFlag.None)
+                : new PropertyDescriptor(PropertyFlag.None);
 
-            if (obj.HasProperty("enumerable"))
+            var enumerableProperty = obj.GetProperty("enumerable");
+            if (enumerableProperty != Undefined)
             {
-                desc.Enumerable = TypeConverter.ToBoolean(obj.Get("enumerable"));
+                desc.Enumerable = TypeConverter.ToBoolean(obj.UnwrapJsValue(enumerableProperty));
+                desc.EnumerableSet = true;
             }
 
-            if (obj.HasProperty("configurable"))
+            var configurableProperty = obj.GetProperty("configurable");
+            if (configurableProperty != Undefined)
             {
-                desc.Configurable = TypeConverter.ToBoolean(obj.Get("configurable"));
+                desc.Configurable = TypeConverter.ToBoolean(obj.UnwrapJsValue(configurableProperty));
+                desc.ConfigurableSet = true;
             }
 
-            if (obj.HasProperty("value"))
+            var valueProperty = obj.GetProperty("value");
+            if (valueProperty != Undefined)
             {
-                var value = obj.Get("value");
-                desc.Value = value;
+                desc.Value = obj.UnwrapJsValue(valueProperty);
             }
 
-            if (obj.HasProperty("writable"))
+            var writableProperty = obj.GetProperty("writable");
+            if (writableProperty != Undefined)
             {
-                desc.Writable = TypeConverter.ToBoolean(obj.Get("writable"));
+                desc.Writable = TypeConverter.ToBoolean(obj.UnwrapJsValue(writableProperty));
+                desc.WritableSet = true;
             }
 
-            if (obj.HasProperty("get"))
+            if (hasGetProperty)
             {
-                var getter = obj.Get("get");
-                if (getter != JsValue.Undefined && getter.TryCast<ICallable>() == null)
+                var getter = obj.UnwrapJsValue(getProperty);
+                if (!getter.IsUndefined() && getter.TryCast<ICallable>() == null)
                 {
                     throw new JavaScriptException(engine.TypeError);
                 }
-                desc.Get = getter;
+
+                ((GetSetPropertyDescriptor) desc).SetGet(getter);
             }
 
-            if (obj.HasProperty("set"))
+            if (hasSetProperty)
             {
-                var setter = obj.Get("set");
-                if (setter != Native.Undefined.Instance && setter.TryCast<ICallable>() == null)
+                var setter = obj.UnwrapJsValue(setProperty);
+                if (!setter.IsUndefined() && setter.TryCast<ICallable>() == null)
                 {
                     throw new JavaScriptException(engine.TypeError);
                 }
-                desc.Set = setter;
+
+                ((GetSetPropertyDescriptor) desc).SetSet(setter);
             }
 
-            if (desc.Get != null || desc.Get != null)
+            if (!ReferenceEquals(desc.Get, null))
             {
-                if (desc.Value != null || desc.Writable.HasValue)
+                if (!ReferenceEquals(desc.Value, null) || desc.WritableSet)
                 {
                     throw new JavaScriptException(engine.TypeError);
                 }
@@ -133,30 +293,84 @@ namespace Jint.Runtime.Descriptors
             return desc;
         }
 
-        public static JsValue FromPropertyDescriptor(Engine engine, IPropertyDescriptor desc)
+        public static JsValue FromPropertyDescriptor(Engine engine, PropertyDescriptor desc)
         {
             if (ReferenceEquals(desc, Undefined))
             {
                 return Native.Undefined.Instance;
             }
-
+            
             var obj = engine.Object.Construct(Arguments.Empty);
 
             if (desc.IsDataDescriptor())
             {
-                obj.SetOwnProperty("value", new ConfigurableEnumerableWritablePropertyDescriptor(desc.Value != null ? desc.Value : Native.Undefined.Instance));
-                obj.SetOwnProperty("writable", new ConfigurableEnumerableWritablePropertyDescriptor(desc.Writable.HasValue && desc.Writable.Value));
+                obj.SetOwnProperty("value", new PropertyDescriptor(desc.Value ?? Native.Undefined.Instance, PropertyFlag.ConfigurableEnumerableWritable));
+                obj.SetOwnProperty("writable", new PropertyDescriptor(desc.Writable, PropertyFlag.ConfigurableEnumerableWritable));
             }
             else
             {
-                obj.SetOwnProperty("get", new ConfigurableEnumerableWritablePropertyDescriptor(desc.Get ?? Native.Undefined.Instance));
-                obj.SetOwnProperty("set", new ConfigurableEnumerableWritablePropertyDescriptor(desc.Set ?? Native.Undefined.Instance));
+                obj.SetOwnProperty("get", new PropertyDescriptor(desc.Get ?? Native.Undefined.Instance, PropertyFlag.ConfigurableEnumerableWritable));
+                obj.SetOwnProperty("set", new PropertyDescriptor(desc.Set ?? Native.Undefined.Instance, PropertyFlag.ConfigurableEnumerableWritable));
             }
 
-            obj.SetOwnProperty("enumerable", new ConfigurableEnumerableWritablePropertyDescriptor(desc.Enumerable.HasValue && desc.Enumerable.Value));
-            obj.SetOwnProperty("configurable", new ConfigurableEnumerableWritablePropertyDescriptor(desc.Configurable.HasValue && desc.Configurable.Value));
+            obj.SetOwnProperty("enumerable", new PropertyDescriptor(desc.Enumerable, PropertyFlag.ConfigurableEnumerableWritable));
+            obj.SetOwnProperty("configurable", new PropertyDescriptor(desc.Configurable, PropertyFlag.ConfigurableEnumerableWritable));
 
             return obj;
         }
+        
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool IsAccessorDescriptor()
+        {
+            return !ReferenceEquals(Get, null) || !ReferenceEquals(Set, null);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool IsDataDescriptor()
+        {
+            return WritableSet || !ReferenceEquals(Value, null);
+        }
+
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.10.3
+        /// </summary>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool IsGenericDescriptor()
+        {
+            return !IsDataDescriptor() && !IsAccessorDescriptor();
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal bool TryGetValue(ObjectInstance thisArg, out JsValue value)
+        {
+            value = JsValue.Undefined;
+
+            if (this == Undefined)
+            {
+                value = JsValue.Undefined;
+                return false;
+            }
+
+            if (IsDataDescriptor())
+            {
+                var val = Value;
+                if (!ReferenceEquals(val, null))
+                {
+                    value = val;
+                    return true;
+                }
+            }
+
+            var getter = Get;
+            if (!ReferenceEquals(getter, null) && !getter.IsUndefined())
+            {
+                // if getter is not undefined it must be ICallable
+                var callable = getter.TryCast<ICallable>();
+                value = callable.Call(thisArg, Arguments.Empty);
+            }
+
+            return true;
+        }
     }
 }

+ 0 - 62
Jint/Runtime/Descriptors/PropertyDescriptorExtensions.cs

@@ -1,62 +0,0 @@
-using System.Runtime.CompilerServices;
-using Jint.Native;
-using Jint.Native.Object;
-
-namespace Jint.Runtime.Descriptors
-{
-    public static class PropertyDescriptorExtensions
-    {
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static bool IsAccessorDescriptor(this IPropertyDescriptor descriptor)
-        {
-            return descriptor.Get != null || descriptor.Set != null;
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static bool IsDataDescriptor(this IPropertyDescriptor descriptor)
-        {
-            return descriptor.Writable.HasValue || descriptor.Value != null;
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.10.3
-        /// </summary>
-        /// <returns></returns>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static bool IsGenericDescriptor(this IPropertyDescriptor descriptor)
-        {
-            return !descriptor.IsDataDescriptor() && !descriptor.IsAccessorDescriptor();
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static bool TryGetValue(this IPropertyDescriptor descriptor, ObjectInstance thisArg, out JsValue value)
-        {
-            value = JsValue.Undefined;
-
-            if (descriptor == PropertyDescriptor.Undefined)
-            {
-                value = JsValue.Undefined;
-                return false;
-            }
-
-            if (descriptor.IsDataDescriptor())
-            {
-                var val = descriptor.Value;
-                if (val != null)
-                {
-                    value = val;
-                    return true;
-                }
-            }
-
-            if (descriptor.Get != null && !descriptor.Get.IsUndefined())
-            {
-                // if getter is not undefined it must be ICallable
-                var callable = descriptor.Get.TryCast<ICallable>();
-                value = callable.Call(thisArg, Arguments.Empty);
-            }
-
-            return true;
-        }
-    }
-}

+ 26 - 0
Jint/Runtime/Descriptors/PropertyFlag.cs

@@ -0,0 +1,26 @@
+using System;
+
+namespace Jint.Runtime.Descriptors
+{
+    [Flags]
+    public enum PropertyFlag
+    {
+        None = 0,
+        Enumerable = 1,
+        EnumerableSet = 2,
+        Writable = 4,
+        WritableSet = 8,
+        Configurable = 16,
+        ConfigurableSet = 32,
+        
+        CustomJsValue = 256,
+        
+        // common helpers
+        AllForbidden = ConfigurableSet | EnumerableSet | WritableSet,
+        ConfigurableEnumerableWritable = Configurable | Enumerable | Writable,
+        NonConfigurable = ConfigurableSet | Enumerable | Writable,
+        OnlyEnumerable = Enumerable | ConfigurableSet | WritableSet,
+        NonEnumerable = Configurable | EnumerableSet | Writable,
+        OnlyWritable = EnumerableSet | Writable | ConfigurableSet
+    }
+}

+ 0 - 24
Jint/Runtime/Descriptors/Specialized/AllForbiddenPropertyDescriptor.cs

@@ -1,24 +0,0 @@
-using Jint.Native;
-
-namespace Jint.Runtime.Descriptors.Specialized
-{
-    /// <summary>
-    /// configurable = false, enumerable = false, writable = false.
-    /// </summary>
-    internal sealed class AllForbiddenPropertyDescriptor : IPropertyDescriptor
-    {
-        public AllForbiddenPropertyDescriptor(JsValue value)
-        {
-            Value = value;
-        }
-
-        public JsValue Get => null;
-        public JsValue Set => null;
-
-        public bool? Enumerable => false;
-        public bool? Writable => false;
-        public bool? Configurable => false;
-
-        public JsValue Value { get; set; }
-    }
-}

+ 4 - 9
Jint/Runtime/Descriptors/Specialized/ClrAccessDescriptor.cs

@@ -4,7 +4,7 @@ using Jint.Runtime.Interop;
 
 namespace Jint.Runtime.Descriptors.Specialized
 {
-    internal sealed class ClrAccessDescriptor : IPropertyDescriptor
+    internal sealed class ClrAccessDescriptor : PropertyDescriptor
     {
         private readonly EnvironmentRecord _env;
         private readonly Engine _engine;
@@ -17,20 +17,15 @@ namespace Jint.Runtime.Descriptors.Specialized
             EnvironmentRecord env,
             Engine engine,
             string name)
+            : base(value: null, PropertyFlag.Configurable)
         {
             _env = env;
             _engine = engine;
             _name = name;
         }
 
-        public JsValue Get => _get = _get ?? new GetterFunctionInstance(_engine, DoGet);
-        public JsValue Set => _set = _set ?? new SetterFunctionInstance(_engine, DoSet);
-
-        public bool? Enumerable => null;
-        public bool? Writable => null;
-        public bool? Configurable => true;
-
-        public JsValue Value { get; set; }
+        public override JsValue Get => _get = _get ?? new GetterFunctionInstance(_engine, DoGet);
+        public override JsValue Set => _set = _set ?? new SetterFunctionInstance(_engine, DoSet);
 
         private JsValue DoGet(JsValue n)
         {

+ 0 - 24
Jint/Runtime/Descriptors/Specialized/ConfigurableEnumerableWritablePropertyDescriptor.cs

@@ -1,24 +0,0 @@
-using Jint.Native;
-
-namespace Jint.Runtime.Descriptors.Specialized
-{
-    /// <summary>
-    /// configurable = true, enumerable = true, writable = true.
-    /// </summary>
-    internal sealed class ConfigurableEnumerableWritablePropertyDescriptor : IPropertyDescriptor
-    {
-        public ConfigurableEnumerableWritablePropertyDescriptor(JsValue value)
-        {
-            Value = value;
-        }
-
-        public JsValue Get => null;
-        public JsValue Set => null;
-
-        public bool? Enumerable => true;
-        public bool? Writable => true;
-        public bool? Configurable => true;
-
-        public JsValue Value { get; set; }
-    }
-}

+ 0 - 24
Jint/Runtime/Descriptors/Specialized/EnumerablePropertyDescriptor.cs

@@ -1,24 +0,0 @@
-using Jint.Native;
-
-namespace Jint.Runtime.Descriptors.Specialized
-{
-    /// <summary>
-    /// configurable = true, enumerable = true, writable = true.
-    /// </summary>
-    internal sealed class EnumerablePropertyDescriptor : IPropertyDescriptor
-    {
-        public EnumerablePropertyDescriptor(JsValue value)
-        {
-            Value = value;
-        }
-
-        public JsValue Get => null;
-        public JsValue Set => null;
-
-        public bool? Enumerable => true;
-        public bool? Writable => false;
-        public bool? Configurable => false;
-
-        public JsValue Value { get; set; }
-    }
-}

+ 3 - 7
Jint/Runtime/Descriptors/Specialized/FieldInfoDescriptor.cs

@@ -10,7 +10,7 @@ namespace Jint.Runtime.Descriptors.Specialized
         private readonly FieldInfo _fieldInfo;
         private readonly object _item;
 
-        public FieldInfoDescriptor(Engine engine, FieldInfo fieldInfo, object item)
+        public FieldInfoDescriptor(Engine engine, FieldInfo fieldInfo, object item) : base(PropertyFlag.CustomJsValue)
         {
             _engine = engine;
             _fieldInfo = fieldInfo;
@@ -19,13 +19,9 @@ namespace Jint.Runtime.Descriptors.Specialized
             Writable = !fieldInfo.Attributes.HasFlag(FieldAttributes.InitOnly); // don't write to fields marked as readonly
         }
 
-        public override JsValue Value
+        protected override JsValue CustomValue
         {
-            get
-            {
-                return JsValue.FromObject(_engine, _fieldInfo.GetValue(_item));
-            }
-
+            get => JsValue.FromObject(_engine, _fieldInfo.GetValue(_item));
             set
             {
                 var currentValue = value;

+ 43 - 0
Jint/Runtime/Descriptors/Specialized/GetSetPropertyDescriptor.cs

@@ -0,0 +1,43 @@
+using Jint.Native;
+
+namespace Jint.Runtime.Descriptors.Specialized
+{
+    public sealed class GetSetPropertyDescriptor : PropertyDescriptor
+    {
+        private JsValue _get;
+        private JsValue _set;
+
+        public GetSetPropertyDescriptor(JsValue get, JsValue set, bool? enumerable = null, bool? configurable = null)
+        : base(null, writable: null, enumerable: enumerable, configurable: configurable)
+        {
+            _get = get;
+            _set = set;
+        }
+
+        internal GetSetPropertyDescriptor(JsValue get, JsValue set, PropertyFlag flags)
+            : base(null, flags)
+        {
+            _get = get;
+            _set = set;
+        }
+
+        public GetSetPropertyDescriptor(PropertyDescriptor descriptor) : base(descriptor)
+        {
+            _get = descriptor.Get;
+            _set = descriptor.Set;
+        }
+
+        public override JsValue Get => _get;
+        public override JsValue Set => _set;
+
+        internal void SetGet(JsValue getter)
+        {
+            _get = getter;
+        }
+        
+        internal void SetSet(JsValue setter)
+        {
+            _set = setter;
+        }
+    }
+}

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

@@ -13,7 +13,7 @@ namespace Jint.Runtime.Descriptors.Specialized
         private readonly PropertyInfo _indexer;
         private readonly MethodInfo _containsKey;
 
-        public IndexDescriptor(Engine engine, Type targetType, string key, object item)
+        public IndexDescriptor(Engine engine, Type targetType, string key, object item) : base(PropertyFlag.CustomJsValue)
         {
             _engine = engine;
             _item = item;
@@ -33,9 +33,8 @@ namespace Jint.Runtime.Descriptors.Specialized
                     {
                         _indexer = indexer;
                         // get contains key method to avoid index exception being thrown in dictionaries
-                        _containsKey = targetType.GetMethod("ContainsKey", new Type[] { paramType });
+                        _containsKey = targetType.GetMethod("ContainsKey", new Type[] {paramType});
                         break;
-
                     }
                 }
             }
@@ -49,13 +48,12 @@ namespace Jint.Runtime.Descriptors.Specialized
             Writable = true;
         }
 
-
         public IndexDescriptor(Engine engine, string key, object item)
             : this(engine, item.GetType(), key, item)
         {
         }
 
-        public override JsValue Value
+        protected override JsValue CustomValue
         {
             get
             {
@@ -66,7 +64,7 @@ namespace Jint.Runtime.Descriptors.Specialized
                     throw new InvalidOperationException("Indexer has no public getter.");
                 }
 
-                object[] parameters = { _key };
+                object[] parameters = {_key};
 
                 if (_containsKey != null)
                 {
@@ -94,7 +92,7 @@ namespace Jint.Runtime.Descriptors.Specialized
                     throw new InvalidOperationException("Indexer has no public setter.");
                 }
 
-                object[] parameters = { _key, value != null ? value.ToObject() : null };
+                object[] parameters = {_key, value?.ToObject()};
                 setter.Invoke(_item, parameters);
             }
         }

+ 0 - 24
Jint/Runtime/Descriptors/Specialized/NonConfigurablePropertyDescriptor.cs

@@ -1,24 +0,0 @@
-using Jint.Native;
-
-namespace Jint.Runtime.Descriptors.Specialized
-{
-    /// <summary>
-    /// configurable = false, enumerable = true, writable = true.
-    /// </summary>
-    internal sealed class NonConfigurablePropertyDescriptor : IPropertyDescriptor
-    {
-        public NonConfigurablePropertyDescriptor(JsValue value)
-        {
-            Value = value;
-        }
-
-        public JsValue Get => null;
-        public JsValue Set => null;
-
-        public bool? Enumerable => true;
-        public bool? Writable => true;
-        public bool? Configurable => false;
-
-        public JsValue Value { get; set; }
-    }
-}

+ 0 - 24
Jint/Runtime/Descriptors/Specialized/NonEnumerablePropertyDescriptor.cs

@@ -1,24 +0,0 @@
-using Jint.Native;
-
-namespace Jint.Runtime.Descriptors.Specialized
-{
-    /// <summary>
-    /// configurable = true, enumerable = false, writable  = true.
-    /// </summary>
-    internal sealed class NonEnumerablePropertyDescriptor : IPropertyDescriptor
-    {
-        public NonEnumerablePropertyDescriptor(JsValue value)
-        {
-            Value = value;
-        }
-
-        public JsValue Get => null;
-        public JsValue Set => null;
-
-        public bool? Enumerable => false;
-        public bool? Writable => true;
-        public bool? Configurable => true;
-
-        public JsValue Value { get; set; }
-    }
-}

+ 0 - 24
Jint/Runtime/Descriptors/Specialized/NullConfigurationPropertyDescriptor.cs

@@ -1,24 +0,0 @@
-using Jint.Native;
-
-namespace Jint.Runtime.Descriptors.Specialized
-{
-    /// <summary>
-    /// configurable = null, enumerable = null, writable = null.
-    /// </summary>
-    internal sealed class NullConfigurationPropertyDescriptor : IPropertyDescriptor
-    {
-        public NullConfigurationPropertyDescriptor(JsValue value)
-        {
-            Value = value;
-        }
-
-        public JsValue Get => null;
-        public JsValue Set => null;
-
-        public bool? Enumerable => null;
-        public bool? Writable => null;
-        public bool? Configurable => null;
-
-        public JsValue Value { get; set; }
-    }
-}

+ 3 - 7
Jint/Runtime/Descriptors/Specialized/PropertyInfoDescriptor.cs

@@ -10,7 +10,7 @@ namespace Jint.Runtime.Descriptors.Specialized
         private readonly PropertyInfo _propertyInfo;
         private readonly object _item;
 
-        public PropertyInfoDescriptor(Engine engine, PropertyInfo propertyInfo, object item)
+        public PropertyInfoDescriptor(Engine engine, PropertyInfo propertyInfo, object item) : base(PropertyFlag.CustomJsValue)
         {
             _engine = engine;
             _propertyInfo = propertyInfo;
@@ -19,13 +19,9 @@ namespace Jint.Runtime.Descriptors.Specialized
             Writable = propertyInfo.CanWrite;
         }
 
-        public override JsValue Value
+        protected override JsValue CustomValue
         {
-            get
-            {
-                return JsValue.FromObject(_engine, _propertyInfo.GetValue(_item, null));
-            }
-
+            get => JsValue.FromObject(_engine, _propertyInfo.GetValue(_item, null));
             set
             {
                 var currentValue = value;

+ 0 - 24
Jint/Runtime/Descriptors/Specialized/WritablePropertyDescriptor.cs

@@ -1,24 +0,0 @@
-using Jint.Native;
-
-namespace Jint.Runtime.Descriptors.Specialized
-{
-    /// <summary>
-    /// configurable = false, enumerable = false, writable  = true.
-    /// </summary>
-    internal sealed class WritablePropertyDescriptor : IPropertyDescriptor
-    {
-        public WritablePropertyDescriptor(JsValue value)
-        {
-            Value = value;
-        }
-
-        public JsValue Get => null;
-        public JsValue Set => null;
-
-        public bool? Enumerable => false;
-        public bool? Writable => true;
-        public bool? Configurable => false;
-
-        public JsValue Value { get; set; }
-    }
-}

+ 3 - 6
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -10,8 +10,6 @@ namespace Jint.Runtime.Environments
     /// </summary>
     public sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
     {
-        private readonly Engine _engine;
-
         private const string BindingNameArguments = "arguments";
         private Binding _argumentsBinding;
 
@@ -19,14 +17,13 @@ namespace Jint.Runtime.Environments
 
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         {
-            _engine = engine;
         }
 
         public override bool HasBinding(string name)
         {
-            if (name == BindingNameArguments)
+            if (_argumentsBinding != null && name.Length == 9 && name == BindingNameArguments)
             {
-                return _argumentsBinding != null;
+                return true;
             }
 
             return _bindings?.ContainsKey(name) == true;
@@ -80,7 +77,7 @@ namespace Jint.Runtime.Environments
         {
             var binding = name == BindingNameArguments ? _argumentsBinding : _bindings[name];
 
-            if (!binding.Mutable && ReferenceEquals(binding.Value, Undefined))
+            if (!binding.Mutable && binding.Value.IsUndefined())
             {
                 if (strict)
                 {

+ 15 - 4
Jint/Runtime/Environments/ExecutionContext.cs

@@ -2,11 +2,22 @@
 
 namespace Jint.Runtime.Environments
 {
-    public sealed class ExecutionContext
+    public readonly struct ExecutionContext
     {
-        public LexicalEnvironment LexicalEnvironment { get; set; }
-        public LexicalEnvironment VariableEnvironment { get; set; }
-        public JsValue ThisBinding { get; set; }
+        public ExecutionContext(LexicalEnvironment lexicalEnvironment, LexicalEnvironment variableEnvironment, JsValue thisBinding)
+        {
+            LexicalEnvironment = lexicalEnvironment;
+            VariableEnvironment = variableEnvironment;
+            ThisBinding = thisBinding;
+        }
 
+        public readonly LexicalEnvironment LexicalEnvironment;
+        public readonly LexicalEnvironment VariableEnvironment;
+        public readonly JsValue ThisBinding;
+
+        public ExecutionContext UpdateLexicalEnvironment(LexicalEnvironment newEnv)
+        {
+            return new ExecutionContext(newEnv, VariableEnvironment, ThisBinding);
+        }
     }
 }

+ 27 - 10
Jint/Runtime/Environments/LexicalEnvironment.cs

@@ -1,4 +1,5 @@
-using Jint.Native;
+using System.Runtime.CompilerServices;
+using Jint.Native;
 using Jint.Native.Object;
 using Jint.Runtime.References;
 
@@ -22,30 +23,46 @@ namespace Jint.Runtime.Environments
             _outer = outer;
         }
 
-        public EnvironmentRecord Record => _record;
+        public EnvironmentRecord Record
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get { return _record; }
+        }
 
         public LexicalEnvironment Outer => _outer;
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static Reference GetIdentifierReference(LexicalEnvironment lex, string name, bool strict)
         {
-            while (true)
+            // optimize for common case where result is in one of the nearest scopes
+            if (lex._record.HasBinding(name))
             {
-                if (lex == null)
-                {
-                    return new Reference(Undefined.Instance, name, strict);
-                }
+                return lex._engine.ReferencePool.Rent(lex._record, name, strict);
+            }
 
+            if (lex._outer == null)
+            {
+                return new Reference(Undefined.Instance, name, strict);
+            }
+            
+            return GetIdentifierReferenceLooping(lex._outer, name, strict);
+        }
+
+        private static Reference GetIdentifierReferenceLooping(LexicalEnvironment lex, string name, bool strict)
+        {
+            while (true)
+            {
                 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);
                 }
 
-                lex = lex.Outer;
+                lex = lex._outer;
             }
         }
 

+ 3 - 6
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -3,7 +3,6 @@ using System.Linq;
 using Jint.Native;
 using Jint.Native.Object;
 using Jint.Runtime.Descriptors;
-using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Runtime.Environments
 {
@@ -13,13 +12,11 @@ namespace Jint.Runtime.Environments
     /// </summary>
     public sealed class ObjectEnvironmentRecord : EnvironmentRecord
     {
-        private readonly Engine _engine;
         private readonly ObjectInstance _bindingObject;
         private readonly bool _provideThis;
 
         public ObjectEnvironmentRecord(Engine engine, ObjectInstance bindingObject, bool provideThis) : base(engine)
         {
-            _engine = engine;
             _bindingObject = bindingObject;
             _provideThis = provideThis;
         }
@@ -35,8 +32,8 @@ namespace Jint.Runtime.Environments
         public override void CreateMutableBinding(string name, JsValue value, bool configurable = true)
         {
             var propertyDescriptor = configurable
-                ? (IPropertyDescriptor) new ConfigurableEnumerableWritablePropertyDescriptor(value)
-                : new NonConfigurablePropertyDescriptor(value);
+                ? new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable)
+                : new PropertyDescriptor(value, PropertyFlag.NonConfigurable);
 
             _bindingObject.SetOwnProperty(name, propertyDescriptor);
         }
@@ -74,7 +71,7 @@ namespace Jint.Runtime.Environments
 
         public override string[] GetAllBindingNames()
         {
-            if (_bindingObject != null)
+            if (!ReferenceEquals(_bindingObject, null))
             {
                 return _bindingObject.GetOwnProperties().Select( x=> x.Key).ToArray();
             }

+ 113 - 91
Jint/Runtime/ExpressionIntepreter.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Runtime.CompilerServices;
 using Esprima;
 using Esprima.Ast;
 using Jint.Native;
@@ -17,10 +16,18 @@ namespace Jint.Runtime
     public class ExpressionInterpreter
     {
         private readonly Engine _engine;
+        private readonly bool _isDebugMode;
+        private readonly int _maxRecursionDepth;
+        private readonly IReferenceResolver _referenceResolver;
 
         public ExpressionInterpreter(Engine engine)
         {
             _engine = engine;
+            
+            // gather some options as fields for faster checks
+            _isDebugMode = engine.Options.IsDebugMode;
+            _maxRecursionDepth = engine.Options.MaxRecursionDepth;
+            _referenceResolver = engine.Options.ReferenceResolver;
         }
 
         private object EvaluateExpression(Expression expression)
@@ -45,7 +52,7 @@ namespace Jint.Runtime
 
         public JsValue EvaluateAssignmentExpression(AssignmentExpression assignmentExpression)
         {
-            var lref = EvaluateExpression(assignmentExpression.Left.As<Expression>()) as Reference;
+            var lref = EvaluateExpression((Expression) assignmentExpression.Left) as Reference;
             JsValue rval = _engine.GetValue(EvaluateExpression(assignmentExpression.Right), true);
 
             if (lref == null)
@@ -56,7 +63,9 @@ namespace Jint.Runtime
             if (assignmentExpression.Operator == AssignmentOperator.Assign) // "="
             {
 
-                if(lref.IsStrict() && lref.GetBase().TryCast<EnvironmentRecord>() != null && (lref.GetReferencedName() == "eval" || lref.GetReferencedName() == "arguments"))
+                if(lref.IsStrict()
+                   && lref.GetBase() is EnvironmentRecord
+                   && (lref.GetReferencedName() == "eval" || lref.GetReferencedName() == "arguments"))
                 {
                     throw new JavaScriptException(_engine.SyntaxError);
                 }
@@ -75,8 +84,7 @@ namespace Jint.Runtime
                     var rprim = TypeConverter.ToPrimitive(rval);
                     if (lprim.IsString() || rprim.IsString())
                     {
-                        var jsString = lprim as JsString;
-                        if (jsString == null)
+                        if (!(lprim is JsString jsString))
                         {
                             jsString = new JsString.ConcatenatedString(TypeConverter.ToString(lprim));
                         }
@@ -93,7 +101,7 @@ namespace Jint.Runtime
                     break;
 
                 case AssignmentOperator.TimesAssign:
-                    if (ReferenceEquals(lval, Undefined.Instance) || ReferenceEquals(rval, Undefined.Instance))
+                    if (lval.IsUndefined() || rval.IsUndefined())
                     {
                         lval = Undefined.Instance;
                     }
@@ -108,7 +116,7 @@ namespace Jint.Runtime
                     break;
 
                 case AssignmentOperator.ModuloAssign:
-                    if (ReferenceEquals(lval, Undefined.Instance) || ReferenceEquals(rval, Undefined.Instance))
+                    if (lval.IsUndefined() || rval.IsUndefined())
                     {
                         lval = Undefined.Instance;
                     }
@@ -155,7 +163,7 @@ namespace Jint.Runtime
 
         private JsValue Divide(JsValue lval, JsValue rval)
         {
-            if (ReferenceEquals(lval, Undefined.Instance) || ReferenceEquals(rval, Undefined.Instance))
+            if (lval.IsUndefined() || rval.IsUndefined())
             {
                 return Undefined.Instance;
             }
@@ -208,7 +216,7 @@ namespace Jint.Runtime
             JsValue left;
             if (expression.Left.Type == Nodes.Literal)
             {
-                left = EvaluateLiteral(expression.Left.As<Literal>());
+                left = EvaluateLiteral((Literal) expression.Left);
             }
             else
             {
@@ -218,7 +226,7 @@ namespace Jint.Runtime
             JsValue right;
             if (expression.Right.Type == Nodes.Literal)
             {
-                right = EvaluateLiteral(expression.Right.As<Literal>());
+                right = EvaluateLiteral((Literal) expression.Right);
             }
             else
             {
@@ -247,7 +255,7 @@ namespace Jint.Runtime
                     break;
 
                 case BinaryOperator.Times:
-                    if (ReferenceEquals(left, Undefined.Instance) || ReferenceEquals(right, Undefined.Instance))
+                    if (left.IsUndefined() || right.IsUndefined())
                     {
                         value = Undefined.Instance;
                     }
@@ -262,7 +270,7 @@ namespace Jint.Runtime
                     break;
 
                 case BinaryOperator.Modulo:
-                    if (ReferenceEquals(left, Undefined.Instance) || ReferenceEquals(right, Undefined.Instance))
+                    if (left.IsUndefined() || right.IsUndefined())
                     {
                         value = Undefined.Instance;
                     }
@@ -282,7 +290,7 @@ namespace Jint.Runtime
 
                 case BinaryOperator.Greater:
                     value = Compare(right, left, false);
-                    if (ReferenceEquals(value, Undefined.Instance))
+                    if (value.IsUndefined())
                     {
                         value = false;
                     }
@@ -290,7 +298,7 @@ namespace Jint.Runtime
 
                 case BinaryOperator.GreaterOrEqual:
                     value = Compare(left, right);
-                    if (ReferenceEquals(value, Undefined.Instance) || value.AsBoolean())
+                    if (value.IsUndefined() || ((JsBoolean) value)._value)
                     {
                         value = false;
                     }
@@ -302,7 +310,7 @@ namespace Jint.Runtime
 
                 case BinaryOperator.Less:
                     value = Compare(left, right);
-                    if (ReferenceEquals(value, Undefined.Instance))
+                    if (value.IsUndefined())
                     {
                         value = false;
                     }
@@ -310,7 +318,7 @@ namespace Jint.Runtime
 
                 case BinaryOperator.LessOrEqual:
                     value = Compare(right, left, false);
-                    if (ReferenceEquals(value, Undefined.Instance) || value.AsBoolean())
+                    if (value.IsUndefined() || ((JsBoolean) value)._value)
                     {
                         value = false;
                     }
@@ -346,12 +354,10 @@ namespace Jint.Runtime
 
                 case BinaryOperator.InstanceOf:
                     var f = right.TryCast<FunctionInstance>();
-
-                    if (f == null)
+                    if (ReferenceEquals(f, null))
                     {
                         throw new JavaScriptException(_engine.TypeError, "instanceof can only be used with a function object");
                     }
-
                     value = f.HasInstance(left);
                     break;
 
@@ -409,12 +415,12 @@ namespace Jint.Runtime
 				return StrictlyEqual(x, y);
             }
 
-            if (ReferenceEquals(x, Null.Instance) && ReferenceEquals(y, Undefined.Instance))
+            if (x.IsNull() && y.IsUndefined())
             {
                 return true;
             }
 
-            if (ReferenceEquals(x, Undefined.Instance) && ReferenceEquals(y, Null.Instance))
+            if (x.IsUndefined() && y.IsNull())
             {
                 return true;
             }
@@ -469,8 +475,8 @@ namespace Jint.Runtime
 
             if (typea == Types.Number)
             {
-                var nx = x.AsNumber();
-                var ny = y.AsNumber();
+                var nx = ((JsNumber) x)._value;
+                var ny = ((JsNumber) y)._value;
 
                 if (double.IsNaN(nx) || double.IsNaN(ny))
                 {
@@ -487,22 +493,25 @@ namespace Jint.Runtime
 
             if (typea == Types.String)
             {
-                return x.AsString() == y.AsString();
+                return x.AsStringWithoutTypeCheck() == y.AsStringWithoutTypeCheck();
             }
 
             if (typea == Types.Boolean)
             {
-                return x.AsBoolean() == y.AsBoolean();
+                return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
             }
 
-			if (typea == Types.Object)
-			{
-			    if (x.AsObject() is IObjectWrapper xw)
-				{
-					var yw = y.AsObject() as IObjectWrapper;
-					return Equals(xw.Target, yw.Target);
-				}
-			}
+            if (typea == Types.Object)
+            {
+                if (x.AsObject() is IObjectWrapper xw)
+                {
+                    var yw = y.AsObject() as IObjectWrapper;
+                    if (yw == null)
+                        return false;
+
+                    return Equals(xw.Target, yw.Target);
+                }
+            }
 
             if (typea == Types.None)
             {
@@ -628,29 +637,32 @@ namespace Jint.Runtime
             return LexicalEnvironment.GetIdentifierReference(env, identifier.Name, strict);
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue EvaluateLiteral(Literal literal)
         {
             switch (literal.TokenType)
             {
                 case TokenType.BooleanLiteral:
-                    return literal.BooleanValue ? JsBoolean.True : JsBoolean.False;
+                    // bool is fast enough
+                    return (JsValue) (literal.CachedValue = literal.CachedValue ?? (literal.NumericValue != 0.0 ? JsBoolean.True : JsBoolean.False));
+                
                 case TokenType.NullLiteral:
+                    // and so is null
                     return JsValue.Null;
+
                 case TokenType.NumericLiteral:
-                    // implicit conversion operator goes through caching
-                    return literal.NumericValue;
+                    return (JsValue) (literal.CachedValue = literal.CachedValue ?? JsNumber.Create(literal.NumericValue));
+                
                 case TokenType.StringLiteral:
-                    // implicit conversion operator goes through caching
-                    return literal.StringValue;
-            }
+                    return (JsValue) (literal.CachedValue = literal.CachedValue ?? (literal.CachedValue = JsString.Create((string) literal.Value)));
+                
+                case TokenType.RegularExpression:
+                    // should not cache
+                    return _engine.RegExp.Construct((System.Text.RegularExpressions.Regex) literal.Value, literal.Regex.Flags);
 
-            if (literal.RegexValue != null) //(literal.Type == Nodes.RegularExpressionLiteral)
-            {
-                return _engine.RegExp.Construct(literal.RegexValue, literal.Regex.Flags);
+                default:
+                    // a rare case, above types should cover all
+                    return JsValue.FromObject(_engine, literal.Value);
             }
-
-            return JsValue.FromObject(_engine, literal.Value);
         }
 
         public JsValue EvaluateObjectExpression(ObjectExpression objectExpression)
@@ -664,15 +676,17 @@ namespace Jint.Runtime
                 var property = objectExpression.Properties[i];
                 var propName = property.Key.GetKey();
                 var previous = obj.GetOwnProperty(propName);
-                IPropertyDescriptor propDesc;
+                PropertyDescriptor propDesc;
 
+                const PropertyFlag enumerableConfigurable = PropertyFlag.Enumerable | PropertyFlag.Configurable;
+                
                 switch (property.Kind)
                 {
                     case PropertyKind.Init:
                     case PropertyKind.Data:
-                        var exprValue = _engine.EvaluateExpression(property.Value.As<Expression>());
+                        var exprValue = _engine.EvaluateExpression(property.Value);
                         var propValue = _engine.GetValue(exprValue, true);
-                        propDesc = new ConfigurableEnumerableWritablePropertyDescriptor(propValue);
+                        propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
                         break;
 
                     case PropertyKind.Get:
@@ -694,7 +708,7 @@ namespace Jint.Runtime
                             );
                         }
 
-                        propDesc = new PropertyDescriptor(get: get, set: null, enumerable: true, configurable: true);
+                        propDesc = new GetSetPropertyDescriptor(get: get, set: null, enumerableConfigurable);
                         break;
 
                     case PropertyKind.Set:
@@ -716,7 +730,7 @@ namespace Jint.Runtime
                             );
                         }
 
-                        propDesc = new PropertyDescriptor(get: null, set: set, enumerable: true, configurable: true);
+                        propDesc = new GetSetPropertyDescriptor(get: null, set: set, enumerableConfigurable);
                         break;
 
                     default:
@@ -742,12 +756,12 @@ namespace Jint.Runtime
 
                     if (previous.IsAccessorDescriptor() && propDesc.IsAccessorDescriptor())
                     {
-                        if (propDesc.Set != null && previous.Set != null)
+                        if (!ReferenceEquals(propDesc.Set, null) && !ReferenceEquals(previous.Set, null))
                         {
                             throw new JavaScriptException(_engine.SyntaxError);
                         }
 
-                        if (propDesc.Get != null && previous.Get != null)
+                        if (!ReferenceEquals(propDesc.Get, null) && !ReferenceEquals(previous.Get, null))
                         {
                             throw new JavaScriptException(_engine.SyntaxError);
                         }
@@ -815,26 +829,28 @@ namespace Jint.Runtime
         public JsValue EvaluateCallExpression(CallExpression callExpression)
         {
             var callee = EvaluateExpression(callExpression.Callee);
-
-            if (_engine.Options._IsDebugMode)
+            
+            if (_isDebugMode)
             {
                 _engine.DebugHandler.AddToDebugCallStack(callExpression);
             }
 
             JsValue thisObject;
-
             // todo: implement as in http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.4
 
-
-            JsValue[] arguments;
-
+            var arguments = Array.Empty<JsValue>();
             if (callExpression.Cached)
             {
                 arguments = (JsValue[]) callExpression.CachedArguments;
             }
             else
             {
-                arguments = BuildArguments(callExpression.Arguments, out bool allLiteral);
+                var allLiteral = true;
+                if (callExpression.Arguments.Count > 0)
+                {
+                    arguments = _engine.JsValueArrayPool.RentArray(callExpression.Arguments.Count);
+                    BuildArguments(callExpression.Arguments, arguments, out allLiteral);
+                }
 
                 if (callExpression.CanBeCached)
                 {
@@ -855,32 +871,30 @@ namespace Jint.Runtime
 
             var r = callee as Reference;
 
-            if (_engine.Options._MaxRecursionDepth >= 0)
+            if (_maxRecursionDepth >= 0)
             {
                 var stackItem = new CallStackElement(callExpression, func, r != null ? r.GetReferencedName() : "anonymous function");
 
                 var recursionDepth = _engine.CallStack.Push(stackItem);
 
-                if (recursionDepth > _engine.Options._MaxRecursionDepth)
+                if (recursionDepth > _maxRecursionDepth)
                 {
                     _engine.CallStack.Pop();
                     throw new RecursionDepthOverflowException(_engine.CallStack, stackItem.ToString());
                 }
             }
 
-            if (ReferenceEquals(func, Undefined.Instance))
+            if (func.IsUndefined())
             {
                 throw new JavaScriptException(_engine.TypeError, r == null ? "" : string.Format("Object has no method '{0}'", r.GetReferencedName()));
             }
 
             if (!func.IsObject())
             {
-
-                if (_engine.Options._ReferenceResolver == null ||
-                    !_engine.Options._ReferenceResolver.TryGetCallable(_engine, callee, out func))
+                if (_referenceResolver == null || !_referenceResolver.TryGetCallable(_engine, callee, out func))
                 {
                     throw new JavaScriptException(_engine.TypeError,
-                        r == null ? "" : string.Format("Property '{0}' of object is not a function", r.GetReferencedName()));
+                        r == null ? "" : $"Property '{r.GetReferencedName()}' of object is not a function");
                 }
             }
 
@@ -917,16 +931,21 @@ namespace Jint.Runtime
 
             var result = callable.Call(thisObject, arguments);
 
-            if (_engine.Options._IsDebugMode)
+            if (_isDebugMode)
             {
                 _engine.DebugHandler.PopDebugCallStack();
             }
 
-            if (_engine.Options._MaxRecursionDepth >= 0)
+            if (_maxRecursionDepth >= 0)
             {
                 _engine.CallStack.Pop();
             }
 
+            if (!callExpression.Cached && arguments.Length > 0)
+            {
+                _engine.JsValueArrayPool.ReturnArray(arguments);
+            }
+
             _engine.ReferencePool.Return(r);
             return result;
         }
@@ -938,7 +957,7 @@ namespace Jint.Runtime
             for (var i = 0; i < expressionsCount; i++)
             {
                 var expression = sequenceExpression.Expressions[i];
-                result = _engine.GetValue(_engine.EvaluateExpression(expression.As<Expression>()), true);
+                result = _engine.GetValue(_engine.EvaluateExpression(expression), true);
             }
 
             return result;
@@ -955,7 +974,7 @@ namespace Jint.Runtime
                     r = value as Reference;
                     if (r != null
                         && r.IsStrict()
-                        && (r.GetBase().TryCast<EnvironmentRecord>() != null)
+                        && r.GetBase() is EnvironmentRecord
                         && ("eval" == r.GetReferencedName() || "arguments" == r.GetReferencedName()))
                     {
                         throw new JavaScriptException(_engine.SyntaxError);
@@ -972,7 +991,7 @@ namespace Jint.Runtime
                     r = value as Reference;
                     if (r != null
                         && r.IsStrict()
-                        && (r.GetBase().TryCast<EnvironmentRecord>() != null)
+                        && r.GetBase() is EnvironmentRecord
                         && ("eval" == r.GetReferencedName() || "arguments" == r.GetReferencedName()))
                     {
                         throw new JavaScriptException(_engine.SyntaxError);
@@ -998,7 +1017,8 @@ namespace Jint.Runtime
 
         public JsValue EvaluateNewExpression(NewExpression newExpression)
         {
-            var arguments = BuildArguments(newExpression.Arguments, out bool _);
+            var arguments = _engine.JsValueArrayPool.RentArray(newExpression.Arguments.Count);
+            BuildArguments(newExpression.Arguments, arguments, out _);
 
             // todo: optimize by defining a common abstract class or interface
             var callee = _engine.GetValue(EvaluateExpression(newExpression.Callee), true).TryCast<IConstructor>();
@@ -1011,6 +1031,8 @@ namespace Jint.Runtime
             // construct the new instance using the Function's constructor method
             var instance = callee.Construct(arguments);
 
+            _engine.JsValueArrayPool.ReturnArray(arguments);
+
             return instance;
         }
 
@@ -1018,16 +1040,22 @@ namespace Jint.Runtime
         {
             var elements = arrayExpression.Elements;
             var count = elements.Count;
-            var a = _engine.Array.Construct(new JsValue[] {count}, (uint) count);
+            
+            var jsValues = _engine.JsValueArrayPool.RentArray(1);
+            jsValues[0] = count;
+            
+            var a = _engine.Array.Construct(jsValues, (uint) count);
             for (var n = 0; n < count; n++)
             {
                 var expr = elements[n];
                 if (expr != null)
                 {
-                    var value = _engine.GetValue(EvaluateExpression(expr.As<Expression>()), true);
-                    a.SetIndexValue((uint) n, value, throwOnError: false);
+                    var value = _engine.GetValue(EvaluateExpression((Expression) expr), true);
+                    a.SetIndexValue((uint) n, value, updateLength: false);
                 }
             }
+            a.SetLength((uint) count);
+            _engine.JsValueArrayPool.ReturnArray(jsValues);
 
             return a;
         }
@@ -1102,11 +1130,11 @@ namespace Jint.Runtime
 
                     var v = _engine.GetValue(value, true);
 
-                    if (ReferenceEquals(v, Undefined.Instance))
+                    if (v.IsUndefined())
                     {
                         return "undefined";
                     }
-                    if (ReferenceEquals(v, Null.Instance))
+                    if (v.IsNull())
                     {
                         return "object";
                     }
@@ -1127,26 +1155,20 @@ namespace Jint.Runtime
             }
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private JsValue[] BuildArguments(List<ArgumentListElement> expressionArguments, out bool allLiteral)
+        private void BuildArguments(
+            List<ArgumentListElement> expressionArguments, 
+            JsValue[] targetArray,
+            out bool cacheable)
         {
-            allLiteral = true;
+            cacheable = true;
             var count = expressionArguments.Count;
 
-            if (count == 0)
-            {
-                return Array.Empty<JsValue>();
-            }
-
-            var arguments = new JsValue[count];
             for (var i = 0; i < count; i++)
             {
                 var argument = (Expression) expressionArguments[i];
-                arguments[i] = _engine.GetValue(EvaluateExpression(argument), true);
-                allLiteral &= argument is Literal;
+                targetArray[i] = _engine.GetValue(EvaluateExpression(argument), true);
+                cacheable &= argument is Literal;
             }
-
-            return arguments;
         }
     }
-}
+}

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

@@ -1,7 +1,7 @@
 using System;
 using Jint.Native;
 using Jint.Native.Function;
-using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Descriptors;
 
 namespace Jint.Runtime.Interop
 {
@@ -17,7 +17,7 @@ namespace Jint.Runtime.Interop
         {
             _func = func;
             Prototype = engine.Function.PrototypeObject;
-            SetOwnProperty("length", new AllForbiddenPropertyDescriptor(length));
+            SetOwnProperty("length", new PropertyDescriptor(length, PropertyFlag.AllForbidden));
             Extensible = true;
         }
 

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

@@ -139,7 +139,7 @@ namespace Jint.Runtime.Interop
                 {
                     if (type == typeof(Action))
                     {
-                        return (Action)(() => function(JsValue.Undefined, new JsValue[0]));
+                        return (Action)(() => function(JsValue.Undefined, Array.Empty<JsValue>()));
                     }
                     else if (typeof(MulticastDelegate).IsAssignableFrom(type))
                     {

+ 4 - 4
Jint/Runtime/Interop/NamespaceReference.cs

@@ -23,7 +23,7 @@ namespace Jint.Runtime.Interop
             _path = path;
         }
 
-        public override bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             if (throwOnError)
             {
@@ -50,7 +50,7 @@ namespace Jint.Runtime.Interop
             for (int i = 0; i < arguments.Length; i++)
             {
                 var genericTypeReference = arguments.At(i);
-                if (ReferenceEquals(genericTypeReference, Undefined)
+                if (genericTypeReference.IsUndefined()
                     || !genericTypeReference.IsObject() 
                     || genericTypeReference.AsObject().Class != "TypeReference")
                 {
@@ -62,7 +62,7 @@ namespace Jint.Runtime.Interop
 
             var typeReference = GetPath(_path + "`" + arguments.Length.ToString(CultureInfo.InvariantCulture)).As<TypeReference>();
 
-            if (typeReference == null)
+            if (ReferenceEquals(typeReference, null))
             {
                 return Undefined;
             }
@@ -193,7 +193,7 @@ namespace Jint.Runtime.Interop
             }
         }
 
-        public override IPropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(string propertyName)
         {
             return PropertyDescriptor.Undefined;
         }

+ 4 - 4
Jint/Runtime/Interop/ObjectWrapper.cs

@@ -50,7 +50,7 @@ namespace Jint.Runtime.Interop
             ownDesc.Value = value;
         }
 
-        public override IPropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(string propertyName)
         {
             if (TryGetProperty(propertyName, out var x))
                 return x;
@@ -86,7 +86,7 @@ namespace Jint.Runtime.Interop
 
             if (methods.Any())
             {
-                var descriptor = new EnumerablePropertyDescriptor(new MethodInfoFunctionInstance(Engine, methods));
+                var descriptor = new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, methods), PropertyFlag.OnlyEnumerable);
                 AddProperty(propertyName, descriptor);
                 return descriptor;
             }
@@ -120,7 +120,7 @@ namespace Jint.Runtime.Interop
 
             if (explicitMethods.Length > 0)
             {
-                var descriptor = new EnumerablePropertyDescriptor(new MethodInfoFunctionInstance(Engine, explicitMethods));
+                var descriptor = new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, explicitMethods), PropertyFlag.OnlyEnumerable);
                 AddProperty(propertyName, descriptor);
                 return descriptor;
             }
@@ -140,7 +140,7 @@ namespace Jint.Runtime.Interop
             return PropertyDescriptor.Undefined;
         }
 
-        private bool EqualsIgnoreCasing(string s1, string s2)
+        private static bool EqualsIgnoreCasing(string s1, string s2)
         {
             bool equals = false;
             if (s1.Length == s2.Length)

+ 8 - 10
Jint/Runtime/Interop/TypeReference.cs

@@ -13,7 +13,7 @@ namespace Jint.Runtime.Interop
     public class TypeReference : FunctionInstance, IConstructor, IObjectWrapper
     {
         private TypeReference(Engine engine)
-            : base(engine, null, null, false)
+            : base(engine, null, null, false, "TypeReference")
         {
         }
 
@@ -28,10 +28,10 @@ namespace Jint.Runtime.Interop
             // The value of the [[Prototype]] internal property of the TypeReference constructor is the Function prototype object
             obj.Prototype = engine.Function.PrototypeObject;
 
-            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(0));
+            obj.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden));
 
             // The initial value of Boolean.prototype is the Boolean prototype object
-            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(engine.Object.PrototypeObject));
+            obj.SetOwnProperty("prototype", new PropertyDescriptor(engine.Object.PrototypeObject, PropertyFlag.AllForbidden));
 
             return obj;
         }
@@ -100,7 +100,7 @@ namespace Jint.Runtime.Interop
         {
             ObjectWrapper wrapper = v.As<ObjectWrapper>();
 
-            if (wrapper == null)
+            if (ReferenceEquals(wrapper, null))
             {
                 return base.HasInstance(v);
             }
@@ -108,7 +108,7 @@ namespace Jint.Runtime.Interop
             return wrapper.Target.GetType() == ReferenceType;
         }
 
-        public override bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             if (throwOnError)
             {
@@ -157,7 +157,7 @@ namespace Jint.Runtime.Interop
             ownDesc.Value = value;
         }
 
-        public override IPropertyDescriptor GetOwnProperty(string propertyName)
+        public override PropertyDescriptor GetOwnProperty(string propertyName)
         {
             // todo: cache members locally
 
@@ -170,7 +170,7 @@ namespace Jint.Runtime.Interop
                 {
                     if (enumNames.GetValue(i) as string == propertyName)
                     {
-                        return new AllForbiddenPropertyDescriptor((int) enumValues.GetValue(i));
+                        return new PropertyDescriptor((int) enumValues.GetValue(i), PropertyFlag.AllForbidden);
                     }
                 }
                 return PropertyDescriptor.Undefined;
@@ -198,11 +198,9 @@ namespace Jint.Runtime.Interop
                 return PropertyDescriptor.Undefined;
             }
 
-            return new AllForbiddenPropertyDescriptor(new MethodInfoFunctionInstance(Engine, methodInfo));
+            return new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, methodInfo), PropertyFlag.AllForbidden);
         }
 
         public object Target => ReferenceType;
-
-        public override string Class => "TypeReference";
     }
 }

+ 5 - 5
Jint/Runtime/JavaScriptException.cs

@@ -77,7 +77,7 @@ namespace Jint.Runtime
                 return message;
             }
             if (error.IsString())
-                return error.AsString();
+                return error.AsStringWithoutTypeCheck();
 
             return error.ToString();
         }
@@ -95,12 +95,12 @@ namespace Jint.Runtime
             {
                 if (_callStack != null)
                     return _callStack;
-                if (Error == null)
+                if (ReferenceEquals(Error, null))
                     return null;
                 if (Error.IsObject() == false)
                     return null;
                 var callstack = Error.AsObject().Get("callstack");
-                if (callstack == JsValue.Undefined)
+                if (callstack.IsUndefined())
                     return null;
                 return callstack.AsString();
             }
@@ -117,8 +117,8 @@ namespace Jint.Runtime
 
         public Location Location { get; set; }
 
-        public int LineNumber { get { return null == Location ? 0 : Location.Start.Line; } }
+        public int LineNumber => Location?.Start.Line ?? 0;
 
-        public int Column { get { return null == Location ? 0 : Location.Start.Column; } }
+        public int Column => Location?.Start.Column ?? 0;
     }
 }

+ 15 - 0
Jint/Runtime/MemoryLimitExceededException.cs

@@ -0,0 +1,15 @@
+using System;
+
+namespace Jint.Runtime
+{
+    public class MemoryLimitExceededException : Exception
+    {
+        public MemoryLimitExceededException() : base()
+        {
+        }
+
+        public MemoryLimitExceededException(string message) : base(message)
+        {
+        }
+    }
+}

+ 64 - 0
Jint/Runtime/RefStack.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Runtime.CompilerServices;
+using Jint.Runtime.Environments;
+
+namespace Jint.Runtime
+{
+    internal sealed class ExecutionContextStack
+    {
+        private ExecutionContext[] _array;
+        private int _size;
+
+        private const int DefaultCapacity = 4;
+
+        public ExecutionContextStack()
+        {
+            _array = new ExecutionContext[4];
+            _size = 0;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public ref readonly ExecutionContext Peek()
+        {
+            if (_size == 0)
+            {
+                ThrowEmptyStackException();
+            }
+            return ref _array[_size - 1];
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Pop()
+        {
+            if (_size == 0)
+            {
+                ThrowEmptyStackException();
+            }
+            _size--;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Push(in ExecutionContext item)
+        {
+            if (_size == _array.Length)
+            {
+                var newSize = 2 * _array.Length;
+                var newArray = new ExecutionContext[newSize];
+                Array.Copy(_array, 0, newArray, 0, _size);
+                _array = newArray;
+            }
+
+            _array[_size++] = item;
+        }
+
+        private static void ThrowEmptyStackException()
+        {
+            throw new InvalidOperationException("stack is empty");
+        }
+
+        public void ReplaceTopLexicalEnvironment(LexicalEnvironment newEnv)
+        {
+            _array[_size - 1] = _array[_size - 1].UpdateLexicalEnvironment(newEnv);
+        }
+    }
+}

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

@@ -1,4 +1,5 @@
 using System;
+using System.Runtime.CompilerServices;
 using Jint.Native;
 using Jint.Runtime.Environments;
 
@@ -20,35 +21,41 @@ namespace Jint.Runtime.References
             _name = name;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue GetBase()
         {
             return _baseValue;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public string GetReferencedName()
         {
             return _name;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsStrict()
         {
             return _strict;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool HasPrimitiveBase()
         {
             return _baseValue.IsPrimitive();
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsUnresolvableReference()
         {
             return _baseValue.IsUndefined();
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsPropertyReference()
         {
             // http://www.ecma-international.org/ecma-262/5.1/#sec-8.7
-            return HasPrimitiveBase() || (_baseValue.IsObject() && !(_baseValue is EnvironmentRecord));
+            return _baseValue.IsPrimitive() || (_baseValue.IsObject() && !(_baseValue is EnvironmentRecord));
         }
 
         internal Reference Reassign(JsValue baseValue, string name, bool strict)

+ 82 - 101
Jint/Runtime/StatementInterpreter.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using Esprima.Ast;
 using Jint.Native;
 using Jint.Runtime.Descriptors;
@@ -25,13 +24,13 @@ namespace Jint.Runtime
 
         public Completion ExecuteEmptyStatement(EmptyStatement emptyStatement)
         {
-            return Completion.Empty;
+            return new Completion(CompletionType.Normal, null, null);
         }
 
         public Completion ExecuteExpressionStatement(ExpressionStatement expressionStatement)
         {
             var exprRef = _engine.EvaluateExpression(expressionStatement.Expression);
-            return _engine.CompletionPool.Rent(Completion.Normal, _engine.GetValue(exprRef, true), null);
+            return new Completion(CompletionType.Normal, _engine.GetValue(exprRef, true), null);
         }
 
         public Completion ExecuteIfStatement(IfStatement ifStatement)
@@ -47,7 +46,7 @@ namespace Jint.Runtime
             }
             else
             {
-                return Completion.Empty;
+                return new Completion(CompletionType.Normal, null, null);
             }
 
             return result;
@@ -59,11 +58,10 @@ namespace Jint.Runtime
             // containing label and could keep a table per program with all the labels
             // labeledStatement.Body.LabelSet = labeledStatement.Label;
             var result = ExecuteStatement(labeledStatement.Body);
-            if (result.Type == Completion.Break && result.Identifier == labeledStatement.Label.Name)
+            if (result.Type == CompletionType.Break && result.Identifier == labeledStatement.Label.Name)
             {
                 var value = result.Value;
-                _engine.CompletionPool.Return(result);
-                return _engine.CompletionPool.Rent(Completion.Normal, value, null);
+                return new Completion(CompletionType.Normal, value, null);
             }
 
             return result;
@@ -82,31 +80,29 @@ namespace Jint.Runtime
             do
             {
                 var stmt = ExecuteStatement(doWhileStatement.Body);
-                if (stmt.Value != null)
+                if (!ReferenceEquals(stmt.Value, null))
                 {
                     v = stmt.Value;
                 }
-                if (stmt.Type != Completion.Continue || stmt.Identifier != doWhileStatement?.LabelSet?.Name)
+                if (stmt.Type != CompletionType.Continue || stmt.Identifier != doWhileStatement?.LabelSet?.Name)
                 {
-                    if (stmt.Type == Completion.Break && (stmt.Identifier == null || stmt.Identifier == doWhileStatement?.LabelSet?.Name))
+                    if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == doWhileStatement?.LabelSet?.Name))
                     {
-                        _engine.CompletionPool.Return(stmt);
-                        return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+                        return new Completion(CompletionType.Normal, v, null);
                     }
 
-                    if (stmt.Type != Completion.Normal)
+                    if (stmt.Type != CompletionType.Normal)
                     {
                         return stmt;
                     }
                 }
 
-                _engine.CompletionPool.Return(stmt);
                 var exprRef = _engine.EvaluateExpression(doWhileStatement.Test);
                 iterating = TypeConverter.ToBoolean(_engine.GetValue(exprRef, true));
 
             } while (iterating);
 
-            return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+            return new Completion(CompletionType.Normal, v, null);
         }
 
         /// <summary>
@@ -122,31 +118,28 @@ namespace Jint.Runtime
                 var jsValue = _engine.GetValue(_engine.EvaluateExpression(whileStatement.Test), true);
                 if (!TypeConverter.ToBoolean(jsValue))
                 {
-                    return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+                    return new Completion(CompletionType.Normal, v, null);
                 }
 
                 var stmt = ExecuteStatement(whileStatement.Body);
 
-                if (stmt.Value != null)
+                if (!ReferenceEquals(stmt.Value, null))
                 {
                     v = stmt.Value;
                 }
 
-                if (stmt.Type != Completion.Continue || stmt.Identifier != whileStatement?.LabelSet?.Name)
+                if (stmt.Type != CompletionType.Continue || stmt.Identifier != whileStatement?.LabelSet?.Name)
                 {
-                    if (stmt.Type == Completion.Break && (stmt.Identifier == null || stmt.Identifier == whileStatement?.LabelSet?.Name))
+                    if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == whileStatement?.LabelSet?.Name))
                     {
-                        _engine.CompletionPool.Return(stmt);
-                        return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+                        return new Completion(CompletionType.Normal, v, null);
                     }
 
-                    if (stmt.Type != Completion.Normal)
+                    if (stmt.Type != CompletionType.Normal)
                     {
                         return stmt;
                     }
                 }
-
-                _engine.CompletionPool.Return(stmt);
             }
         }
 
@@ -157,18 +150,17 @@ namespace Jint.Runtime
         /// <returns></returns>
         public Completion ExecuteForStatement(ForStatement forStatement)
         {
-
-            if (forStatement.Init != null)
+            var init = forStatement.Init;
+            if (init != null)
             {
-                if (forStatement.Init.Type == Nodes.VariableDeclaration)
+                if (init.Type == Nodes.VariableDeclaration)
                 {
-                    var c = ExecuteStatement(forStatement.Init.As<Statement>());
-                    _engine.CompletionPool.Return(c);
+                    var c = ExecuteStatement((Statement) init);
 
                 }
                 else
                 {
-                    _engine.GetValue(_engine.EvaluateExpression(forStatement.Init.As<Expression>()), true);
+                    _engine.GetValue(_engine.EvaluateExpression(init), true);
                 }
             }
 
@@ -180,23 +172,24 @@ namespace Jint.Runtime
                     var testExprRef = _engine.EvaluateExpression(forStatement.Test);
                     if (!TypeConverter.ToBoolean(_engine.GetValue(testExprRef, true)))
                     {
-                        return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+                        return new Completion(CompletionType.Normal, v, null);
                     }
                 }
 
                 var stmt = ExecuteStatement(forStatement.Body);
-                if (stmt.Value != null)
+                if (!ReferenceEquals(stmt.Value, null))
                 {
                     v = stmt.Value;
                 }
-                if (stmt.Type == Completion.Break && (stmt.Identifier == null || stmt.Identifier == forStatement?.LabelSet?.Name))
+
+                var stmtType = stmt.Type;
+                if (stmtType == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == forStatement?.LabelSet?.Name))
                 {
-                    _engine.CompletionPool.Return(stmt);
-                    return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+                    return new Completion(CompletionType.Normal, v, null);
                 }
-                if (stmt.Type != Completion.Continue || ((stmt.Identifier != null) && stmt.Identifier != forStatement?.LabelSet?.Name))
+                if (stmtType != CompletionType.Continue || ((stmt.Identifier != null) && stmt.Identifier != forStatement?.LabelSet?.Name))
                 {
-                    if (stmt.Type != Completion.Normal)
+                    if (stmtType != CompletionType.Normal)
                     {
                         return stmt;
                     }
@@ -205,7 +198,6 @@ namespace Jint.Runtime
                 {
                     _engine.GetValue(_engine.EvaluateExpression(forStatement.Update), true);
                 }
-                _engine.CompletionPool.Return(stmt);
             }
         }
 
@@ -216,15 +208,15 @@ namespace Jint.Runtime
         /// <returns></returns>
         public Completion ExecuteForInStatement(ForInStatement forInStatement)
         {
-            Identifier identifier = forInStatement.Left.Type == Nodes.VariableDeclaration
-                                        ? forInStatement.Left.As<VariableDeclaration>().Declarations.First().Id.As<Identifier>()
-                                        : forInStatement.Left.As<Identifier>();
+            var identifier = forInStatement.Left.Type == Nodes.VariableDeclaration
+                ? (Identifier) ((VariableDeclaration) forInStatement.Left).Declarations[0].Id
+                : (Identifier) forInStatement.Left;
 
             var varRef = _engine.EvaluateExpression(identifier) as Reference;
             var experValue = _engine.GetValue(_engine.EvaluateExpression(forInStatement.Right), true);
-            if (ReferenceEquals(experValue, Undefined.Instance) || ReferenceEquals(experValue,  Null.Instance))
+            if (experValue.IsUndefined() || experValue.IsNull())
             {
-                return Completion.Empty;
+                return new Completion(CompletionType.Normal, null, null);
             }
 
             var obj = TypeConverter.ToObject(_engine, experValue);
@@ -234,13 +226,13 @@ namespace Jint.Runtime
             var cursor = obj;
             var processedKeys = new HashSet<string>();
 
-            while (cursor != null)
+            while (!ReferenceEquals(cursor, null))
             {
                 var keys = _engine.Object.GetOwnPropertyNames(Undefined.Instance, Arguments.From(cursor)).AsArray();
 
                 for (var i = 0; i < keys.GetLength(); i++)
                 {
-                    var p = keys.GetOwnProperty(TypeConverter.ToString(i)).Value.AsString();
+                    var p = keys.GetOwnProperty(TypeConverter.ToString(i)).Value.AsStringWithoutTypeCheck();
 
                     if (processedKeys.Contains(p))
                     {
@@ -255,9 +247,8 @@ namespace Jint.Runtime
                         continue;
                     }
 
-
                     var value = cursor.GetOwnProperty(p);
-                    if (!value.Enumerable.HasValue || !value.Enumerable.Value)
+                    if (!value.Enumerable)
                     {
                         continue;
                     }
@@ -265,29 +256,27 @@ namespace Jint.Runtime
                     _engine.PutValue(varRef, p);
 
                     var stmt = ExecuteStatement(forInStatement.Body);
-                    if (stmt.Value != null)
+                    if (!ReferenceEquals(stmt.Value, null))
                     {
                         v = stmt.Value;
                     }
-                    if (stmt.Type == Completion.Break)
+                    if (stmt.Type == CompletionType.Break)
                     {
-                        _engine.CompletionPool.Return(stmt);
-                        return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+                        return new Completion(CompletionType.Normal, v, null);
                     }
-                    if (stmt.Type != Completion.Continue)
+                    if (stmt.Type != CompletionType.Continue)
                     {
-                        if (stmt.Type != Completion.Normal)
+                        if (stmt.Type != CompletionType.Normal)
                         {
                             return stmt;
                         }
                     }
-                    _engine.CompletionPool.Return(stmt);
                 }
 
                 cursor = cursor.Prototype;
             }
 
-            return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+            return new Completion(CompletionType.Normal, v, null);
         }
 
         /// <summary>
@@ -297,10 +286,10 @@ namespace Jint.Runtime
         /// <returns></returns>
         public Completion ExecuteContinueStatement(ContinueStatement continueStatement)
         {
-            return _engine.CompletionPool.Rent(
-                Completion.Continue,
+            return new Completion(
+                CompletionType.Continue,
                 null,
-                continueStatement.Label != null ? continueStatement.Label.Name : null);
+                continueStatement.Label?.Name);
         }
 
         /// <summary>
@@ -310,10 +299,10 @@ namespace Jint.Runtime
         /// <returns></returns>
         public Completion ExecuteBreakStatement(BreakStatement breakStatement)
         {
-            return _engine.CompletionPool.Rent(
-                Completion.Break,
+            return new Completion(
+                CompletionType.Break,
                 null,
-                breakStatement.Label != null ? breakStatement.Label.Name : null);
+                breakStatement.Label?.Name);
         }
 
         /// <summary>
@@ -325,11 +314,11 @@ namespace Jint.Runtime
         {
             if (statement.Argument == null)
             {
-                return _engine.CompletionPool.Rent(Completion.Return, Undefined.Instance, null);
+                return new Completion(CompletionType.Return, Undefined.Instance, null);
             }
 
             var jsValue = _engine.GetValue(_engine.EvaluateExpression(statement.Argument), true);
-            return _engine.CompletionPool.Rent(Completion.Return, jsValue, null);
+            return new Completion(CompletionType.Return, jsValue, null);
         }
 
         /// <summary>
@@ -343,7 +332,7 @@ namespace Jint.Runtime
             var obj = TypeConverter.ToObject(_engine, jsValue);
             var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
             var newEnv = LexicalEnvironment.NewObjectEnvironment(_engine, obj, oldEnv, true);
-            _engine.ExecutionContext.LexicalEnvironment = newEnv;
+            _engine.UpdateLexicalEnvironment(newEnv);
 
             Completion c;
             try
@@ -352,11 +341,11 @@ namespace Jint.Runtime
             }
             catch (JavaScriptException e)
             {
-                c = _engine.CompletionPool.Rent(Completion.Throw, e.Error, null, withStatement.Location);
+                c = new Completion(CompletionType.Throw, e.Error, null, withStatement.Location);
             }
             finally
             {
-                _engine.ExecutionContext.LexicalEnvironment = oldEnv;
+                _engine.UpdateLexicalEnvironment(oldEnv);
             }
 
             return c;
@@ -371,9 +360,9 @@ namespace Jint.Runtime
         {
             var jsValue = _engine.GetValue(_engine.EvaluateExpression(switchStatement.Discriminant), true);
             var r = ExecuteSwitchBlock(switchStatement.Cases, jsValue);
-            if (r.Type == Completion.Break && r.Identifier == switchStatement.LabelSet?.Name)
+            if (r.Type == CompletionType.Break && r.Identifier == switchStatement.LabelSet?.Name)
             {
-                return _engine.CompletionPool.Rent(Completion.Normal, r.Value, null);
+                return new Completion(CompletionType.Normal, r.Value, null);
             }
             return r;
         }
@@ -404,12 +393,12 @@ namespace Jint.Runtime
                 if (hit && clause.Consequent != null)
                 {
                     var r = ExecuteStatementList(clause.Consequent);
-                    if (r.Type != Completion.Normal)
+                    if (r.Type != CompletionType.Normal)
                     {
                         return r;
                     }
 
-                    v = r.Value != null ? r.Value : Undefined.Instance;
+                    v = r.Value ?? Undefined.Instance;
                 }
             }
 
@@ -417,20 +406,20 @@ namespace Jint.Runtime
             if (hit == false && defaultCase != null)
             {
                 var r = ExecuteStatementList(defaultCase.Consequent);
-                if (r.Type != Completion.Normal)
+                if (r.Type != CompletionType.Normal)
                 {
                     return r;
                 }
 
-                v = r.Value != null ? r.Value : Undefined.Instance;
+                v = r.Value ?? Undefined.Instance;
             }
 
-            return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+            return new Completion(CompletionType.Normal, v, null);
         }
 
         public Completion ExecuteStatementList(List<StatementListItem> statementList)
         {
-            var c = Completion.Empty;
+            var c = new Completion(CompletionType.Normal, null, null);
             Completion sl = c;
             Statement s = null;
 
@@ -439,39 +428,29 @@ namespace Jint.Runtime
                 var statementListCount = statementList.Count;
                 for (var i = 0; i < statementListCount; i++)
                 {
-                    var statement = statementList[i];
-                    s = statement.As<Statement>();
+                    s = (Statement) statementList[i];
                     c = ExecuteStatement(s);
-                    if (c.Type != Completion.Normal)
+                    if (c.Type != CompletionType.Normal)
                     {
-                        var executeStatementList = _engine.CompletionPool.Rent(
+                        var executeStatementList = new Completion(
                             c.Type,
-                            c.Value != null ? c.Value : sl.Value,
+                            c.Value ?? sl.Value,
                             c.Identifier,
                             c.Location);
 
-                        _engine.CompletionPool.Return(sl);
-                        _engine.CompletionPool.Return(c);
                         return executeStatementList;
                     }
 
-                    if (sl != c)
-                    {
-                        _engine.CompletionPool.Return(sl);
-                    }
-
                     sl = c;
                 }
             }
             catch (JavaScriptException v)
             {
-                var completion = _engine.CompletionPool.Rent(Completion.Throw, v.Error, null, v.Location ?? s.Location);
+                var completion = new Completion(CompletionType.Throw, v.Error, null, v.Location ?? s.Location);
                 return completion;
             }
 
-            var rent = _engine.CompletionPool.Rent(c.Type, c.GetValueOrDefault(), c.Identifier);
-            _engine.CompletionPool.Return(c);
-            return rent;
+            return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
         }
 
         /// <summary>
@@ -482,7 +461,7 @@ namespace Jint.Runtime
         public Completion ExecuteThrowStatement(ThrowStatement throwStatement)
         {
             var jsValue = _engine.GetValue(_engine.EvaluateExpression(throwStatement.Argument), true);
-            return _engine.CompletionPool.Rent(Completion.Throw, jsValue, null, throwStatement.Location);
+            return new Completion(CompletionType.Throw, jsValue, null, throwStatement.Location);
         }
 
         /// <summary>
@@ -493,7 +472,7 @@ namespace Jint.Runtime
         public Completion ExecuteTryStatement(TryStatement tryStatement)
         {
             var b = ExecuteStatement(tryStatement.Block);
-            if (b.Type == Completion.Throw)
+            if (b.Type == CompletionType.Throw)
             {
                 // execute catch
                 var catchClause = tryStatement.Handler;
@@ -502,17 +481,18 @@ namespace Jint.Runtime
                     var c = _engine.GetValue(b);
                     var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
                     var catchEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
-                    catchEnv.Record.CreateMutableBinding(catchClause.Param.As<Identifier>().Name, c);
-                    _engine.ExecutionContext.LexicalEnvironment = catchEnv;
+                    catchEnv.Record.CreateMutableBinding(((Identifier) catchClause.Param).Name, c);
+
+                    _engine.UpdateLexicalEnvironment(catchEnv);
                     b = ExecuteStatement(catchClause.Body);
-                    _engine.ExecutionContext.LexicalEnvironment = oldEnv;
+                    _engine.UpdateLexicalEnvironment(oldEnv);
                 }
             }
 
             if (tryStatement.Finalizer != null)
             {
                 var f = ExecuteStatement(tryStatement.Finalizer);
-                if (f.Type == Completion.Normal)
+                if (f.Type == CompletionType.Normal)
                 {
                     return b;
                 }
@@ -536,13 +516,14 @@ namespace Jint.Runtime
                 var declaration = statement.Declarations[i];
                 if (declaration.Init != null)
                 {
-                    if (!(_engine.EvaluateExpression(declaration.Id.As<Identifier>()) is Reference lhs))
+                    if (!(_engine.EvaluateExpression(declaration.Id) is Reference lhs))
                     {
                         throw new ArgumentException();
                     }
 
-                    if (lhs.IsStrict() && lhs.GetBase().TryCast<EnvironmentRecord>() != null &&
-                        (lhs.GetReferencedName() == "eval" || lhs.GetReferencedName() == "arguments"))
+                    if (lhs.IsStrict()
+                        && lhs.GetBase() is EnvironmentRecord
+                        && (lhs.GetReferencedName() == "eval" || lhs.GetReferencedName() == "arguments"))
                     {
                         throw new JavaScriptException(_engine.SyntaxError);
                     }
@@ -553,7 +534,7 @@ namespace Jint.Runtime
                 }
             }
 
-            return Completion.EmptyUndefined;
+            return new Completion(CompletionType.Normal, Undefined.Instance, null);
         }
 
         public Completion ExecuteBlockStatement(BlockStatement blockStatement)
@@ -573,7 +554,7 @@ namespace Jint.Runtime
                 System.Diagnostics.Debugger.Break();
             }
 
-            return Completion.Empty;
+            return new Completion(CompletionType.Normal, null, null);
         }
     }
 }

+ 62 - 78
Jint/Runtime/TypeConverter.cs

@@ -52,17 +52,10 @@ namespace Jint.Runtime
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.1
         /// </summary>
-        /// <param name="input"></param>
-        /// <param name="preferredType"></param>
-        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static JsValue ToPrimitive(JsValue input, Types preferredType = Types.None)
         {
-            if (ReferenceEquals(input, Null.Instance) || ReferenceEquals(input, Undefined.Instance))
-            {
-                return input;
-            }
-
-            if (input.IsPrimitive())
+            if (input._type == Types.Null || input._type == Types.Undefined || input.IsPrimitive())
             {
                 return input;
             }
@@ -74,28 +67,21 @@ namespace Jint.Runtime
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
         /// </summary>
-        /// <param name="o"></param>
-        /// <returns></returns>
         public static bool ToBoolean(JsValue o)
         {
-            if (o.IsObject())
-            {
-                return true;
-            }
-
             if (o.IsBoolean())
             {
-                return o.AsBoolean();
+                return ((JsBoolean) o)._value;
             }
 
-            if (ReferenceEquals(o, Undefined.Instance) || ReferenceEquals(o, Null.Instance))
+            if (o.IsUndefined() || o.IsNull())
             {
                 return false;
             }
 
             if (o.IsNumber())
             {
-                var n = o.AsNumber();
+                var n = ((JsNumber) o)._value;
                 if (n.Equals(0) || double.IsNaN(n))
                 {
                     return false;
@@ -106,13 +92,7 @@ namespace Jint.Runtime
 
             if (o.IsString())
             {
-                var s = o.AsString();
-                if (String.IsNullOrEmpty(s))
-                {
-                    return false;
-                }
-
-                return true;
+                return !((JsString) o).IsNullOrEmpty();
             }
 
             return true;
@@ -128,36 +108,35 @@ namespace Jint.Runtime
             // check number first as this is what is usually expected
             if (o.IsNumber())
             {
-                return o.AsNumber();
+                return ((JsNumber) o)._value;
             }
 
-            if (o.IsObject())
+            if (o.IsUndefined())
             {
-                var p = o.AsObject() as IPrimitiveInstance;
-                if (p != null)
-                {
-                    o = p.PrimitiveValue;
-                }
+                return double.NaN;
             }
 
-            if (ReferenceEquals(o, Undefined.Instance))
+            if (o.IsNull())
             {
-                return double.NaN;
+                return 0;
             }
 
-            if (ReferenceEquals(o, Null.Instance))
+            if (o._type == Types.Object)
             {
-                return 0;
+                if (o is IPrimitiveInstance p)
+                {
+                    o = p.PrimitiveValue;
+                }
             }
 
             if (o.IsBoolean())
             {
-                return o.AsBoolean() ? 1 : 0;
+                return ((JsBoolean) o)._value ? 1 : 0;
             }
 
             if (o.IsString())
             {
-                return ToNumber(o.AsString());
+                return ToNumber(o.AsStringWithoutTypeCheck());
             }
 
             return ToNumber(ToPrimitive(o, Types.Number));
@@ -171,7 +150,9 @@ namespace Jint.Runtime
                 return 0;
             }
 
-            var s = StringPrototype.TrimEx(input);
+            var s = StringPrototype.IsWhiteSpaceEx(input[0]) || StringPrototype.IsWhiteSpaceEx(input[input.Length - 1])
+                ? StringPrototype.TrimEx(input)
+                : input;
 
             if (string.IsNullOrEmpty(s))
             {
@@ -348,20 +329,29 @@ namespace Jint.Runtime
         {
             if (o.IsString())
             {
-                return o.AsString();
+                return o.AsStringWithoutTypeCheck();
+            }
+
+            if (o.IsUndefined())
+            {
+                return Undefined.Text;
+            }
+
+            if (o.IsNull())
+            {
+                return Null.Text;
             }
 
             if (o.IsObject())
             {
-                var p = o.AsObject() as IPrimitiveInstance;
-                if (p != null)
+                if (o is IPrimitiveInstance p)
                 {
                     o = p.PrimitiveValue;
                 }
                 else
                 {
                     var s = o.AsInstance<SymbolInstance>();
-                    if (s != null)
+                    if (!ReferenceEquals(s, null))
                     {
                         // TODO: throw a TypeError
                         // NB: But it requires an Engine reference
@@ -370,24 +360,14 @@ namespace Jint.Runtime
                 }
             }
 
-            if (ReferenceEquals(o, Undefined.Instance))
-            {
-                return Undefined.Text;
-            }
-
-            if (ReferenceEquals(o, Null.Instance))
-            {
-                return Null.Text;
-            }
-
             if (o.IsBoolean())
             {
-                return o.AsBoolean() ? "true" : "false";
+                return ((JsBoolean) o)._value ? "true" : "false";
             }
 
             if (o.IsNumber())
             {
-                return ToString(o.AsNumber());
+                return ToString(((JsNumber) o)._value);
             }
 
             if (o.IsSymbol())
@@ -398,43 +378,45 @@ namespace Jint.Runtime
             return ToString(ToPrimitive(o, Types.String));
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static ObjectInstance ToObject(Engine engine, JsValue value)
         {
             if (value.IsObject())
             {
-                return value.AsObject();
-            }
-
-            if (ReferenceEquals(value, Undefined.Instance))
-            {
-                throw new JavaScriptException(engine.TypeError);
-            }
-
-            if (ReferenceEquals(value, Null.Instance))
-            {
-                throw new JavaScriptException(engine.TypeError);
+                return (ObjectInstance) value;
             }
 
             if (value.IsBoolean())
             {
-                return engine.Boolean.Construct(value.AsBoolean());
+                return engine.Boolean.Construct(((JsBoolean) value)._value);
             }
 
             if (value.IsNumber())
             {
-                return engine.Number.Construct(value.AsNumber());
+                return engine.Number.Construct(((JsNumber) value)._value);
             }
 
             if (value.IsString())
             {
-                return engine.String.Construct(value.AsString());
+                return engine.String.Construct(value.AsStringWithoutTypeCheck());
             }
 
             if (value.IsSymbol())
             {
-                return engine.Symbol.Construct(value.AsSymbol());
+                return engine.Symbol.Construct(((JsSymbol) value)._value);
             }
 
+            if (value.IsUndefined() || value.IsNull())
+            {
+                ThrowTypeError(engine);
+            }
+            
+            ThrowTypeError(engine);
+            return null;
+        }
+
+        private static void ThrowTypeError(Engine engine)
+        {
             throw new JavaScriptException(engine.TypeError);
         }
 
@@ -442,8 +424,7 @@ namespace Jint.Runtime
         {
             if (value.IsObject())
             {
-                var primitive = value.TryCast<IPrimitiveInstance>();
-                if (primitive != null)
+                if (value is IPrimitiveInstance primitive)
                 {
                     return primitive.Type;
                 }
@@ -454,16 +435,19 @@ namespace Jint.Runtime
             return value.Type;
         }
 
-        public static void CheckObjectCoercible(Engine engine, JsValue o, MemberExpression expression,
+        public static void CheckObjectCoercible(
+            Engine engine,
+            JsValue o,
+            MemberExpression expression,
             object baseReference)
         {
-            if (o != Undefined.Instance && o != Null.Instance)
+            if (!o.IsUndefined() && !o.IsNull())
             {
                 return;
             }
 
-            if (engine.Options._ReferenceResolver != null &&
-                engine.Options._ReferenceResolver.CheckCoercible(o))
+            var referenceResolver = engine.Options.ReferenceResolver;
+            if (referenceResolver != null && referenceResolver.CheckCoercible(o))
             {
                 return;
             }
@@ -487,7 +471,7 @@ namespace Jint.Runtime
 
         public static void CheckObjectCoercible(Engine engine, JsValue o)
         {
-            if (ReferenceEquals(o, Undefined.Instance) || ReferenceEquals(o, Null.Instance))
+            if (o.IsUndefined() || o.IsNull())
             {
                 throw new JavaScriptException(engine.TypeError);
             }

+ 6 - 18
Jint/StrictModeScope.cs

@@ -2,7 +2,7 @@
 
 namespace Jint
 {
-    public class StrictModeScope : IDisposable
+    public readonly struct StrictModeScope : IDisposable
     {
         private readonly bool _strict;
         private readonly bool _force;
@@ -21,12 +21,15 @@ namespace Jint
                 _forcedRefCount = _refCount;
                 _refCount = 0;
             }
+            else
+            {
+                _forcedRefCount = 0;
+            }
 
             if (_strict)
             {
                 _refCount++;
             }
-
         }
 
         public void Dispose()
@@ -42,21 +45,6 @@ namespace Jint
             } 
         }
 
-        public static bool IsStrictModeCode
-        {
-            get { return _refCount > 0; }
-        }
-
-        public static int RefCount
-        {
-            get
-            {
-                return _refCount;
-            }
-            set
-            {
-                _refCount = value;
-            }
-        }
+        public static bool IsStrictModeCode => _refCount > 0;
     }
 }

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott