浏览代码

New interpreter engine (#559)

Marko Lahma 6 年之前
父节点
当前提交
3b06e02101
共有 73 个文件被更改,包括 3370 次插入2077 次删除
  1. 1 12
      Jint.Benchmark/ArrayStressBenchmark.cs
  2. 4 17
      Jint.Benchmark/DromaeoBenchmark.cs
  3. 1 11
      Jint.Benchmark/LinqJsBenchmark.cs
  4. 4 1
      Jint.Benchmark/MinimalScriptBenchmark.cs
  5. 1 13
      Jint.Benchmark/StopwatchBenchmark.cs
  6. 1 10
      Jint.Benchmark/SunSpiderBenchmark.cs
  7. 1 13
      Jint.Benchmark/UncacheableExpressionsBenchmark.cs
  8. 9 1
      Jint/ArrayExt.cs
  9. 15 157
      Jint/Engine.cs
  10. 1 1
      Jint/EsprimaExtensions.cs
  11. 69 5
      Jint/Native/Array/ArrayInstance.cs
  12. 29 27
      Jint/Native/Array/ArrayPrototype.cs
  13. 3 1
      Jint/Native/Function/EvalFunctionInstance.cs
  14. 8 4
      Jint/Native/Function/FunctionConstructor.cs
  15. 39 45
      Jint/Native/Function/FunctionPrototype.cs
  16. 6 10
      Jint/Native/Function/ScriptFunctionInstance.cs
  17. 6 0
      Jint/Native/JsString.cs
  18. 20 8
      Jint/Native/Math/MathInstance.cs
  19. 2 2
      Jint/Native/Object/ObjectConstructor.cs
  20. 7 6
      Jint/Native/Object/ObjectInstance.cs
  21. 2 2
      Jint/Native/String/StringInstance.cs
  22. 2 2
      Jint/Native/String/StringPrototype.cs
  23. 5 5
      Jint/Runtime/Debugger/DebugHandler.cs
  24. 38 1
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  25. 4 0
      Jint/Runtime/Environments/EnvironmentRecord.cs
  26. 40 16
      Jint/Runtime/Environments/LexicalEnvironment.cs
  27. 18 0
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  28. 15 0
      Jint/Runtime/ExceptionHelper.cs
  29. 0 1094
      Jint/Runtime/ExpressionIntepreter.cs
  30. 46 0
      Jint/Runtime/Interpreter/Expressions/JintArrayExpression.cs
  31. 190 0
      Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs
  32. 316 0
      Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs
  33. 168 0
      Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs
  34. 25 0
      Jint/Runtime/Interpreter/Expressions/JintConditionalExpression.cs
  35. 331 0
      Jint/Runtime/Interpreter/Expressions/JintExpression.cs
  36. 37 0
      Jint/Runtime/Interpreter/Expressions/JintFunctionExpression.cs
  37. 46 0
      Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs
  38. 64 0
      Jint/Runtime/Interpreter/Expressions/JintLiteralExpression.cs
  39. 34 0
      Jint/Runtime/Interpreter/Expressions/JintLogicalAndExpression.cs
  40. 34 0
      Jint/Runtime/Interpreter/Expressions/JintLogicalOrExpression.cs
  41. 52 0
      Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs
  42. 47 0
      Jint/Runtime/Interpreter/Expressions/JintNewExpression.cs
  43. 196 0
      Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs
  44. 38 0
      Jint/Runtime/Interpreter/Expressions/JintSequenceExpression.cs
  45. 16 0
      Jint/Runtime/Interpreter/Expressions/JintThisExpression.cs
  46. 120 0
      Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs
  47. 89 0
      Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs
  48. 115 0
      Jint/Runtime/Interpreter/JintStatementList.cs
  49. 22 0
      Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs
  50. 22 0
      Jint/Runtime/Interpreter/Statements/JintBreakStatement.cs
  51. 22 0
      Jint/Runtime/Interpreter/Statements/JintContinueStatement.cs
  52. 26 0
      Jint/Runtime/Interpreter/Statements/JintDebuggerStatement.cs
  53. 55 0
      Jint/Runtime/Interpreter/Statements/JintDoWhileStatement.cs
  54. 16 0
      Jint/Runtime/Interpreter/Statements/JintEmptyStatement.cs
  55. 21 0
      Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs
  56. 101 0
      Jint/Runtime/Interpreter/Statements/JintForInStatement.cs
  57. 85 0
      Jint/Runtime/Interpreter/Statements/JintForStatement.cs
  58. 16 0
      Jint/Runtime/Interpreter/Statements/JintFunctionDeclarationStatement.cs
  59. 38 0
      Jint/Runtime/Interpreter/Statements/JintIfStatement.cs
  60. 31 0
      Jint/Runtime/Interpreter/Statements/JintLabeledStatement.cs
  61. 19 0
      Jint/Runtime/Interpreter/Statements/JintProgram.cs
  62. 27 0
      Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs
  63. 139 0
      Jint/Runtime/Interpreter/Statements/JintStatement.cs
  64. 104 0
      Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs
  65. 32 0
      Jint/Runtime/Interpreter/Statements/JintSwitchStatement.cs
  66. 24 0
      Jint/Runtime/Interpreter/Statements/JintThrowStatement.cs
  67. 64 0
      Jint/Runtime/Interpreter/Statements/JintTryStatement.cs
  68. 63 0
      Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs
  69. 56 0
      Jint/Runtime/Interpreter/Statements/JintWhileStatement.cs
  70. 46 0
      Jint/Runtime/Interpreter/Statements/JintWithStatement.cs
  71. 1 1
      Jint/Runtime/References/Reference.cs
  72. 0 612
      Jint/Runtime/StatementInterpreter.cs
  73. 25 0
      Jint/Runtime/TypeConverter.cs

+ 1 - 12
Jint.Benchmark/ArrayStressBenchmark.cs

@@ -1,21 +1,10 @@
 using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Configs;
-using BenchmarkDotNet.Diagnosers;
-using BenchmarkDotNet.Jobs;
 
 namespace Jint.Benchmark
 {
-    [Config(typeof(Config))]
+    [MemoryDiagnoser]
     public class ArrayStressBenchmark : SingleScriptBenchmark
     {
-        private class Config : ManualConfig
-        {
-            public Config()
-            {
-                Add(Job.ShortRun);
-                Add(MemoryDiagnoser.Default);
-            }
-        }
         protected override string Script => "var ret=[],tmp,num=100,i=256;for(var j1=0;j1<i*15;j1++){ret=[];ret.length=i}for(var j2=0;j2<i*10;j2++){ret=new Array(i)}ret=[];for(var j3=0;j3<i;j3++){ret.unshift(j3)}ret=[];for(var j4=0;j4<i;j4++){ret.splice(0,0,j4)}var a=ret.slice();for(var j5=0;j5<i;j5++){tmp=a.shift()}var b=ret.slice();for(var j6=0;j6<i;j6++){tmp=b.splice(0,1)}ret=[];for(var j7=0;j7<i*25;j7++){ret.push(j7)}var c=ret.slice();for(var j8=0;j8<i*25;j8++){tmp=c.pop()}var done = true;";
 
         [Params(20)]

+ 4 - 17
Jint.Benchmark/DromaeoBenchmark.cs

@@ -3,33 +3,20 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Configs;
-using BenchmarkDotNet.Diagnosers;
-using BenchmarkDotNet.Jobs;
-using Jint;
 
-namespace Esprima.Benchmark
+namespace Jint.Benchmark
 {
-    [Config(typeof(Config))]
+    [MemoryDiagnoser]
     public class DromaeoBenchmark
     {
-        private class Config : ManualConfig
-        {
-            public Config()
-            {
-                Add(Job.MediumRun.WithLaunchCount(1));
-                Add(MemoryDiagnoser.Default);
-            }
-        }
-        
-        private static readonly Dictionary<string, string> files = new Dictionary<string, string>
+        public static readonly Dictionary<string, string> files = new Dictionary<string, string>
         {
             {"dromaeo-3d-cube", null},
             {"dromaeo-core-eval", null},
             {"dromaeo-object-array", null},
             {"dromaeo-object-regexp", null},
             {"dromaeo-object-string", null},
-            {"dromaeo-string-base64", null}
+            //{"dromaeo-string-base64", null}
         };
 
         private Engine engine;

+ 1 - 11
Jint.Benchmark/LinqJsBenchmark.cs

@@ -1,20 +1,10 @@
 using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Configs;
-using BenchmarkDotNet.Jobs;
 
 namespace Jint.Benchmark
 {
-    [Config(typeof(Config))]
+    [MemoryDiagnoser]
     public class LinqJsBenchmark : SingleScriptBenchmark
     {
-        private class Config : ManualConfig
-        {
-            public Config()
-            {
-                Add(Job.ShortRun);
-            }
-        }
-
         [Params(10)]
         public override int N { get; set; }
 

+ 4 - 1
Jint.Benchmark/MinimalScriptBenchmark.cs

@@ -1,5 +1,8 @@
-namespace Jint.Benchmark
+using BenchmarkDotNet.Attributes;
+
+namespace Jint.Benchmark
 {
+    [MemoryDiagnoser]
     public class MinimalScriptBenchmark : SingleScriptBenchmark
     {
         protected override string Script => "var done = true;";

+ 1 - 13
Jint.Benchmark/StopwatchBenchmark.cs

@@ -1,22 +1,10 @@
 using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Configs;
-using BenchmarkDotNet.Diagnosers;
-using BenchmarkDotNet.Jobs;
 
 namespace Jint.Benchmark
 {
-    [Config(typeof(Config))]
+    [MemoryDiagnoser]
     public class StopwatchBenchmark : SingleScriptBenchmark
     {
-        private class Config : ManualConfig
-        {
-            public Config()
-            {
-                Add(Job.ShortRun);
-                Add(MemoryDiagnoser.Default);
-            }
-        }
- 
         [Params(1)]
         public override int N { get; set; }
 

+ 1 - 10
Jint.Benchmark/SunSpiderBenchmark.cs

@@ -10,18 +10,9 @@ using Jint;
 
 namespace Esprima.Benchmark
 {
-    [Config(typeof(Config))]
+    [MemoryDiagnoser]
     public class SunSpiderBenchmark
     {
-        private class Config : ManualConfig
-        {
-            public Config()
-            {
-                Add(Job.ShortRun.WithLaunchCount(1));
-                Add(MemoryDiagnoser.Default);
-            }
-        }
-        
         private static readonly Dictionary<string, string> files = new Dictionary<string, string>
         {
             {"3d-cube", null},

+ 1 - 13
Jint.Benchmark/UncacheableExpressionsBenchmark.cs

@@ -4,9 +4,6 @@ using System.IO;
 using System.Linq;
 using System.Text;
 using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Configs;
-using BenchmarkDotNet.Diagnosers;
-using BenchmarkDotNet.Jobs;
 using Jint.Native;
 using Newtonsoft.Json;
 using Undefined = Jint.Native.Undefined;
@@ -16,18 +13,9 @@ namespace Jint.Benchmark
     /// <summary>
     /// Test case for situation where object is projected via filter and map, Jint deems code as uncacheable.
     /// </summary>
-    [Config(typeof(Config))]
+    [MemoryDiagnoser]
     public class UncacheableExpressionsBenchmark
     {
-        private class Config : ManualConfig
-        {
-            public Config()
-            {
-                Add(Job.MediumRun.WithLaunchCount(1));
-                Add(MemoryDiagnoser.Default);
-            }
-        }
-
         private Document doc;
 
         private string targetObject;

+ 9 - 1
Jint/ArrayExt.cs

@@ -1,4 +1,6 @@
 
+using System.Runtime.CompilerServices;
+
 namespace System
 {
     internal static class ArrayExt
@@ -9,10 +11,16 @@ namespace System
 
             static EmptyArray()
             {
-                EmptyArray<T>.Value = new T[0];
+                Value = new T[0];
             }
         }
 
+        #if NETSTANDARD
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static T[] Empty<T>() => Array.Empty<T>();
+        #else
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static T[] Empty<T>() => EmptyArray<T>.Value;
+        #endif
     }
 }

+ 15 - 157
Jint/Engine.cs

@@ -27,6 +27,7 @@ using Jint.Runtime.Debugger;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Environments;
 using Jint.Runtime.Interop;
+using Jint.Runtime.Interpreter;
 using Jint.Runtime.References;
 
 namespace Jint
@@ -40,22 +41,20 @@ namespace Jint
             Loc = true
         };
 
-        private readonly ExpressionInterpreter _expressions;
-        private readonly StatementInterpreter _statements;
         private readonly ExecutionContextStack _executionContexts;
         private JsValue _completionValue = JsValue.Undefined;
         private int _statementsCount;
         private long _initialMemoryUsage;
         private long _timeoutTicks;
-        private INode _lastSyntaxNode;
+        internal INode _lastSyntaxNode;
 
         // cached access
         private readonly bool _isDebugMode;
         internal readonly bool _isStrict;
         private readonly int _maxStatements;
         private readonly long _memoryLimit;
-        private readonly bool _runBeforeStatementChecks;
-        private readonly IReferenceResolver _referenceResolver;
+        internal readonly bool _runBeforeStatementChecks;
+        internal readonly IReferenceResolver _referenceResolver;
         internal readonly ReferencePool _referencePool;
         internal readonly ArgumentsInstancePool _argumentsInstancePool;
         internal readonly JsValueArrayPool _jsValueArrayPool;
@@ -246,9 +245,6 @@ namespace Jint
             Eval = new EvalFunctionInstance(this, System.ArrayExt.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
             Global.FastAddProperty("eval", Eval, true, false, true);
 
-            _statements = new StatementInterpreter(this);
-            _expressions = new ExpressionInterpreter(this);
-
             if (Options._IsClrAllowed)
             {
                 Global.FastAddProperty("System", new NamespaceReference(this, "System"), false, false, false);
@@ -433,7 +429,8 @@ namespace Jint
             {
                 DeclarationBindingInstantiation(DeclarationBindingType.GlobalCode, program.HoistingScope.FunctionDeclarations, program.HoistingScope.VariableDeclarations, null, null);
 
-                var result = _statements.ExecuteProgram(program);
+                var list = new JintStatementList(this, null, program.Body);
+                var result = list.Execute();
                 if (result.Type == CompletionType.Throw)
                 {
                     var ex = new JavaScriptException(result.GetValueOrDefault()).SetCallstack(this, result.Location);
@@ -459,91 +456,7 @@ namespace Jint
             return _completionValue;
         }
 
-        public Completion ExecuteStatement(Statement statement)
-        {
-            _lastSyntaxNode = statement;
-
-            if (_runBeforeStatementChecks)
-            {
-                BeforeExecuteStatement(statement);
-            }
-
-            switch (statement.Type)
-            {
-                case Nodes.BlockStatement:
-                    return _statements.ExecuteStatementList(((BlockStatement) statement).Body);
-
-                case Nodes.ReturnStatement:
-                    var jsValue = ((ReturnStatement) statement).Argument == null
-                        ? Undefined.Instance
-                        : GetValue(EvaluateExpression(((ReturnStatement) statement).Argument), true);
-
-                    return new Completion(CompletionType.Return, jsValue, null);
-
-                case Nodes.VariableDeclaration:
-                    return _statements.ExecuteVariableDeclaration((VariableDeclaration) statement);
-
-                case Nodes.BreakStatement:
-                    return _statements.ExecuteBreakStatement((BreakStatement) statement);
-
-                case Nodes.ContinueStatement:
-                    return _statements.ExecuteContinueStatement((ContinueStatement) statement);
-
-                case Nodes.DoWhileStatement:
-                    return _statements.ExecuteDoWhileStatement((DoWhileStatement) statement);
-
-                case Nodes.EmptyStatement:
-                    return new Completion(CompletionType.Normal, null, null);
-
-                case Nodes.ExpressionStatement:
-                    return new Completion(
-                        CompletionType.Normal,
-                        GetValue(EvaluateExpression(((ExpressionStatement) statement).Expression), true),
-                        null);
-
-                case Nodes.ForStatement:
-                    return _statements.ExecuteForStatement((ForStatement) statement);
-
-                case Nodes.ForInStatement:
-                    return _statements.ExecuteForInStatement((ForInStatement) statement);
-
-                case Nodes.IfStatement:
-                    return _statements.ExecuteIfStatement((IfStatement) statement);
-
-                case Nodes.LabeledStatement:
-                    return _statements.ExecuteLabeledStatement((LabeledStatement) statement);
-
-                case Nodes.SwitchStatement:
-                    return _statements.ExecuteSwitchStatement((SwitchStatement) statement);
-
-                case Nodes.FunctionDeclaration:
-                    return new Completion(CompletionType.Normal, null, null);
-
-                case Nodes.ThrowStatement:
-                    return _statements.ExecuteThrowStatement((ThrowStatement) statement);
-
-                case Nodes.TryStatement:
-                    return _statements.ExecuteTryStatement((TryStatement) statement);
-
-                case Nodes.WhileStatement:
-                    return _statements.ExecuteWhileStatement((WhileStatement) statement);
-
-                case Nodes.WithStatement:
-                    return _statements.ExecuteWithStatement((WithStatement) statement);
-
-                case Nodes.DebuggerStatement:
-                    return _statements.ExecuteDebuggerStatement((DebuggerStatement) statement);
-
-                case Nodes.Program:
-                    return _statements.ExecuteProgram((Program) statement);
-
-                default:
-                    ExceptionHelper.ThrowArgumentOutOfRangeException();
-                    return new Completion(CompletionType.Normal, null, null);
-            }
-        }
-
-        private void BeforeExecuteStatement(Statement statement)
+        internal void RunBeforeExecuteStatementChecks(Statement statement)
         {
             if (_maxStatements > 0 && _statementsCount++ > _maxStatements)
             {
@@ -577,66 +490,6 @@ namespace Jint
             }
         }
 
-        public object EvaluateExpression(INode expression)
-        {
-            _lastSyntaxNode = expression;
-
-            switch (expression.Type)
-            {
-                case Nodes.AssignmentExpression:
-                    return _expressions.EvaluateAssignmentExpression((AssignmentExpression) expression);
-
-                case Nodes.ArrayExpression:
-                    return _expressions.EvaluateArrayExpression((ArrayExpression) expression);
-
-                case Nodes.BinaryExpression:
-                    return _expressions.EvaluateBinaryExpression((BinaryExpression) expression);
-
-                case Nodes.CallExpression:
-                    return _expressions.EvaluateCallExpression((CallExpression) expression);
-
-                case Nodes.ConditionalExpression:
-                    return _expressions.EvaluateConditionalExpression((ConditionalExpression) expression);
-
-                case Nodes.FunctionExpression:
-                    return _expressions.EvaluateFunctionExpression((IFunction) expression);
-
-                case Nodes.Identifier:
-                    return _expressions.EvaluateIdentifier((Identifier) expression);
-
-                case Nodes.Literal:
-                    return _expressions.EvaluateLiteral((Literal) expression);
-
-                case Nodes.LogicalExpression:
-                    return _expressions.EvaluateLogicalExpression((BinaryExpression) expression);
-
-                case Nodes.MemberExpression:
-                    return _expressions.EvaluateMemberExpression((MemberExpression) expression);
-
-                case Nodes.NewExpression:
-                    return _expressions.EvaluateNewExpression((NewExpression) expression);
-
-                case Nodes.ObjectExpression:
-                    return _expressions.EvaluateObjectExpression((ObjectExpression) expression);
-
-                case Nodes.SequenceExpression:
-                    return _expressions.EvaluateSequenceExpression((SequenceExpression) expression);
-
-                case Nodes.ThisExpression:
-                    return _expressions.EvaluateThisExpression((ThisExpression) expression);
-
-                case Nodes.UpdateExpression:
-                    return _expressions.EvaluateUpdateExpression((UpdateExpression) expression);
-
-                case Nodes.UnaryExpression:
-                    return _expressions.EvaluateUnaryExpression((UnaryExpression) expression);
-
-                default:
-                    ExceptionHelper.ThrowArgumentOutOfRangeException();
-                    return null;
-            }
-        }
-
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1
         /// </summary>
@@ -657,6 +510,11 @@ namespace Jint
                 return ((Completion) value).Value;
             }
 
+            return GetValue(reference, returnReferenceToPool);
+        }
+
+        internal JsValue GetValue(Reference reference, bool returnReferenceToPool)
+        {
             if (reference._baseValue._type == Types.Undefined)
             {
                 if (_referenceResolver != null &&
@@ -664,6 +522,7 @@ namespace Jint
                 {
                     return val;
                 }
+
                 ExceptionHelper.ThrowReferenceError(this, reference.GetReferencedName() + " is not defined");
             }
 
@@ -682,6 +541,7 @@ namespace Jint
                 {
                     _referencePool.Return(reference);
                 }
+
                 if (!(reference._baseValue._type != Types.Object && reference._baseValue._type != Types.None))
                 {
                     var o = TypeConverter.ToObject(this, baseValue);
@@ -708,7 +568,7 @@ namespace Jint
                         return Undefined.Instance;
                     }
 
-                    var callable = (ICallable)getter.AsObject();
+                    var callable = (ICallable) getter.AsObject();
                     return callable.Call(baseValue, Arguments.Empty);
                 }
             }
@@ -731,8 +591,6 @@ namespace Jint
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.2
         /// </summary>
-        /// <param name="reference"></param>
-        /// <param name="value"></param>
         public void PutValue(Reference reference, JsValue value)
         {
             if (reference._baseValue._type == Types.Undefined)

+ 1 - 1
Jint/EsprimaExtensions.cs

@@ -5,7 +5,7 @@ using Jint.Runtime;
 
 namespace Jint
 {
-    internal static class EsprimaExtensions
+    public static class EsprimaExtensions
     {
         public static string GetKey<T>(this T expression) where T : Expression
         {

+ 69 - 5
Jint/Native/Array/ArrayInstance.cs

@@ -4,9 +4,6 @@ using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 
-using PropertyDescriptor = Jint.Runtime.Descriptors.PropertyDescriptor;
-using TypeConverter = Jint.Runtime.TypeConverter;
-
 namespace Jint.Native.Array
 {
     public class ArrayInstance : ObjectInstance
@@ -34,6 +31,9 @@ namespace Jint.Native.Array
             }
         }
 
+        /// <summary>
+        /// Possibility to construct valid array fast, requires that supplied array does not have holes.
+        /// </summary>
         public ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine, objectClass: "Array")
         {
             int length = 0;
@@ -374,6 +374,32 @@ namespace Jint.Native.Array
             return base.GetOwnProperty(propertyName);
         }
 
+        internal PropertyDescriptor GetOwnProperty(uint index)
+        {
+            if (TryGetDescriptor(index, out var result))
+            {
+                return result;
+            }
+
+            return PropertyDescriptor.Undefined;
+        }
+
+        internal JsValue Get(uint index)
+        {
+            var desc = GetProperty(index);
+            return UnwrapJsValue(desc);
+        }
+
+        internal PropertyDescriptor GetProperty(uint index)
+        {
+            var prop = GetOwnProperty(index);
+            if (prop != PropertyDescriptor.Undefined)
+            {
+                return prop;
+            }
+            return Prototype?.GetProperty(TypeConverter.ToString(index)) ?? PropertyDescriptor.Undefined;
+        }
+
         protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
         {
             if (IsArrayIndex(propertyName, out var index))
@@ -604,8 +630,7 @@ namespace Jint.Native.Array
 
             if (canUseDense)
             {
-                var temp = _dense;
-                if (index >= (uint) temp.Length)
+                if (index >= (uint) _dense.Length)
                 {
                     EnsureCapacity((uint) newSize);
                 }
@@ -809,5 +834,44 @@ namespace Jint.Native.Array
             }
             return array;
         }
+
+        /// <summary>
+        /// Fast path for concatenating sane-sized arrays, we assume size has been calculated.
+        /// </summary>
+        internal void CopyValues(ArrayInstance source, uint sourceStartIndex, uint targetStartIndex, uint length)
+        {
+            if (length == 0)
+            {
+                return;
+            }
+
+            if (_dense != null && source._dense != null
+                               && _dense.Length >= targetStartIndex + length
+                               && ReferenceEquals(_dense[targetStartIndex], null))
+            {
+                uint j = 0;
+                for (uint i = sourceStartIndex; i < sourceStartIndex + length; ++i, j++)
+                {
+                    var sourcePropertyDescriptor = i < source._dense.Length && source._dense[i] != null
+                        ? source._dense[i]
+                        : source.GetProperty(i.ToString());
+
+                    _dense[targetStartIndex + j] = sourcePropertyDescriptor?._value != null
+                        ? new PropertyDescriptor(sourcePropertyDescriptor._value, PropertyFlag.ConfigurableEnumerableWritable)
+                        : null;
+                }
+            }
+            else
+            {
+                // slower version
+                for (uint k = sourceStartIndex; k < length; k++)
+                {
+                    if (source.TryGetValue(k, out var subElement))
+                    {
+                        SetIndexValue(targetStartIndex, subElement, updateLength: false);
+                    }
+                }
+            }
+        }
     }
 }

+ 29 - 27
Jint/Native/Array/ArrayPrototype.cs

@@ -5,6 +5,8 @@ using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
+using Jint.Runtime.Interpreter.Expressions;
+
 using static System.String;
 
 namespace Jint.Native.Array
@@ -254,7 +256,7 @@ namespace Jint.Native.Array
             {
                 if (o.TryGetValue(i, out var value))
                 {
-                    var same = ExpressionInterpreter.StrictlyEqual(value, searchElement);
+                    var same = JintBinaryExpression.StrictlyEqual(value, searchElement);
                     if (same)
                     {
                         return i;
@@ -558,7 +560,7 @@ namespace Jint.Native.Array
             {
                 if (o.TryGetValue(k, out var elementK))
                 {
-                    var same = ExpressionInterpreter.StrictlyEqual(elementK, searchElement);
+                    var same = JintBinaryExpression.StrictlyEqual(elementK, searchElement);
                     if (same)
                     {
                         return k;
@@ -889,17 +891,24 @@ namespace Jint.Native.Array
                 ExceptionHelper.ThrowRangeError(_engine, "Invalid array length");;
             }
 
-            var a = Engine.Array.Construct((uint) (final - k));
-            uint n = 0;
-            for (; k < final; k++)
+            var length = (uint) System.Math.Max(0, (long) final - (long) k);
+            var a = Engine.Array.Construct(length);
+            if (thisObj is ArrayInstance ai)
+            {
+                a.CopyValues(ai, (uint) k, 0, length);
+            }
+            else
             {
-                if (o.TryGetValue(k, out var kValue))
+                // slower path
+                for (uint n = 0; k < final; k++, n++)
                 {
-                    a.SetIndexValue(n, kValue, updateLength: true);
+                    if (o.TryGetValue(k, out var kValue))
+                    {
+                        a.SetIndexValue(n, kValue, updateLength: false);
+                    }
                 }
-
-                n++;
             }
+            a.DefineOwnProperty("length", new PropertyDescriptor(length, PropertyFlag.None), false);
 
             return a;
         }
@@ -1058,49 +1067,42 @@ namespace Jint.Native.Array
         private JsValue Concat(JsValue thisObj, JsValue[] arguments)
         {
             var o = TypeConverter.ToObject(Engine, thisObj);
-            uint n = 0;
             var items = new List<JsValue>(arguments.Length + 1) {o};
             items.AddRange(arguments);
 
             // try to find best capacity
-            bool hasNonSpreadables = false;
+            bool hasObjectSpreadables = false;
             uint capacity = 0;
             for (var i = 0; i < items.Count; i++)
             {
                 uint increment;
                 var objectInstance = items[i] as ObjectInstance;
-                if (objectInstance == null
-                    || (hasNonSpreadables |= objectInstance.IsConcatSpreadable) == false)
+                if (objectInstance == null)
                 {
                     increment = 1;
                 }
                 else
                 {
+                    var isConcatSpreadable = objectInstance.IsConcatSpreadable;
+                    hasObjectSpreadables |= isConcatSpreadable;
                     var operations = ArrayOperations.For(objectInstance);
-                    increment = operations.GetLength();
+                    increment = isConcatSpreadable ? operations.GetLength() : 1; 
                 }
                 capacity += increment;
             }
 
+            uint n = 0;
             var a = Engine.Array.ConstructFast(capacity);
             for (var i = 0; i < items.Count; i++)
             {
                 var e = items[i];
                 if (e is ArrayInstance eArray
-                    && (!hasNonSpreadables || eArray.IsConcatSpreadable))
+                    && eArray.IsConcatSpreadable)
                 {
-                    var len = eArray.GetLength();
-                    for (uint k = 0; k < len; k++)
-                    {
-                        if (eArray.TryGetValue(k, out var subElement))
-                        {
-                            a.SetIndexValue(n, subElement, updateLength: false);
-                        }
-
-                        n++;
-                    }
+                    a.CopyValues(eArray, 0, n, eArray.GetLength());
+                    n += eArray.GetLength();
                 }
-                else if (hasNonSpreadables
+                else if (hasObjectSpreadables
                          && e is ObjectInstance oi 
                          && oi.IsConcatSpreadable)
                 {
@@ -1455,7 +1457,7 @@ namespace Jint.Native.Array
                     return _array.TryGetValue((uint) index, out value);
                 }
 
-                public override JsValue Get(ulong index) => _array.Get(TypeConverter.ToString(index));
+                public override JsValue Get(ulong index) => _array.Get((uint) index);
 
                 public override void DeleteAt(ulong index) => _array.DeleteAt((uint) index);
 

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

@@ -2,6 +2,7 @@
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Environments;
+using Jint.Runtime.Interpreter.Statements;
 
 namespace Jint.Native.Function
 {
@@ -62,7 +63,8 @@ namespace Jint.Native.Function
                                 this, 
                                 arguments);
 
-                            var result = _engine.ExecuteStatement(program);
+                            var statement = JintStatement.Build(_engine, program);
+                            var result = statement.Execute();
                             var value = result.GetValueOrDefault();
 
                             if (argumentInstanceRented)

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

@@ -84,8 +84,10 @@ namespace Jint.Native.Function
                 Engine,
                 function,
                 LexicalEnvironment.NewDeclarativeEnvironment(Engine, Engine.ExecutionContext.LexicalEnvironment),
-                function.Strict
-                ) { Extensible = true };
+                function.Strict)
+            {
+                Extensible = true
+            };
 
             return functionObject;
 
@@ -102,8 +104,10 @@ namespace Jint.Native.Function
                 Engine,
                 functionDeclaration,
                 LexicalEnvironment.NewDeclarativeEnvironment(Engine, Engine.ExecutionContext.LexicalEnvironment),
-                functionDeclaration.Strict
-                ) { Extensible = true };
+                functionDeclaration.Strict)
+            {
+                Extensible = true
+            };
 
             return functionObject;
         }

+ 39 - 45
Jint/Native/Function/FunctionPrototype.cs

@@ -1,4 +1,6 @@
-using Jint.Native.Object;
+using System;
+using Jint.Native.Array;
+using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
@@ -18,14 +20,14 @@ namespace Jint.Native.Function
 
         public static FunctionPrototype CreatePrototypeObject(Engine engine)
         {
-            var obj = new FunctionPrototype(engine);
-            obj.Extensible = true;
-
-            // 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;
+            var obj = new FunctionPrototype(engine)
+            {
+                Extensible = true,
+                // The value of the [[Prototype]] internal property of the Function prototype object is the standard built-in Object prototype object
+                Prototype = engine.Object.PrototypeObject
+            };
 
             obj.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden));
-
             return obj;
         }
 
@@ -46,16 +48,18 @@ namespace Jint.Native.Function
             });
 
             var thisArg = arguments.At(0);
-            var f = new BindFunctionInstance(Engine) {Extensible = true};
-            f.TargetFunction = thisObj;
-            f.BoundThis = thisArg;
-            f.BoundArgs = arguments.Skip(1);
-            f.Prototype = Engine.Function.PrototypeObject;
-
-            var o = target as FunctionInstance;
-            if (!ReferenceEquals(o, null))
+            var f = new BindFunctionInstance(Engine)
+            {
+                Extensible = true,
+                TargetFunction = thisObj,
+                BoundThis = thisArg,
+                BoundArgs = arguments.Skip(1),
+                Prototype = Engine.Function.PrototypeObject
+            };
+
+            if (target is FunctionInstance functionInstance)
             {
-                var l = TypeConverter.ToNumber(o.Get("length")) - (arguments.Length - 1);
+                var l = TypeConverter.ToNumber(functionInstance.Get("length")) - (arguments.Length - 1);
                 f.SetOwnProperty("length", new PropertyDescriptor(System.Math.Max(l, 0), PropertyFlag.AllForbidden));
             }
             else
@@ -63,7 +67,6 @@ namespace Jint.Native.Function
                 f.SetOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden));
             }
 
-
             var thrower = Engine.Function.ThrowTypeError;
             const PropertyFlag flags = PropertyFlag.EnumerableSet | PropertyFlag.ConfigurableSet;
             f.DefineOwnProperty("caller", new GetSetPropertyDescriptor(thrower, thrower, flags), false);
@@ -74,47 +77,34 @@ namespace Jint.Native.Function
 
         private JsValue ToString(JsValue thisObj, JsValue[] arguments)
         {
-            var func = thisObj.TryCast<FunctionInstance>();
-
-            if (ReferenceEquals(func, null))
+            if (!(thisObj is FunctionInstance))
             {
-                ExceptionHelper.ThrowTypeError(_engine, "Function object expected.");
+                return ExceptionHelper.ThrowTypeError<FunctionInstance>(_engine, "Function object expected.");
             }
 
             return "function() {{ ... }}";
         }
 
-        public JsValue Apply(JsValue thisObject, JsValue[] arguments)
+        private JsValue Apply(JsValue thisObject, JsValue[] arguments)
         {
-            var func = thisObject.TryCast<ICallable>();
+            var func = thisObject as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(Engine);
             var thisArg = arguments.At(0);
             var argArray = arguments.At(1);
 
-            if (func is null)
-            {
-                return ExceptionHelper.ThrowTypeError<JsValue>(Engine);
-            }
-
             if (argArray.IsNullOrUndefined())
             {
                 return func.Call(thisArg, Arguments.Empty);
             }
 
-            var argArrayObj = argArray.TryCast<ObjectInstance>();
-            if (argArrayObj is null)
-            {
-                return ExceptionHelper.ThrowTypeError<JsValue>(Engine);
-            }
-
-            var len = ((JsNumber) argArrayObj.Get("length"))._value;
-            uint n = TypeConverter.ToUint32(len);
+            var argArrayObj = argArray as ObjectInstance ?? ExceptionHelper.ThrowTypeError<ObjectInstance>(Engine);
+            var operations = ArrayPrototype.ArrayOperations.For(argArrayObj);
 
+            uint n = operations.GetLength();
             var argList = _engine._jsValueArrayPool.RentArray((int) n);
-            for (int index = 0; index < n; index++)
+            for (uint i = 0; i < n; i++)
             {
-                string indexName = TypeConverter.ToString(index);
-                var nextArg = argArrayObj.Get(indexName);
-                argList[index] = nextArg;
+                var nextArg = operations.Get(i);
+                argList[i] = nextArg;
             }
 
             var result = func.Call(thisArg, argList);
@@ -123,15 +113,19 @@ namespace Jint.Native.Function
             return result;
         }
 
-        public JsValue CallImpl(JsValue thisObject, JsValue[] arguments)
+        private JsValue CallImpl(JsValue thisObject, JsValue[] arguments)
         {
-            var func = thisObject.TryCast<ICallable>();
-            if (func is null)
+            var func = thisObject as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(Engine);
+            JsValue[] values = ArrayExt.Empty<JsValue>();
+            if (arguments.Length > 1)
             {
-                return ExceptionHelper.ThrowTypeError<JsValue>(Engine);
+                values = _engine._jsValueArrayPool.RentArray(arguments.Length - 1);
+                System.Array.Copy(arguments, 1, values, 0, arguments.Length - 1);
             }
 
-            return func.Call(arguments.At(0), arguments.Length == 0 ? arguments : arguments.Skip(1));
+            var result = func.Call(arguments.At(0), values);
+
+            return result;
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)

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

@@ -5,31 +5,27 @@ using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Environments;
+using Jint.Runtime.Interpreter.Statements;
 
 namespace Jint.Native.Function
 {
-    /// <summary>
-    ///
-    /// </summary>
     public sealed class ScriptFunctionInstance : FunctionInstance, IConstructor
     {
         private readonly IFunction _functionDeclaration;
+        private readonly JintStatement _functionBody;
 
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
         /// </summary>
-        /// <param name="engine"></param>
-        /// <param name="functionDeclaration"></param>
-        /// <param name="scope"></param>
-        /// <param name="strict"></param>
         public ScriptFunctionInstance(
             Engine engine, 
-            IFunction functionDeclaration, 
-            LexicalEnvironment scope, 
+            IFunction functionDeclaration,
+            LexicalEnvironment scope,
             bool strict)
             : base(engine, functionDeclaration.Id?.Name ?? "", GetParameterNames(functionDeclaration), scope, strict)
         {
             _functionDeclaration = functionDeclaration;
+            _functionBody = JintStatement.Build(engine, functionDeclaration.Body);
 
             Extensible = true;
             Prototype = _engine.Function.PrototypeObject;
@@ -120,7 +116,7 @@ namespace Jint.Native.Function
                         this,
                         arguments);
 
-                    var result = _engine.ExecuteStatement(_functionDeclaration.Body);
+                    var result = _functionBody.Execute();
                     
                     var value = result.GetValueOrDefault();
                     

+ 6 - 0
Jint/Native/JsString.cs

@@ -13,6 +13,12 @@ namespace Jint.Native
 
         public static readonly JsString Empty = new JsString("");
         private static readonly JsString NullString = new JsString("null");
+        internal static readonly JsString UndefinedString = new JsString("undefined");
+        internal static readonly JsString ObjectString = new JsString("object");
+        internal static readonly JsString FunctionString = new JsString("function");
+        internal static readonly JsString BooleanString = new JsString("boolean");
+        internal static readonly JsString StringString = new JsString("string");
+        internal static readonly JsString NumberString = new JsString("number");
 
         internal string _value;
 

+ 20 - 8
Jint/Native/Math/MathInstance.cs

@@ -464,22 +464,34 @@ namespace Jint.Native.Math
             var x = TypeConverter.ToNumber(arguments.At(0));
             var y = TypeConverter.ToNumber(arguments.At(1));
 
-            if (double.IsNaN(y))
+            // check easy case where values are valid
+            if (x > 1 && y > 1 && x < int.MaxValue && y < int.MaxValue)
             {
-                return double.NaN;
+                return System.Math.Pow(x, y);
             }
 
-            if (y.Equals(0))
+            if (y == 0)
             {
                 return 1;
             }
+            
+            return HandlePowUnlikely(y, x);
+        }
+
+        private static JsValue HandlePowUnlikely(double y, double x)
+        {
+            if (double.IsNaN(y))
+            {
+                return double.NaN;
+            }
 
-            if (double.IsNaN(x) && !y.Equals(0))
+            if (double.IsNaN(x))
             {
                 return double.NaN;
             }
 
-            if (System.Math.Abs(x) > 1)
+            var absX = System.Math.Abs(x);
+            if (absX > 1)
             {
                 if (double.IsPositiveInfinity(y))
                 {
@@ -492,7 +504,7 @@ namespace Jint.Native.Math
                 }
             }
 
-            if (System.Math.Abs(x).Equals(1))
+            if (absX == 1)
             {
                 if (double.IsInfinity(y))
                 {
@@ -500,7 +512,7 @@ namespace Jint.Native.Math
                 }
             }
 
-            if (System.Math.Abs(x) < 1)
+            if (absX < 1)
             {
                 if (double.IsPositiveInfinity(y))
                 {
@@ -593,7 +605,7 @@ namespace Jint.Native.Math
             }
 
             // If x<0 and x is finite and y is finite and y is not an integer, the result is NaN.
-            if (x < 0 && !double.IsInfinity(x) && !double.IsInfinity(y) && !y.Equals((int)y))
+            if (x < 0 && !double.IsInfinity(x) && !double.IsInfinity(y) && !y.Equals((int) y))
             {
                 return double.NaN;
             }

+ 2 - 2
Jint/Native/Object/ObjectConstructor.cs

@@ -109,8 +109,8 @@ namespace Jint.Native.Object
             {
                 Extensible = true,
                 Prototype = Engine.Object.PrototypeObject,
-                _properties =  propertyCount > 0
-                    ? new StringDictionarySlim<PropertyDescriptor>(System.Math.Max(2, propertyCount))
+                _properties =  propertyCount > 1
+                    ? new StringDictionarySlim<PropertyDescriptor>(propertyCount)
                     : null
             };
 

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

@@ -15,6 +15,7 @@ using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
+using Jint.Runtime.Interpreter.Expressions;
 
 namespace Jint.Native.Object
 {
@@ -568,9 +569,9 @@ namespace Jint.Native.Object
                 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)))
+                ((ReferenceEquals(currentGet, null) && ReferenceEquals(descGet, null)) || (!ReferenceEquals(currentGet, null) && !ReferenceEquals(descGet, null) && JintExpression.SameValue(currentGet, descGet))) &&
+                ((ReferenceEquals(currentSet, null) && ReferenceEquals(descSet, null)) || (!ReferenceEquals(currentSet, null) && !ReferenceEquals(descSet, null) && JintExpression.SameValue(currentSet, descSet))) &&
+                ((ReferenceEquals(currentValue, null) && ReferenceEquals(descValue, null)) || (!ReferenceEquals(currentValue, null) && !ReferenceEquals(descValue, null) && JintBinaryExpression.StrictlyEqual(currentValue, descValue)))
             )
             {
                 return true;
@@ -647,7 +648,7 @@ namespace Jint.Native.Object
 
                         if (!current.Writable)
                         {
-                            if (!ReferenceEquals(descValue, null) && !ExpressionInterpreter.SameValue(descValue, currentValue))
+                            if (!ReferenceEquals(descValue, null) && !JintExpression.SameValue(descValue, currentValue))
                             {
                                 if (throwOnError)
                                 {
@@ -663,9 +664,9 @@ namespace Jint.Native.Object
                 {
                     if (!current.Configurable)
                     {
-                        if ((!ReferenceEquals(descSet, null) && !ExpressionInterpreter.SameValue(descSet, currentSet ?? Undefined))
+                        if ((!ReferenceEquals(descSet, null) && !JintExpression.SameValue(descSet, currentSet ?? Undefined))
                             ||
-                            (!ReferenceEquals(descGet, null) && !ExpressionInterpreter.SameValue(descGet, currentGet ?? Undefined)))
+                            (!ReferenceEquals(descGet, null) && !JintExpression.SameValue(descGet, currentGet ?? Undefined)))
                         {
                             if (throwOnError)
                             {

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

@@ -52,12 +52,12 @@ namespace Jint.Native.String
                 return desc;
             }
 
-            var integer = TypeConverter.ToInteger(propertyName);
-            if (integer == 0 && propertyName != "0" || propertyName != System.Math.Abs(integer).ToString())
+            if (!TypeConverter.CanBeIndex(propertyName))
             {
                 return PropertyDescriptor.Undefined;
             }
 
+            var integer = TypeConverter.ToInteger(propertyName);
             var str = PrimitiveValue;
             var dIndex = integer;
             if (!IsInt(dIndex))

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

@@ -443,8 +443,8 @@ namespace Jint.Native.String
             }
 
             var len = s.Length;
-            var intStart = (int)TypeConverter.ToInteger(start);
-            var intEnd = arguments.At(1).IsUndefined() ? len : (int)TypeConverter.ToInteger(end);
+            var intStart = (int) start;
+            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);

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

@@ -162,7 +162,7 @@ namespace Jint.Runtime.Debugger
         private static Dictionary<string, JsValue> GetLocalVariables(LexicalEnvironment lex)
         {
             Dictionary<string, JsValue> locals = new Dictionary<string, JsValue>();
-            if (!ReferenceEquals(lex?.Record, null))
+            if (!ReferenceEquals(lex?._record, null))
             {
                 AddRecordsFromEnvironment(lex, locals);
             }
@@ -174,22 +174,22 @@ namespace Jint.Runtime.Debugger
             Dictionary<string, JsValue> globals = new Dictionary<string, JsValue>();
             LexicalEnvironment tempLex = lex;
 
-            while (tempLex != null && !ReferenceEquals(tempLex.Record, null))
+            while (!ReferenceEquals(tempLex?._record, null))
             {
                 AddRecordsFromEnvironment(tempLex, globals);
-                tempLex = tempLex.Outer;
+                tempLex = tempLex._outer;
             }
             return globals;
         }
 
         private static void AddRecordsFromEnvironment(LexicalEnvironment lex, Dictionary<string, JsValue> locals)
         {
-            var bindings = lex.Record.GetAllBindingNames();
+            var bindings = lex._record.GetAllBindingNames();
             foreach (var binding in bindings)
             {
                 if (locals.ContainsKey(binding) == false)
                 {
-                    var jsValue = lex.Record.GetBindingValue(binding, false);
+                    var jsValue = lex._record.GetBindingValue(binding, false);
                     if (jsValue.TryCast<ICallable>() == null)
                     {
                         locals.Add(binding, jsValue);

+ 38 - 1
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
-using Esprima.Ast;
+ using System.Runtime.CompilerServices;
+ using Esprima.Ast;
 using Jint.Collections;
 using Jint.Native;
 using Jint.Native.Argument;
@@ -118,6 +119,32 @@ namespace Jint.Runtime.Environments
             return ContainsKey(name);
         }
 
+        internal override bool TryGetBinding(string name, bool strict, out Binding binding)
+        {
+            if (_set && _key == name)
+            {
+                binding = _value;
+                return true;
+            }
+
+            if (name.Length == 9
+                && name == BindingNameArguments
+                && !ReferenceEquals(_argumentsBinding.Value, null))
+            {
+                _argumentsBindingWasAccessed = true;
+                binding = _argumentsBinding;
+                return true;
+            }
+
+            if (_dictionary != null)
+            {
+                return _dictionary.TryGetValue(name, out binding);
+            }
+
+            binding = default;
+            return false;
+        }
+
         public override void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false)
         {
             SetItem(name, new Binding(value, canBeDeleted, mutable: true));
@@ -143,7 +170,17 @@ namespace Jint.Runtime.Environments
         public override JsValue GetBindingValue(string name, bool strict)
         {
             ref var binding = ref GetExistingItem(name);
+            return UnwrapBindingValue(name, strict, binding);
+        }
 
+        internal override JsValue UnwrapBindingValue(string name, bool strict, in Binding binding)
+        {
+            return UnwrapBindingValueInternal(strict, binding);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private JsValue UnwrapBindingValueInternal(bool strict, in Binding binding)
+        {
             if (!binding.Mutable && binding.Value._type == Types.Undefined)
             {
                 if (strict)

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

@@ -22,6 +22,10 @@ namespace Jint.Runtime.Environments
         /// <returns><c>true</c> if it does and <c>false</c> if it does not.</returns>
         public abstract bool HasBinding(string name);
 
+        internal abstract bool TryGetBinding(string name, bool strict, out Binding binding);
+
+        internal abstract JsValue UnwrapBindingValue(string name, bool strict, in Binding binding);
+
         /// <summary>
         /// Creates a new mutable binding in an environment record.
         /// </summary>

+ 40 - 16
Jint/Runtime/Environments/LexicalEnvironment.cs

@@ -6,7 +6,7 @@ using Jint.Runtime.References;
 namespace Jint.Runtime.Environments
 {
     /// <summary>
-    /// Represents a Liexical Environment (a.k.a Scope)
+    /// Represents a Lexical Environment (a.k.a Scope)
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.2
     /// </summary>
@@ -14,7 +14,7 @@ namespace Jint.Runtime.Environments
     {
         private readonly Engine _engine;
         internal readonly EnvironmentRecord _record;
-        private readonly LexicalEnvironment _outer;
+        internal readonly LexicalEnvironment _outer;
 
         public LexicalEnvironment(Engine engine, EnvironmentRecord record, LexicalEnvironment outer)
         {
@@ -23,43 +23,67 @@ namespace Jint.Runtime.Environments
             _outer = outer;
         }
 
-        public EnvironmentRecord Record
-        {
-            [MethodImpl(MethodImplOptions.AggressiveInlining)]
-            get { return _record; }
-        }
+        public EnvironmentRecord Record => _record;
 
         public LexicalEnvironment Outer => _outer;
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static Reference GetIdentifierReference(LexicalEnvironment lex, string name, bool strict)
+        {
+            var identifierEnvironment = TryGetIdentifierEnvironmentWithBindingValue(lex, name, strict, out var temp, out _)
+                ? temp
+                : JsValue.Undefined;
+
+            return lex._engine._referencePool.Rent(identifierEnvironment, name, strict);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static bool TryGetIdentifierEnvironmentWithBindingValue(
+            LexicalEnvironment lex,
+            string name,
+            bool strict,
+            out EnvironmentRecord record,
+            out JsValue value)
         {
             // optimize for common case where result is in one of the nearest scopes
-            if (lex._record.HasBinding(name))
+            if (lex._record.TryGetBinding(name, strict, out var binding))
             {
-                return lex._engine._referencePool.Rent(lex._record, name, strict);
+                record = lex._record;
+                value = lex._record.UnwrapBindingValue(name, strict, binding);
+                return true;
             }
 
             if (lex._outer == null)
             {
-                return new Reference(Undefined.Instance, name, strict);
+                record = default;
+                value = default;
+                return false;
             }
-            
-            return GetIdentifierReferenceLooping(lex._outer, name, strict);
+
+            return TryGetIdentifierReferenceLooping(lex._outer, name, strict, out record, out value);
         }
 
-        private static Reference GetIdentifierReferenceLooping(LexicalEnvironment lex, string name, bool strict)
+        private static bool TryGetIdentifierReferenceLooping(
+            LexicalEnvironment lex,
+            string name,
+            bool strict,
+            out EnvironmentRecord record,
+            out JsValue value)
         {
             while (true)
             {
-                if (lex._record.HasBinding(name))
+                if (lex._record.TryGetBinding(name, strict, out var binding))
                 {
-                    return lex._engine._referencePool.Rent(lex._record, name, strict);
+                    record = lex._record;
+                    value = lex._record.UnwrapBindingValue(name, strict, binding);
+                    return true;
                 }
 
                 if (lex._outer == null)
                 {
-                    return lex._engine._referencePool.Rent(Undefined.Instance, name, strict);
+                    record = default;
+                    value = default;
+                    return false;
                 }
 
                 lex = lex._outer;

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

@@ -26,6 +26,24 @@ namespace Jint.Runtime.Environments
             return _bindingObject.HasProperty(name);
         }
 
+        internal override bool TryGetBinding(string name, bool strict, out Binding binding)
+        {
+            if (!_bindingObject.HasProperty(name))
+            {
+                binding = default;
+                return false;
+            }
+
+            // we unwrap by name
+            binding = new Binding(null, false, false);
+            return true;
+        }
+
+        internal override JsValue UnwrapBindingValue(string name, bool strict, in Binding binding)
+        {
+            return GetBindingValue(name, strict);
+        }
+
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1.2.2
         /// </summary>

+ 15 - 0
Jint/Runtime/ExceptionHelper.cs

@@ -42,6 +42,11 @@ namespace Jint.Runtime
             throw new TypeErrorException(message);
         }
 
+        public static T ThrowReferenceError<T>(Engine engine, string message = null)
+        {
+            throw new JavaScriptException(engine.ReferenceError, message);
+        }
+
         public static T ThrowTypeError<T>(Engine engine, string message = null, Exception exception = null)
         {
             ThrowTypeError(engine, message, exception);
@@ -78,6 +83,16 @@ namespace Jint.Runtime
             throw new NotImplementedException();
         }
 
+        public static T ThrowNotImplementedException<T>()
+        {
+            throw new NotImplementedException();
+        }
+
+        public static T ThrowArgumentOutOfRangeException<T>()
+        {
+            throw new ArgumentOutOfRangeException();
+        }
+
         public static void ThrowArgumentOutOfRangeException(string paramName, string message)
         {
             throw new ArgumentOutOfRangeException(paramName, message);

+ 0 - 1094
Jint/Runtime/ExpressionIntepreter.cs

@@ -1,1094 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Esprima;
-using Esprima.Ast;
-using Jint.Native;
-using Jint.Native.Function;
-using Jint.Native.Number;
-using Jint.Runtime.Descriptors;
-using Jint.Runtime.Descriptors.Specialized;
-using Jint.Runtime.Environments;
-using Jint.Runtime.Interop;
-using Jint.Runtime.References;
-
-namespace Jint.Runtime
-{
-    public sealed 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)
-        {
-            return _engine.EvaluateExpression(expression);
-        }
-
-        public JsValue EvaluateConditionalExpression(ConditionalExpression conditionalExpression)
-        {
-            var lref = _engine.EvaluateExpression(conditionalExpression.Test);
-            if (TypeConverter.ToBoolean(_engine.GetValue(lref, true)))
-            {
-                var trueRef = _engine.EvaluateExpression(conditionalExpression.Consequent);
-                return _engine.GetValue(trueRef, true);
-            }
-            else
-            {
-                var falseRef = _engine.EvaluateExpression(conditionalExpression.Alternate);
-                return _engine.GetValue(falseRef, true);
-            }
-        }
-
-        public JsValue EvaluateAssignmentExpression(AssignmentExpression assignmentExpression)
-        {
-            var lref = _engine.EvaluateExpression((Expression) assignmentExpression.Left) as Reference;
-            JsValue rval = _engine.GetValue(_engine.EvaluateExpression(assignmentExpression.Right), true);
-
-            if (lref == null)
-            {
-                ExceptionHelper.ThrowReferenceError(_engine);
-            }
-
-            if (assignmentExpression.Operator == AssignmentOperator.Assign) // "="
-            {
-                lref.AssertValid(_engine);
-
-                _engine.PutValue(lref, rval);
-                _engine._referencePool.Return(lref);
-                return rval;
-            }
-
-            JsValue lval = _engine.GetValue(lref, false);
-
-            switch (assignmentExpression.Operator)
-            {
-                case AssignmentOperator.PlusAssign:
-                    var lprim = TypeConverter.ToPrimitive(lval);
-                    var rprim = TypeConverter.ToPrimitive(rval);
-                    if (lprim.IsString() || rprim.IsString())
-                    {
-                        if (!(lprim is JsString jsString))
-                        {
-                            jsString = new JsString.ConcatenatedString(TypeConverter.ToString(lprim));
-                        }
-                        lval = jsString.Append(rprim);
-                    }
-                    else
-                    {
-                        lval = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim);
-                    }
-                    break;
-
-                case AssignmentOperator.MinusAssign:
-                    lval = TypeConverter.ToNumber(lval) - TypeConverter.ToNumber(rval);
-                    break;
-
-                case AssignmentOperator.TimesAssign:
-                    if (lval.IsUndefined() || rval.IsUndefined())
-                    {
-                        lval = Undefined.Instance;
-                    }
-                    else
-                    {
-                        lval = TypeConverter.ToNumber(lval) * TypeConverter.ToNumber(rval);
-                    }
-                    break;
-
-                case AssignmentOperator.DivideAssign:
-                    lval = Divide(lval, rval);
-                    break;
-
-                case AssignmentOperator.ModuloAssign:
-                    if (lval.IsUndefined() || rval.IsUndefined())
-                    {
-                        lval = Undefined.Instance;
-                    }
-                    else
-                    {
-                        lval = TypeConverter.ToNumber(lval) % TypeConverter.ToNumber(rval);
-                    }
-                    break;
-
-                case AssignmentOperator.BitwiseAndAssign:
-                    lval = TypeConverter.ToInt32(lval) & TypeConverter.ToInt32(rval);
-                    break;
-
-                case AssignmentOperator.BitwiseOrAssign:
-                    lval = TypeConverter.ToInt32(lval) | TypeConverter.ToInt32(rval);
-                    break;
-
-                case AssignmentOperator.BitwiseXOrAssign:
-                    lval = TypeConverter.ToInt32(lval) ^ TypeConverter.ToInt32(rval);
-                    break;
-
-                case AssignmentOperator.LeftShiftAssign:
-                    lval = TypeConverter.ToInt32(lval) << (int)(TypeConverter.ToUint32(rval) & 0x1F);
-                    break;
-
-                case AssignmentOperator.RightShiftAssign:
-                    lval = TypeConverter.ToInt32(lval) >> (int)(TypeConverter.ToUint32(rval) & 0x1F);
-                    break;
-
-                case AssignmentOperator.UnsignedRightShiftAssign:
-                    lval = (uint)TypeConverter.ToInt32(lval) >> (int)(TypeConverter.ToUint32(rval) & 0x1F);
-                    break;
-
-                default:
-                    ExceptionHelper.ThrowNotImplementedException();
-                    return null;
-            }
-
-            _engine.PutValue(lref, lval);
-
-            _engine._referencePool.Return(lref);
-            return lval;
-        }
-
-        private JsValue Divide(JsValue lval, JsValue rval)
-        {
-            if (lval.IsUndefined() || rval.IsUndefined())
-            {
-                return Undefined.Instance;
-            }
-            else
-            {
-                var lN = TypeConverter.ToNumber(lval);
-                var rN = TypeConverter.ToNumber(rval);
-
-                if (double.IsNaN(rN) || double.IsNaN(lN))
-                {
-                    return double.NaN;
-                }
-
-                if (double.IsInfinity(lN) && double.IsInfinity(rN))
-                {
-                    return double.NaN;
-                }
-
-                if (double.IsInfinity(lN) && rN == 0)
-                {
-                    if (NumberInstance.IsNegativeZero(rN))
-                    {
-                        return -lN;
-                    }
-
-                    return lN;
-                }
-
-                if (lN == 0 && rN == 0)
-                {
-                    return double.NaN;
-                }
-
-                if (rN == 0)
-                {
-                    if (NumberInstance.IsNegativeZero(rN))
-                    {
-                        return lN > 0 ? -double.PositiveInfinity : -double.NegativeInfinity;
-                    }
-
-                    return lN > 0 ? double.PositiveInfinity : double.NegativeInfinity;
-                }
-
-                return lN/rN;
-            }
-        }
-
-        public JsValue EvaluateBinaryExpression(BinaryExpression expression)
-        {
-            JsValue left;
-            if (expression.Left.Type == Nodes.Literal)
-            {
-                left = EvaluateLiteral((Literal) expression.Left);
-            }
-            else
-            {
-                left = _engine.GetValue(_engine.EvaluateExpression(expression.Left), true);
-            }
-
-            JsValue right;
-            if (expression.Right.Type == Nodes.Literal)
-            {
-                right = EvaluateLiteral((Literal) expression.Right);
-            }
-            else
-            {
-                right = _engine.GetValue(_engine.EvaluateExpression(expression.Right), true);
-            }
-
-            JsValue value;
-
-            switch (expression.Operator)
-            {
-                case BinaryOperator.Plus:
-                    var lprim = TypeConverter.ToPrimitive(left);
-                    var rprim = TypeConverter.ToPrimitive(right);
-                    if (lprim.IsString() || rprim.IsString())
-                    {
-                        value = TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim);
-                    }
-                    else
-                    {
-                        value = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim);
-                    }
-                    break;
-
-                case BinaryOperator.Minus:
-                    value = TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right);
-                    break;
-
-                case BinaryOperator.Times:
-                    if (left.IsUndefined() || right.IsUndefined())
-                    {
-                        value = Undefined.Instance;
-                    }
-                    else
-                    {
-                        value = TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right);
-                    }
-                    break;
-
-                case BinaryOperator.Divide:
-                    value = Divide(left, right);
-                    break;
-
-                case BinaryOperator.Modulo:
-                    if (left.IsUndefined() || right.IsUndefined())
-                    {
-                        value = Undefined.Instance;
-                    }
-                    else
-                    {
-                        value = TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right);
-                    }
-                    break;
-
-                case BinaryOperator.Equal:
-                    value = Equal(left, right) ? JsBoolean.True : JsBoolean.False;
-                    break;
-
-                case BinaryOperator.NotEqual:
-                    value = Equal(left, right) ? JsBoolean.False : JsBoolean.True;
-                    break;
-
-                case BinaryOperator.Greater:
-                    value = Compare(right, left, false);
-                    if (value.IsUndefined())
-                    {
-                        value = false;
-                    }
-                    break;
-
-                case BinaryOperator.GreaterOrEqual:
-                    value = Compare(left, right);
-                    if (value.IsUndefined() || ((JsBoolean) value)._value)
-                    {
-                        value = false;
-                    }
-                    else
-                    {
-                        value = true;
-                    }
-                    break;
-
-                case BinaryOperator.Less:
-                    value = Compare(left, right);
-                    if (value.IsUndefined())
-                    {
-                        value = false;
-                    }
-                    break;
-
-                case BinaryOperator.LessOrEqual:
-                    value = Compare(right, left, false);
-                    if (value.IsUndefined() || ((JsBoolean) value)._value)
-                    {
-                        value = false;
-                    }
-                    else
-                    {
-                        value = true;
-                    }
-                    break;
-
-                case BinaryOperator.StrictlyEqual:
-                    return StrictlyEqual(left, right) ? JsBoolean.True : JsBoolean.False;
-
-                case BinaryOperator.StricltyNotEqual:
-                    return StrictlyEqual(left, right)? JsBoolean.False : JsBoolean.True;
-
-                case BinaryOperator.BitwiseAnd:
-                    return TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right);
-
-                case BinaryOperator.BitwiseOr:
-                    return TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right);
-
-                case BinaryOperator.BitwiseXOr:
-                    return TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right);
-
-                case BinaryOperator.LeftShift:
-                    return TypeConverter.ToInt32(left) << (int)(TypeConverter.ToUint32(right) & 0x1F);
-
-                case BinaryOperator.RightShift:
-                    return TypeConverter.ToInt32(left) >> (int)(TypeConverter.ToUint32(right) & 0x1F);
-
-                case BinaryOperator.UnsignedRightShift:
-                    return (uint)TypeConverter.ToInt32(left) >> (int)(TypeConverter.ToUint32(right) & 0x1F);
-
-                case BinaryOperator.Exponentiation:
-                    return Math.Pow(TypeConverter.ToNumber(left), TypeConverter.ToNumber(right));
-
-                case BinaryOperator.InstanceOf:
-                    var f = right.TryCast<FunctionInstance>();
-                    if (ReferenceEquals(f, null))
-                    {
-                        ExceptionHelper.ThrowTypeError(_engine, "instanceof can only be used with a function object");
-                    }
-                    value = f.HasInstance(left);
-                    break;
-
-                case BinaryOperator.In:
-                    if (!right.IsObject())
-                    {
-                        ExceptionHelper.ThrowTypeError(_engine, "in can only be used with an object");
-                    }
-
-                    value = right.AsObject().HasProperty(TypeConverter.ToString(left));
-                    break;
-
-                default:
-                    ExceptionHelper.ThrowNotImplementedException();
-                    return null;
-            }
-
-            return value;
-        }
-
-        public JsValue EvaluateLogicalExpression(BinaryExpression binaryExpression)
-        {
-            var left = _engine.GetValue(_engine.EvaluateExpression(binaryExpression.Left), true);
-
-            switch (binaryExpression.Operator)
-            {
-                case BinaryOperator.LogicalAnd:
-                    if (!TypeConverter.ToBoolean(left))
-                    {
-                        return left;
-                    }
-
-                    return _engine.GetValue(_engine.EvaluateExpression(binaryExpression.Right), true);
-
-                case BinaryOperator.LogicalOr:
-                    if (TypeConverter.ToBoolean(left))
-                    {
-                        return left;
-                    }
-
-                    return _engine.GetValue(_engine.EvaluateExpression(binaryExpression.Right), true);
-
-                default:
-                    ExceptionHelper.ThrowNotImplementedException();
-                    return null;
-            }
-        }
-
-        private static bool Equal(JsValue x, JsValue y)
-        {
-            if (x._type == y._type)
-            {
-				return StrictlyEqual(x, y);
-            }
-
-            if (x._type == Types.Null && y._type == Types.Undefined)
-            {
-                return true;
-            }
-
-            if (x._type == Types.Undefined && y._type == Types.Null)
-            {
-                return true;
-            }
-
-            if (x._type == Types.Number && y._type == Types.String)
-            {
-                return Equal(x, TypeConverter.ToNumber(y));
-            }
-
-            if (x._type == Types.String && y._type == Types.Number)
-            {
-                return Equal(TypeConverter.ToNumber(x), y);
-            }
-
-            if (x._type == Types.Boolean)
-            {
-                return Equal(TypeConverter.ToNumber(x), y);
-            }
-
-            if (y._type == Types.Boolean)
-            {
-                return Equal(x, TypeConverter.ToNumber(y));
-            }
-
-            if (y._type == Types.Object && (x._type == Types.String || x._type == Types.Number))
-            {
-                return Equal(x, TypeConverter.ToPrimitive(y));
-            }
-
-            if (x._type == Types.Object && (y._type == Types.String || y._type == Types.Number))
-            {
-                return Equal(TypeConverter.ToPrimitive(x), y);
-            }
-
-            return false;
-        }
-
-        public static bool StrictlyEqual(JsValue x, JsValue y)
-        {
-            if (x._type != y._type)
-            {
-                return false;
-            }
-
-            if (x._type == Types.Boolean || x._type == Types.String)
-            {
-                return x.Equals(y);
-            }
-
-                        
-            if (x._type >= Types.None && x._type <= Types.Null)
-            {
-                return true;
-            }
-
-            if (x is JsNumber jsNumber)
-            {
-                var nx = jsNumber._value;
-                var ny = ((JsNumber) y)._value;
-                return !double.IsNaN(nx) && !double.IsNaN(ny) && nx == ny;
-            }
-
-            if (x is IObjectWrapper xw)
-            {
-                if (!(y is IObjectWrapper yw))
-                {
-                    return false;
-                }
-
-                return Equals(xw.Target, yw.Target);
-            }
-
-            return x == y;
-        }
-
-        public static bool SameValue(JsValue x, JsValue y)
-        {
-            var typea = TypeConverter.GetPrimitiveType(x);
-            var typeb = TypeConverter.GetPrimitiveType(y);
-
-            if (typea != typeb)
-            {
-                return false;
-            }
-
-            switch (typea)
-            {
-                case Types.None:
-                    return true;
-                case Types.Number:
-                var nx = TypeConverter.ToNumber(x);
-                var ny = TypeConverter.ToNumber(y);
-
-                if (double.IsNaN(nx) && double.IsNaN(ny))
-                {
-                    return true;
-                }
-
-                if (nx == ny)
-                {
-                    if (nx == 0)
-                    {
-                        // +0 !== -0
-                        return NumberInstance.IsNegativeZero(nx) == NumberInstance.IsNegativeZero(ny);
-                    }
-
-                    return true;
-                }
-
-                return false;
-                case Types.String:
-                    return TypeConverter.ToString(x) == TypeConverter.ToString(y);
-                case Types.Boolean:
-                    return TypeConverter.ToBoolean(x) == TypeConverter.ToBoolean(y);
-                default:
-                    return x == y;
-            }
-
-        }
-
-        public static JsValue Compare(JsValue x, JsValue y, bool leftFirst = true)
-        {
-            JsValue px, py;
-            if (leftFirst)
-            {
-                px = TypeConverter.ToPrimitive(x, Types.Number);
-                py = TypeConverter.ToPrimitive(y, Types.Number);
-            }
-            else
-            {
-                py = TypeConverter.ToPrimitive(y, Types.Number);
-                px = TypeConverter.ToPrimitive(x, Types.Number);
-            }
-
-            var typea = px.Type;
-            var typeb = py.Type;
-
-            if (typea != Types.String || typeb != Types.String)
-            {
-                var nx = TypeConverter.ToNumber(px);
-                var ny = TypeConverter.ToNumber(py);
-
-                if (double.IsNaN(nx) || double.IsNaN(ny))
-                {
-                    return Undefined.Instance;
-                }
-
-                if (nx == ny)
-                {
-                    return false;
-                }
-
-                if (double.IsPositiveInfinity(nx))
-                {
-                    return false;
-                }
-
-                if (double.IsPositiveInfinity(ny))
-                {
-                    return true;
-                }
-
-                if (double.IsNegativeInfinity(ny))
-                {
-                    return false;
-                }
-
-                if (double.IsNegativeInfinity(nx))
-                {
-                    return true;
-                }
-
-                return nx < ny;
-            }
-            else
-            {
-                return String.CompareOrdinal(TypeConverter.ToString(x), TypeConverter.ToString(y)) < 0;
-            }
-        }
-
-        public Reference EvaluateIdentifier(Identifier identifier)
-        {
-            var env = _engine.ExecutionContext.LexicalEnvironment;
-            var strict = StrictModeScope.IsStrictModeCode;
-
-            return LexicalEnvironment.GetIdentifierReference(env, identifier.Name, strict);
-        }
-
-        public JsValue EvaluateLiteral(Literal literal)
-        {
-            switch (literal.TokenType)
-            {
-                case TokenType.BooleanLiteral:
-                    // bool is fast enough
-                    return literal.NumericValue > 0.0 ? JsBoolean.True : JsBoolean.False;
-                
-                case TokenType.NullLiteral:
-                    // and so is null
-                    return JsValue.Null;
-
-                case TokenType.NumericLiteral:
-                    return (JsValue) (literal.CachedValue = literal.CachedValue ?? JsNumber.Create(literal.NumericValue));
-                
-                case TokenType.StringLiteral:
-                    return (JsValue) (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);
-
-                default:
-                    // a rare case, above types should cover all
-                    return JsValue.FromObject(_engine, literal.Value);
-            }
-        }
-
-        public JsValue EvaluateObjectExpression(ObjectExpression objectExpression)
-        {
-            // http://www.ecma-international.org/ecma-262/5.1/#sec-11.1.5
-            var propertiesCount = objectExpression.Properties.Count;
-            var obj = _engine.Object.Construct(propertiesCount);
-            for (var i = 0; i < propertiesCount; i++)
-            {
-                var property = objectExpression.Properties[i];
-                var propName = property.Key.GetKey();
-                if (!obj._properties.TryGetValue(propName, out var previous))
-                {
-                    previous = PropertyDescriptor.Undefined;
-                }
-                
-                PropertyDescriptor propDesc;
-
-                if (property.Kind == PropertyKind.Init || property.Kind == PropertyKind.Data)
-                {
-                    var exprValue = _engine.EvaluateExpression(property.Value);
-                    var propValue = _engine.GetValue(exprValue, true);
-                    propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
-                }
-                else if (property.Kind == PropertyKind.Get || property.Kind == PropertyKind.Set)
-                {
-                    var function = property.Value as IFunction;
-
-                    if (function == null)
-                    {
-                        return ExceptionHelper.ThrowSyntaxError<JsValue>(_engine);
-                    }
-
-                    ScriptFunctionInstance functionInstance;
-                    using (new StrictModeScope(function.Strict))
-                    {
-                        functionInstance = new ScriptFunctionInstance(
-                            _engine,
-                            function,
-                            _engine.ExecutionContext.LexicalEnvironment,
-                            StrictModeScope.IsStrictModeCode
-                        );
-                    }
-
-                    propDesc = new GetSetPropertyDescriptor(
-                        get: property.Kind == PropertyKind.Get ? functionInstance : null,
-                        set: property.Kind == PropertyKind.Set ? functionInstance : null,
-                        PropertyFlag.Enumerable | PropertyFlag.Configurable);
-                }
-                else
-                {
-                    ExceptionHelper.ThrowArgumentOutOfRangeException();
-                    return null;
-                }
-
-                if (previous != PropertyDescriptor.Undefined)
-                {
-                    if (StrictModeScope.IsStrictModeCode && previous.IsDataDescriptor() && propDesc.IsDataDescriptor())
-                    {
-                        ExceptionHelper.ThrowSyntaxError(_engine);
-                    }
-
-                    if (previous.IsDataDescriptor() && propDesc.IsAccessorDescriptor())
-                    {
-                        ExceptionHelper.ThrowSyntaxError(_engine);
-                    }
-
-                    if (previous.IsAccessorDescriptor() && propDesc.IsDataDescriptor())
-                    {
-                        ExceptionHelper.ThrowSyntaxError(_engine);
-                    }
-
-                    if (previous.IsAccessorDescriptor() && propDesc.IsAccessorDescriptor())
-                    {
-                        if (!ReferenceEquals(propDesc.Set, null) && !ReferenceEquals(previous.Set, null))
-                        {
-                            ExceptionHelper.ThrowSyntaxError(_engine);
-                        }
-
-                        if (!ReferenceEquals(propDesc.Get, null) && !ReferenceEquals(previous.Get, null))
-                        {
-                            ExceptionHelper.ThrowSyntaxError(_engine);
-                        }
-                    }
-
-                    obj.DefineOwnProperty(propName, propDesc, false);
-                }
-                else
-                {
-                    // do faster direct set
-                    obj._properties[propName] = propDesc;
-                }
-            }
-
-            return obj;
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.1
-        /// </summary>
-        /// <param name="memberExpression"></param>
-        /// <returns></returns>
-        public Reference EvaluateMemberExpression(MemberExpression memberExpression)
-        {
-            var baseReference = _engine.EvaluateExpression(memberExpression.Object);
-            var baseValue = _engine.GetValue(baseReference, false);
-
-            string propertyNameString;
-            if (!memberExpression.Computed) // index accessor ?
-            {
-                // we can take fast path without querying the engine again
-                propertyNameString = ((Identifier) memberExpression.Property).Name;
-            }
-            else
-            {
-                var propertyNameReference = _engine.EvaluateExpression(memberExpression.Property);
-                var propertyNameValue = _engine.GetValue(propertyNameReference, true);
-                propertyNameString = TypeConverter.ToPropertyKey(propertyNameValue);
-            }
-
-            TypeConverter.CheckObjectCoercible(_engine, baseValue, memberExpression, baseReference);
-
-            if (baseReference is Reference r)
-            {
-                _engine._referencePool.Return(r);
-            }
-            return _engine._referencePool.Rent(baseValue, propertyNameString, StrictModeScope.IsStrictModeCode);
-        }
-
-        public JsValue EvaluateFunctionExpression(IFunction functionExpression)
-        {
-            var funcEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
-            var envRec = (DeclarativeEnvironmentRecord) funcEnv._record;
-
-            var closure = new ScriptFunctionInstance(
-                _engine,
-                functionExpression,
-                funcEnv,
-                functionExpression.Strict);
-
-            if (!string.IsNullOrEmpty(functionExpression.Id?.Name))
-            {
-                envRec.CreateMutableBinding(functionExpression.Id.Name, closure);
-            }
-
-            return closure;
-        }
-
-        public JsValue EvaluateCallExpression(CallExpression callExpression)
-        {
-            var callee = _engine.EvaluateExpression(callExpression.Callee);
-            
-            if (_isDebugMode)
-            {
-                _engine.DebugHandler.AddToDebugCallStack(callExpression);
-            }
-
-            // todo: implement as in http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.4
-
-            var arguments = ArrayExt.Empty<JsValue>();
-            if (callExpression.Cached)
-            {
-                arguments = (JsValue[]) callExpression.CachedArguments;
-            }
-            else
-            {
-                var allLiteral = true;
-                if (callExpression.Arguments.Count > 0)
-                {
-                    arguments = _engine._jsValueArrayPool.RentArray(callExpression.Arguments.Count);
-                    BuildArguments(callExpression.Arguments, arguments, out allLiteral);
-                }
-
-                if (callExpression.CanBeCached)
-                {
-                    // The arguments array can be cached if they are all literals
-                    if (allLiteral)
-                    {
-                        callExpression.CachedArguments = arguments;
-                        callExpression.Cached = true;
-                    }
-                    else
-                    {
-                        callExpression.CanBeCached = false;
-                    }
-                }
-            }
-
-            var func = _engine.GetValue(callee, false);
-
-            var r = callee as Reference;
-            if (_maxRecursionDepth >= 0)
-            {
-                var stackItem = new CallStackElement(callExpression, func, r?._name ?? "anonymous function");
-
-                var recursionDepth = _engine.CallStack.Push(stackItem);
-
-                if (recursionDepth > _maxRecursionDepth)
-                {
-                    _engine.CallStack.Pop();
-                    ExceptionHelper.ThrowRecursionDepthOverflowException(_engine.CallStack, stackItem.ToString());
-                }
-            }
-
-            if (func._type == Types.Undefined)
-            {
-                ExceptionHelper.ThrowTypeError(_engine, r == null ? "" : $"Object has no method '{r.GetReferencedName()}'");
-            }
-
-            if (func._type != Types.Object)
-            {
-                if (_referenceResolver == null || !_referenceResolver.TryGetCallable(_engine, callee, out func))
-                {
-                    ExceptionHelper.ThrowTypeError(_engine,
-                        r == null ? "" : $"Property '{r.GetReferencedName()}' of object is not a function");
-                }
-            }
-
-            if (!(func is ICallable callable))
-            {
-                return ExceptionHelper.ThrowTypeError<JsValue>(_engine);
-            }
-
-            var thisObject = Undefined.Instance;
-            if (r != null)
-            {
-                if (r.IsPropertyReference())
-                {
-                    thisObject = r._baseValue;
-                }
-                else
-                {
-                    var env = (EnvironmentRecord) r._baseValue;
-                    thisObject = env.ImplicitThisValue();
-                }
-                
-                // is it a direct call to eval ? http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.1.1
-                if (r._name == "eval" && callable is EvalFunctionInstance instance)
-                {
-                    var value = instance.Call(thisObject, arguments, true);
-                    _engine._referencePool.Return(r);
-                    return value;
-                }
-            }
-
-            var result = callable.Call(thisObject, arguments);
-
-            if (_isDebugMode)
-            {
-                _engine.DebugHandler.PopDebugCallStack();
-            }
-
-            if (_maxRecursionDepth >= 0)
-            {
-                _engine.CallStack.Pop();
-            }
-
-            if (!callExpression.Cached && arguments.Length > 0)
-            {
-                _engine._jsValueArrayPool.ReturnArray(arguments);
-            }
-
-            _engine._referencePool.Return(r);
-            return result;
-        }
-
-        public JsValue EvaluateSequenceExpression(SequenceExpression sequenceExpression)
-        {
-            var result = Undefined.Instance;
-            var expressionsCount = sequenceExpression.Expressions.Count;
-            for (var i = 0; i < expressionsCount; i++)
-            {
-                var expression = sequenceExpression.Expressions[i];
-                result = _engine.GetValue(_engine.EvaluateExpression(expression), true);
-            }
-
-            return result;
-        }
-
-        public JsValue EvaluateUpdateExpression(UpdateExpression updateExpression)
-        {
-            var value = _engine.EvaluateExpression(updateExpression.Argument);
-
-            var r = (Reference) value;
-            r.AssertValid(_engine);
-
-            var oldValue = TypeConverter.ToNumber(_engine.GetValue(value, false));
-            double newValue = 0;
-            if (updateExpression.Operator == UnaryOperator.Increment)
-            {
-                newValue = oldValue + 1;
-            }
-            else if (updateExpression.Operator == UnaryOperator.Decrement)
-            {
-                newValue = oldValue - 1;
-            }
-            else
-            {
-                ExceptionHelper.ThrowArgumentException();
-            }
-
-            _engine.PutValue(r, newValue);
-            _engine._referencePool.Return(r);
-            return updateExpression.Prefix ? newValue : oldValue;
-        }
-
-        public JsValue EvaluateThisExpression(ThisExpression thisExpression)
-        {
-            return _engine.ExecutionContext.ThisBinding;
-        }
-
-        public JsValue EvaluateNewExpression(NewExpression newExpression)
-        {
-            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(_engine.EvaluateExpression(newExpression.Callee), true).TryCast<IConstructor>();
-
-            if (callee == null)
-            {
-                ExceptionHelper.ThrowTypeError(_engine, "The object can't be used as constructor.");
-            }
-
-            // construct the new instance using the Function's constructor method
-            var instance = callee.Construct(arguments);
-
-            _engine._jsValueArrayPool.ReturnArray(arguments);
-
-            return instance;
-        }
-
-        public JsValue EvaluateArrayExpression(ArrayExpression arrayExpression)
-        {
-            var elements = arrayExpression.Elements;
-            var count = elements.Count;
-            
-            var a = _engine.Array.ConstructFast((uint) count);
-            for (var n = 0; n < count; n++)
-            {
-                var expr = elements[n];
-                if (expr != null)
-                {
-                    var value = _engine.GetValue(_engine.EvaluateExpression((Expression) expr), true);
-                    a.SetIndexValue((uint) n, value, updateLength: false);
-                }
-            }
-            return a;
-        }
-
-        public JsValue EvaluateUnaryExpression(UnaryExpression unaryExpression)
-        {
-            var value = _engine.EvaluateExpression(unaryExpression.Argument);
-
-            switch (unaryExpression.Operator)
-            {
-                case UnaryOperator.Plus:
-                    return TypeConverter.ToNumber(_engine.GetValue(value, true));
-
-                case UnaryOperator.Minus:
-                    var n = TypeConverter.ToNumber(_engine.GetValue(value, true));
-                    return double.IsNaN(n) ? double.NaN : n*-1;
-
-                case UnaryOperator.BitwiseNot:
-                    return ~TypeConverter.ToInt32(_engine.GetValue(value, true));
-
-                case UnaryOperator.LogicalNot:
-                    return !TypeConverter.ToBoolean(_engine.GetValue(value, true));
-
-                case UnaryOperator.Delete:
-                    var r = value as Reference;
-                    if (r == null)
-                    {
-                        return true;
-                    }
-                    if (r.IsUnresolvableReference())
-                    {
-                        if (r._strict)
-                        {
-                            ExceptionHelper.ThrowSyntaxError(_engine);
-                        }
-
-                        _engine._referencePool.Return(r);
-                        return true;
-                    }
-                    if (r.IsPropertyReference())
-                    {
-                        var o = TypeConverter.ToObject(_engine, r.GetBase());
-                        var jsValue = o.Delete(r._name, r._strict);
-                        _engine._referencePool.Return(r);
-                        return jsValue;
-                    }
-                    if (r._strict)
-                    {
-                        ExceptionHelper.ThrowSyntaxError(_engine);
-                    }
-
-                    var bindings = r.GetBase().TryCast<EnvironmentRecord>();
-                    var referencedName = r.GetReferencedName();
-                    _engine._referencePool.Return(r);
-
-                    return bindings.DeleteBinding(referencedName);
-
-                case UnaryOperator.Void:
-                    _engine.GetValue(value, true);
-                    return Undefined.Instance;
-
-                case UnaryOperator.TypeOf:
-                    r = value as Reference;
-                    if (r != null)
-                    {
-                        if (r.IsUnresolvableReference())
-                        {
-                            _engine._referencePool.Return(r);
-                            return "undefined";
-                        }
-                    }
-
-                    var v = _engine.GetValue(value, true);
-
-                    if (v.IsUndefined())
-                    {
-                        return "undefined";
-                    }
-                    if (v.IsNull())
-                    {
-                        return "object";
-                    }
-
-                    switch (v._type)
-                    {
-                        case Types.Boolean: return "boolean";
-                        case Types.Number: return "number";
-                        case Types.String: return "string";
-                    }
-                    if (v.TryCast<ICallable>() != null)
-                    {
-                        return "function";
-                    }
-                    return "object";
-
-                default:
-                    ExceptionHelper.ThrowArgumentException();
-                    return null;
-            }
-        }
-
-        private void BuildArguments(
-            List<ArgumentListElement> expressionArguments, 
-            JsValue[] targetArray,
-            out bool cacheable)
-        {
-            cacheable = true;
-            for (var i = 0; i < (uint) targetArray.Length; i++)
-            {
-                var argument = (Expression) expressionArguments[i];
-                targetArray[i] = _engine.GetValue(_engine.EvaluateExpression(argument), true);
-                cacheable &= argument is Literal;
-            }
-        }
-    }
-}

+ 46 - 0
Jint/Runtime/Interpreter/Expressions/JintArrayExpression.cs

@@ -0,0 +1,46 @@
+using Esprima.Ast;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintArrayExpression : JintExpression
+    {
+        private JintExpression[] _expressions;
+
+        public JintArrayExpression(Engine engine, ArrayExpression expression) : base(engine, expression)
+        {
+            _initialized = false;
+        }
+
+        protected override void Initialize()
+        {
+            var node = (ArrayExpression) _expression;
+            _expressions = new JintExpression[node.Elements.Count];
+            for (var n = 0; n < _expressions.Length; n++)
+            {
+                var expr = node.Elements[n];
+                if (expr != null)
+                {
+                    var expression = Build(_engine, (Expression) expr);
+                    _expressions[n] = expression;
+                }
+            }
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var a = _engine.Array.ConstructFast((uint) _expressions.Length);
+            var expressions = _expressions;
+            for (uint i = 0; i < (uint) expressions.Length; i++)
+            {
+                var expr = expressions[i];
+                if (expr != null)
+                {
+                    var value = expr.GetValue();
+                    a.SetIndexValue(i, value, updateLength: false);
+                }
+            }
+
+            return a;
+        }
+    }
+}

+ 190 - 0
Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs

@@ -0,0 +1,190 @@
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Runtime.Environments;
+using Jint.Runtime.References;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintAssignmentExpression : JintExpression
+    {
+        private readonly JintExpression _left;
+        private readonly JintExpression _right;
+
+        private JintAssignmentExpression(Engine engine, AssignmentExpression expression) : base(engine, expression)
+        {
+            _left = Build(engine, (Expression) expression.Left);
+            _right = Build(engine, expression.Right);
+        }
+
+        internal static JintExpression Build(Engine engine, AssignmentExpression expression)
+        {
+            if (expression.Operator == AssignmentOperator.Assign)
+            {
+                return new Assignment(engine, expression);
+            }
+
+            return new JintAssignmentExpression(engine, expression);
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var lref = _left.Evaluate() as Reference ?? ExceptionHelper.ThrowReferenceError<Reference>(_engine);
+            JsValue rval = _right.GetValue();
+
+            JsValue lval = _engine.GetValue(lref, false);
+
+            var expression = (AssignmentExpression) _expression;
+            switch (expression.Operator)
+            {
+                case AssignmentOperator.PlusAssign:
+                    var lprim = TypeConverter.ToPrimitive(lval);
+                    var rprim = TypeConverter.ToPrimitive(rval);
+                    if (lprim.IsString() || rprim.IsString())
+                    {
+                        if (!(lprim is JsString jsString))
+                        {
+                            jsString = new JsString.ConcatenatedString(TypeConverter.ToString(lprim));
+                        }
+
+                        lval = jsString.Append(rprim);
+                    }
+                    else
+                    {
+                        lval = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim);
+                    }
+
+                    break;
+
+                case AssignmentOperator.MinusAssign:
+                    lval = TypeConverter.ToNumber(lval) - TypeConverter.ToNumber(rval);
+                    break;
+
+                case AssignmentOperator.TimesAssign:
+                    if (lval.IsUndefined() || rval.IsUndefined())
+                    {
+                        lval = Undefined.Instance;
+                    }
+                    else
+                    {
+                        lval = TypeConverter.ToNumber(lval) * TypeConverter.ToNumber(rval);
+                    }
+
+                    break;
+
+                case AssignmentOperator.DivideAssign:
+                    lval = Divide(lval, rval);
+                    break;
+
+                case AssignmentOperator.ModuloAssign:
+                    if (lval.IsUndefined() || rval.IsUndefined())
+                    {
+                        lval = Undefined.Instance;
+                    }
+                    else
+                    {
+                        lval = TypeConverter.ToNumber(lval) % TypeConverter.ToNumber(rval);
+                    }
+
+                    break;
+
+                case AssignmentOperator.BitwiseAndAssign:
+                    lval = TypeConverter.ToInt32(lval) & TypeConverter.ToInt32(rval);
+                    break;
+
+                case AssignmentOperator.BitwiseOrAssign:
+                    lval = TypeConverter.ToInt32(lval) | TypeConverter.ToInt32(rval);
+                    break;
+
+                case AssignmentOperator.BitwiseXOrAssign:
+                    lval = TypeConverter.ToInt32(lval) ^ TypeConverter.ToInt32(rval);
+                    break;
+
+                case AssignmentOperator.LeftShiftAssign:
+                    lval = TypeConverter.ToInt32(lval) << (int) (TypeConverter.ToUint32(rval) & 0x1F);
+                    break;
+
+                case AssignmentOperator.RightShiftAssign:
+                    lval = TypeConverter.ToInt32(lval) >> (int) (TypeConverter.ToUint32(rval) & 0x1F);
+                    break;
+
+                case AssignmentOperator.UnsignedRightShiftAssign:
+                    lval = (uint) TypeConverter.ToInt32(lval) >> (int) (TypeConverter.ToUint32(rval) & 0x1F);
+                    break;
+
+                default:
+                    ExceptionHelper.ThrowNotImplementedException();
+                    return null;
+            }
+
+            _engine.PutValue(lref, lval);
+
+            _engine._referencePool.Return(lref);
+            return lval;
+        }
+
+        private class Assignment : JintExpression
+        {
+            private readonly JintExpression _left;
+            private readonly JintExpression _right;
+
+            private readonly JintIdentifierExpression _leftIdentifier;
+            private readonly bool _evalOrArguments;
+
+            public Assignment(Engine engine, AssignmentExpression expression) : base(engine, expression)
+            {
+                _left = Build(engine, (Expression) expression.Left);
+                _right = Build(engine, expression.Right);
+
+                _leftIdentifier = _left as JintIdentifierExpression;
+                _evalOrArguments = _leftIdentifier?._expressionName == "eval" || _leftIdentifier?._expressionName == "arguments";
+            }
+
+            protected override object EvaluateInternal()
+            {
+                JsValue rval = null;
+                if (_leftIdentifier != null)
+                {
+                    rval = AssignToIdentifier();
+                }
+
+                return rval ?? SetValue();
+            }
+
+            private JsValue SetValue()
+            {
+                // slower version
+                var lref = _left.Evaluate() as Reference ?? ExceptionHelper.ThrowReferenceError<Reference>(_engine);
+                lref.AssertValid(_engine);
+
+                var rval = _right.GetValue();
+                _engine.PutValue(lref, rval);
+                _engine._referencePool.Return(lref);
+                return rval;
+            }
+
+            private JsValue AssignToIdentifier()
+            {
+                var env = _engine.ExecutionContext.LexicalEnvironment;
+                var strict = StrictModeScope.IsStrictModeCode;
+                if (LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
+                    env,
+                    _leftIdentifier._expressionName,
+                    strict,
+                    out var environmentRecord,
+                    out _))
+                {
+                    if (strict && _evalOrArguments)
+                    {
+                        ExceptionHelper.ThrowSyntaxError(_engine);
+                    }
+
+                    var rval = _right.GetValue();
+                    environmentRecord.SetMutableBinding(_leftIdentifier._expressionName, rval, strict);
+                    return rval;
+                }
+
+                return null;
+            }
+        }
+    }
+}

+ 316 - 0
Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs

@@ -0,0 +1,316 @@
+using System;
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Native.Function;
+using Jint.Native.Object;
+using Jint.Runtime.Interop;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal abstract class JintBinaryExpression : JintExpression
+    {
+        private readonly JintExpression _left;
+        private readonly JintExpression _right;
+        private readonly BinaryOperator _operatorType;
+
+        private JintBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
+        {
+            _left = Build(engine, expression.Left);
+            _right = Build(engine, expression.Right);
+            _operatorType = expression.Operator;
+        }
+
+        internal static JintExpression Build(Engine engine, BinaryExpression expression)
+        {
+            JintExpression result;
+            switch (expression.Operator)
+            {
+                case BinaryOperator.StrictlyEqual:
+                    result = new StrictlyEqualBinaryExpression(engine, expression);
+                    break;
+                case BinaryOperator.StricltyNotEqual:
+                    result = new StrictlyNotEqualBinaryExpression(engine, expression);
+                    break;
+                case BinaryOperator.Less:
+                    result = new LessBinaryExpression(engine, expression);
+                    break;
+                case BinaryOperator.Greater:
+                    result = new GreaterBinaryExpression(engine, expression);
+                    break;
+                default:
+                    result = new JintGenericBinaryExpression(engine, expression);
+                    break;
+            }
+
+            if (expression.Left.Type == Nodes.Literal
+                && expression.Right.Type == Nodes.Literal
+                && expression.Operator != BinaryOperator.InstanceOf
+                && expression.Operator != BinaryOperator.In)
+            {
+                // calculate eagerly
+                // TODO result = new JintConstantExpression(engine, (JsValue) result.Evaluate());
+            }
+
+            return result;
+        }
+
+        public static bool StrictlyEqual(JsValue x, JsValue y)
+        {
+            if (x._type != y._type)
+            {
+                return false;
+            }
+
+            if (x._type == Types.Boolean || x._type == Types.String)
+            {
+                return x.Equals(y);
+            }
+
+
+            if (x._type >= Types.None && x._type <= Types.Null)
+            {
+                return true;
+            }
+
+            if (x is JsNumber jsNumber)
+            {
+                var nx = jsNumber._value;
+                var ny = ((JsNumber) y)._value;
+                return !double.IsNaN(nx) && !double.IsNaN(ny) && nx == ny;
+            }
+
+            if (x is IObjectWrapper xw)
+            {
+                if (!(y is IObjectWrapper yw))
+                {
+                    return false;
+                }
+
+                return Equals(xw.Target, yw.Target);
+            }
+
+            return x == y;
+        }
+
+        private sealed class JintGenericBinaryExpression : JintBinaryExpression
+        {
+            private readonly Func<JsValue, JsValue, JsValue> _operator;
+
+            public JintGenericBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
+            {
+                switch (_operatorType)
+                {
+                    case BinaryOperator.Plus:
+                        _operator = (left, right) =>
+                        {
+                            var lprim = TypeConverter.ToPrimitive(left);
+                            var rprim = TypeConverter.ToPrimitive(right);
+                            return lprim.IsString() || rprim.IsString()
+                                ? (JsValue) JsString.Create(TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim))
+                                : JsNumber.Create(TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim));
+                        };
+                        break;
+
+                    case BinaryOperator.Minus:
+                        _operator = (left, right) => JsNumber.Create(TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right));
+                        break;
+
+                    case BinaryOperator.Times:
+                        _operator = (left, right) => left.IsUndefined() || right.IsUndefined()
+                            ? Undefined.Instance
+                            : JsNumber.Create(TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right));
+                        break;
+
+                    case BinaryOperator.Divide:
+                        _operator = Divide;
+                        break;
+
+                    case BinaryOperator.Modulo:
+                        _operator = (left, right) => left.IsUndefined() || right.IsUndefined()
+                            ? Undefined.Instance
+                            : TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right);
+
+                        break;
+
+                    case BinaryOperator.Equal:
+                        _operator = (left, right) => Equal(left, right)
+                            ? JsBoolean.True
+                            : JsBoolean.False;
+                        break;
+
+                    case BinaryOperator.NotEqual:
+                        _operator = (left, right) => Equal(left, right) ? JsBoolean.False : JsBoolean.True;
+                        break;
+
+                    case BinaryOperator.GreaterOrEqual:
+                        _operator = (left, right) =>
+                        {
+                            var value = Compare(left, right);
+                            if (value.IsUndefined() || ((JsBoolean) value)._value)
+                            {
+                                value = JsBoolean.False;
+                            }
+                            else
+                            {
+                                value = JsBoolean.True;
+                            }
+
+                            return value;
+                        };
+
+                        break;
+
+                    case BinaryOperator.LessOrEqual:
+                        _operator = (left, right) =>
+                        {
+                            var value = Compare(right, left, false);
+                            if (value.IsUndefined() || ((JsBoolean) value)._value)
+                            {
+                                value = JsBoolean.False;
+                            }
+                            else
+                            {
+                                value = JsBoolean.True;
+                            }
+
+                            return value;
+                        };
+
+                        break;
+
+                    case BinaryOperator.BitwiseAnd:
+                        _operator = (left, right) => JsNumber.Create(TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right));
+                        break;
+
+                    case BinaryOperator.BitwiseOr:
+                        _operator = (left, right) => JsNumber.Create(TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right));
+                        break;
+
+                    case BinaryOperator.BitwiseXOr:
+                        _operator = (left, right) => JsNumber.Create(TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right));
+                        break;
+
+                    case BinaryOperator.LeftShift:
+                        _operator = (left, right) => JsNumber.Create(TypeConverter.ToInt32(left) << (int) (TypeConverter.ToUint32(right) & 0x1F));
+                        break;
+
+                    case BinaryOperator.RightShift:
+                        _operator = (left, right) => JsNumber.Create(TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
+                        break;
+
+                    case BinaryOperator.UnsignedRightShift:
+                        _operator = (left, right) => JsNumber.Create((uint) TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
+                        break;
+
+                    case BinaryOperator.Exponentiation:
+                        _operator = (left, right) => JsNumber.Create(Math.Pow(TypeConverter.ToNumber(left), TypeConverter.ToNumber(right)));
+                        break;
+
+                    case BinaryOperator.InstanceOf:
+                        _operator = (left, right) =>
+                        {
+                            if (!(right is FunctionInstance f))
+                            {
+                                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "instanceof can only be used with a function object");
+                            }
+
+                            return f.HasInstance(left) ? JsBoolean.True : JsBoolean.False;
+                        };
+                        break;
+
+                    case BinaryOperator.In:
+                        _operator = (left, right) =>
+                        {
+                            if (!(right is ObjectInstance oi))
+                            {
+                                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "in can only be used with an object");
+                            }
+
+                            return oi.HasProperty(TypeConverter.ToString(left)) ? JsBoolean.True : JsBoolean.False;
+                        };
+
+                        break;
+
+                    default:
+                        _operator = ExceptionHelper.ThrowNotImplementedException<Func<JsValue, JsValue, JsValue>>();
+                        break;
+                }
+            }
+
+            protected override object EvaluateInternal()
+            {
+                var left = _left.GetValue();
+                var right = _right.GetValue();
+                return _operator(left, right);
+            }
+        }
+
+        private sealed class StrictlyEqualBinaryExpression : JintBinaryExpression
+        {
+            public StrictlyEqualBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
+            {
+            }
+
+            protected override object EvaluateInternal()
+            {
+                var left = _left.GetValue();
+                var right = _right.GetValue();
+                return StrictlyEqual(left, right) ? JsBoolean.True : JsBoolean.False;
+            }
+        }
+
+        private sealed class StrictlyNotEqualBinaryExpression : JintBinaryExpression
+        {
+            public StrictlyNotEqualBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
+            {
+            }
+
+            protected override object EvaluateInternal()
+            {
+                var left = _left.GetValue();
+                var right = _right.GetValue();
+                return StrictlyEqual(left, right) ? JsBoolean.False : JsBoolean.True;
+            }
+        }
+
+        private sealed class LessBinaryExpression : JintBinaryExpression
+        {
+            public LessBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
+            {
+            }
+
+            protected override object EvaluateInternal()
+            {
+                var left = _left.GetValue();
+                var right = _right.GetValue();
+                var value = Compare(left, right);
+                if (value._type == Types.Undefined)
+                {
+                    value = JsBoolean.False;
+                }
+
+                return value;
+            }
+        }
+
+        private sealed class GreaterBinaryExpression : JintBinaryExpression
+        {
+            public GreaterBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
+            {
+            }
+
+            protected override object EvaluateInternal()
+            {
+                var left = _left.GetValue();
+                var right = _right.GetValue();
+                var value = Compare(right, left, false);
+                if (value._type == Types.Undefined)
+                {
+                    value = JsBoolean.False;
+                }
+
+                return value;
+            }
+        }
+    }
+}

+ 168 - 0
Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs

@@ -0,0 +1,168 @@
+using System;
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Native.Function;
+using Jint.Runtime.Environments;
+using Jint.Runtime.References;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintCallExpression : JintExpression
+    {
+        private readonly bool _isDebugMode;
+        private readonly int _maxRecursionDepth;
+
+        private readonly JintExpression _calleeExpression;
+
+        public JintCallExpression(Engine engine, CallExpression expression) : base(engine, expression)
+        {
+            _initialized = false;
+            _isDebugMode = engine.Options.IsDebugMode;
+            _maxRecursionDepth = engine.Options.MaxRecursionDepth;
+            _calleeExpression = Build(engine, expression.Callee);
+        }
+
+        protected override void Initialize()
+        {
+            var expression = (CallExpression) _expression;
+            var cachedArgumentsHolder = new CachedArgumentsHolder
+            {
+                JintArguments = new JintExpression[expression.Arguments.Count]
+            };
+
+            bool cacheable = true;
+            for (var i = 0; i < expression.Arguments.Count; i++)
+            {
+                var expressionArgument = (Expression) expression.Arguments[i];
+                cachedArgumentsHolder.JintArguments[i] = Build(_engine, expressionArgument);
+                cacheable &= (expressionArgument is Literal || expressionArgument is FunctionExpression);
+            }
+
+            if (cacheable)
+            {
+                expression.Cached = true;
+                var arguments = ArrayExt.Empty<JsValue>();
+                if (cachedArgumentsHolder.JintArguments.Length > 0)
+                {
+                    arguments = _engine._jsValueArrayPool.RentArray(cachedArgumentsHolder.JintArguments.Length);
+                    BuildArguments(cachedArgumentsHolder.JintArguments, arguments);
+                }
+
+                cachedArgumentsHolder.CachedArguments = arguments;
+            }
+
+            expression.CachedArguments = cachedArgumentsHolder;
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var callee = _calleeExpression.Evaluate();
+            var expression = (CallExpression) _expression;
+
+            if (_isDebugMode)
+            {
+                _engine.DebugHandler.AddToDebugCallStack(expression);
+            }
+
+            // todo: implement as in http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.4
+
+            var cachedArguments = (CachedArgumentsHolder) expression.CachedArguments;
+            var arguments = ArrayExt.Empty<JsValue>();
+            if (expression.Cached)
+            {
+                arguments = cachedArguments.CachedArguments;
+            }
+            else
+            {
+                if (cachedArguments.JintArguments.Length > 0)
+                {
+                    arguments = _engine._jsValueArrayPool.RentArray(cachedArguments.JintArguments.Length);
+                    BuildArguments(cachedArguments.JintArguments, arguments);
+                }
+            }
+
+            var func = _engine.GetValue(callee, false);
+
+            var r = callee as Reference;
+            if (_maxRecursionDepth >= 0)
+            {
+                var stackItem = new CallStackElement(expression, func, r?._name ?? "anonymous function");
+
+                var recursionDepth = _engine.CallStack.Push(stackItem);
+
+                if (recursionDepth > _maxRecursionDepth)
+                {
+                    _engine.CallStack.Pop();
+                    ExceptionHelper.ThrowRecursionDepthOverflowException(_engine.CallStack, stackItem.ToString());
+                }
+            }
+
+            if (func._type == Types.Undefined)
+            {
+                ExceptionHelper.ThrowTypeError(_engine, r == null ? "" : $"Object has no method '{r.GetReferencedName()}'");
+            }
+
+            if (func._type != Types.Object)
+            {
+                if (_engine._referenceResolver == null || !_engine._referenceResolver.TryGetCallable(_engine, callee, out func))
+                {
+                    ExceptionHelper.ThrowTypeError(_engine,
+                        r == null ? "" : $"Property '{r.GetReferencedName()}' of object is not a function");
+                }
+            }
+
+            if (!(func is ICallable callable))
+            {
+                return ExceptionHelper.ThrowTypeError<object>(_engine);
+            }
+
+            var thisObject = Undefined.Instance;
+            if (r != null)
+            {
+                if (r.IsPropertyReference())
+                {
+                    thisObject = r._baseValue;
+                }
+                else
+                {
+                    var env = (EnvironmentRecord) r._baseValue;
+                    thisObject = env.ImplicitThisValue();
+                }
+
+                // is it a direct call to eval ? http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.1.1
+                if (r._name == "eval" && callable is EvalFunctionInstance instance)
+                {
+                    var value = instance.Call(thisObject, arguments, true);
+                    _engine._referencePool.Return(r);
+                    return value;
+                }
+            }
+
+            var result = callable.Call(thisObject, arguments);
+
+            if (_isDebugMode)
+            {
+                _engine.DebugHandler.PopDebugCallStack();
+            }
+
+            if (_maxRecursionDepth >= 0)
+            {
+                _engine.CallStack.Pop();
+            }
+
+            if (!expression.Cached && arguments.Length > 0)
+            {
+                _engine._jsValueArrayPool.ReturnArray(arguments);
+            }
+
+            _engine._referencePool.Return(r);
+            return result;
+        }
+
+        internal class CachedArgumentsHolder
+        {
+            internal JintExpression[] JintArguments;
+            internal JsValue[] CachedArguments;
+        }
+    }
+}

+ 25 - 0
Jint/Runtime/Interpreter/Expressions/JintConditionalExpression.cs

@@ -0,0 +1,25 @@
+using Esprima.Ast;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintConditionalExpression : JintExpression
+    {
+        private readonly JintExpression _test;
+        private readonly JintExpression _consequent;
+        private readonly JintExpression _alternate;
+
+        public JintConditionalExpression(Engine engine, ConditionalExpression expression) : base(engine, expression)
+        {
+            _test = Build(engine, expression.Test);
+            _consequent = Build(engine, expression.Consequent);
+            _alternate = Build(engine, expression.Alternate);
+        }
+
+        protected override object EvaluateInternal()
+        {
+            return TypeConverter.ToBoolean(_test.GetValue())
+                ? _consequent.GetValue()
+                : _alternate.GetValue();
+        }
+    }
+}

+ 331 - 0
Jint/Runtime/Interpreter/Expressions/JintExpression.cs

@@ -0,0 +1,331 @@
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Native.Number;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal abstract class JintExpression
+    {
+        // require sub-classes to set to false explicitly to skip virtual call
+        protected bool _initialized = true;
+
+        protected internal readonly Engine _engine;
+        protected internal readonly INode _expression;
+
+        protected JintExpression(Engine engine, INode expression)
+        {
+            _engine = engine;
+            _expression = expression;
+        }
+
+        /// <summary>
+        /// Resolves the underlying value for this expression.
+        /// By default uses the Engine for resolving.
+        /// </summary>
+        /// <seealso cref="JintLiteralExpression"/>
+        public virtual JsValue GetValue()
+        {
+            return _engine.GetValue(Evaluate(), true);
+        }
+
+        public object Evaluate()
+        {
+            _engine._lastSyntaxNode = _expression;
+            if (!_initialized)
+            {
+                Initialize();
+                _initialized = true;
+            }
+            return EvaluateInternal();
+        }
+
+        /// <summary>
+        /// Opportunity to build one-time structures and caching based on lexical context.
+        /// </summary>
+        protected virtual void Initialize()
+        {
+        }
+
+        protected abstract object EvaluateInternal();
+
+        protected internal static JintExpression Build(Engine engine, Expression expression)
+        {
+            switch (expression.Type)
+            {
+                case Nodes.AssignmentExpression:
+                    return JintAssignmentExpression.Build(engine, (AssignmentExpression) expression);
+
+                case Nodes.ArrayExpression:
+                    return new JintArrayExpression(engine, (ArrayExpression) expression);
+
+                case Nodes.BinaryExpression:
+                    return JintBinaryExpression.Build(engine, (BinaryExpression) expression);
+
+                case Nodes.CallExpression:
+                    return new JintCallExpression(engine, (CallExpression) expression);
+
+                case Nodes.ConditionalExpression:
+                    return new JintConditionalExpression(engine, (ConditionalExpression) expression);
+
+                case Nodes.FunctionExpression:
+                    return new JintFunctionExpression(engine, (IFunction) expression);
+
+                case Nodes.Identifier:
+                    return new JintIdentifierExpression(engine, (Identifier) expression);
+
+                case Nodes.Literal:
+                    return new JintLiteralExpression(engine, (Literal) expression);
+
+                case Nodes.LogicalExpression:
+                    var binaryExpression = (BinaryExpression) expression;
+                    switch (binaryExpression.Operator)
+                    {
+                        case BinaryOperator.LogicalAnd:
+                            return new JintLogicalAndExpression(engine, binaryExpression);
+                        case BinaryOperator.LogicalOr:
+                            return new JintLogicalOrExpression(engine, binaryExpression);
+                        default:
+                            return ExceptionHelper.ThrowArgumentOutOfRangeException<JintExpression>();
+                    }
+
+                case Nodes.MemberExpression:
+                    return new JintMemberExpression(engine, (MemberExpression) expression);
+
+                case Nodes.NewExpression:
+                    return new JintNewExpression(engine, (NewExpression) expression);
+
+                case Nodes.ObjectExpression:
+                    return new JintObjectExpression(engine, (ObjectExpression) expression);
+
+                case Nodes.SequenceExpression:
+                    return new JintSequenceExpression(engine, (SequenceExpression) expression);
+
+                case Nodes.ThisExpression:
+                    return new JintThisExpression(engine, (ThisExpression) expression);
+
+                case Nodes.UpdateExpression:
+                    return new JintUpdateExpression(engine, (UpdateExpression) expression);
+
+                case Nodes.UnaryExpression:
+                    return new JintUnaryExpression(engine, (UnaryExpression) expression);
+
+                default:
+                    ExceptionHelper.ThrowArgumentOutOfRangeException();
+                    return null;
+            }
+        }
+
+        protected JsValue Divide(JsValue lval, JsValue rval)
+        {
+            if (lval.IsUndefined() || rval.IsUndefined())
+            {
+                return Undefined.Instance;
+            }
+            else
+            {
+                var lN = TypeConverter.ToNumber(lval);
+                var rN = TypeConverter.ToNumber(rval);
+
+                if (double.IsNaN(rN) || double.IsNaN(lN))
+                {
+                    return double.NaN;
+                }
+
+                if (double.IsInfinity(lN) && double.IsInfinity(rN))
+                {
+                    return double.NaN;
+                }
+
+                if (double.IsInfinity(lN) && rN == 0)
+                {
+                    if (NumberInstance.IsNegativeZero(rN))
+                    {
+                        return -lN;
+                    }
+
+                    return lN;
+                }
+
+                if (lN == 0 && rN == 0)
+                {
+                    return double.NaN;
+                }
+
+                if (rN == 0)
+                {
+                    if (NumberInstance.IsNegativeZero(rN))
+                    {
+                        return lN > 0 ? -double.PositiveInfinity : -double.NegativeInfinity;
+                    }
+
+                    return lN > 0 ? double.PositiveInfinity : double.NegativeInfinity;
+                }
+
+                return lN / rN;
+            }
+        }
+
+
+        protected static bool Equal(JsValue x, JsValue y)
+        {
+            if (x._type == y._type)
+            {
+                return JintBinaryExpression.StrictlyEqual(x, y);
+            }
+
+            if (x._type == Types.Null && y._type == Types.Undefined)
+            {
+                return true;
+            }
+
+            if (x._type == Types.Undefined && y._type == Types.Null)
+            {
+                return true;
+            }
+
+            if (x._type == Types.Number && y._type == Types.String)
+            {
+                return Equal(x, TypeConverter.ToNumber(y));
+            }
+
+            if (x._type == Types.String && y._type == Types.Number)
+            {
+                return Equal(TypeConverter.ToNumber(x), y);
+            }
+
+            if (x._type == Types.Boolean)
+            {
+                return Equal(TypeConverter.ToNumber(x), y);
+            }
+
+            if (y._type == Types.Boolean)
+            {
+                return Equal(x, TypeConverter.ToNumber(y));
+            }
+
+            if (y._type == Types.Object && (x._type == Types.String || x._type == Types.Number))
+            {
+                return Equal(x, TypeConverter.ToPrimitive(y));
+            }
+
+            if (x._type == Types.Object && (y._type == Types.String || y._type == Types.Number))
+            {
+                return Equal(TypeConverter.ToPrimitive(x), y);
+            }
+
+            return false;
+        }
+
+        public static bool SameValue(JsValue x, JsValue y)
+        {
+            var typea = TypeConverter.GetPrimitiveType(x);
+            var typeb = TypeConverter.GetPrimitiveType(y);
+
+            if (typea != typeb)
+            {
+                return false;
+            }
+
+            switch (typea)
+            {
+                case Types.None:
+                    return true;
+                case Types.Number:
+                    var nx = TypeConverter.ToNumber(x);
+                    var ny = TypeConverter.ToNumber(y);
+
+                    if (double.IsNaN(nx) && double.IsNaN(ny))
+                    {
+                        return true;
+                    }
+
+                    if (nx == ny)
+                    {
+                        if (nx == 0)
+                        {
+                            // +0 !== -0
+                            return NumberInstance.IsNegativeZero(nx) == NumberInstance.IsNegativeZero(ny);
+                        }
+
+                        return true;
+                    }
+
+                    return false;
+                case Types.String:
+                    return TypeConverter.ToString(x) == TypeConverter.ToString(y);
+                case Types.Boolean:
+                    return TypeConverter.ToBoolean(x) == TypeConverter.ToBoolean(y);
+                default:
+                    return x == y;
+            }
+        }
+
+        public static JsValue Compare(JsValue x, JsValue y, bool leftFirst = true)
+        {
+            JsValue px, py;
+            if (leftFirst)
+            {
+                px = TypeConverter.ToPrimitive(x, Types.Number);
+                py = TypeConverter.ToPrimitive(y, Types.Number);
+            }
+            else
+            {
+                py = TypeConverter.ToPrimitive(y, Types.Number);
+                px = TypeConverter.ToPrimitive(x, Types.Number);
+            }
+
+            var typea = px.Type;
+            var typeb = py.Type;
+
+            if (typea != Types.String || typeb != Types.String)
+            {
+                var nx = TypeConverter.ToNumber(px);
+                var ny = TypeConverter.ToNumber(py);
+
+                if (double.IsNaN(nx) || double.IsNaN(ny))
+                {
+                    return Undefined.Instance;
+                }
+
+                if (nx == ny)
+                {
+                    return false;
+                }
+
+                if (double.IsPositiveInfinity(nx))
+                {
+                    return false;
+                }
+
+                if (double.IsPositiveInfinity(ny))
+                {
+                    return true;
+                }
+
+                if (double.IsNegativeInfinity(ny))
+                {
+                    return false;
+                }
+
+                if (double.IsNegativeInfinity(nx))
+                {
+                    return true;
+                }
+
+                return nx < ny;
+            }
+            else
+            {
+                return string.CompareOrdinal(TypeConverter.ToString(x), TypeConverter.ToString(y)) < 0;
+            }
+        }
+
+        protected void BuildArguments(JintExpression[] jintExpressions, JsValue[] targetArray)
+        {
+            for (var i = 0; i < jintExpressions.Length; i++)
+            {
+                targetArray[i] = jintExpressions[i].GetValue();
+            }
+        }
+    }
+}

+ 37 - 0
Jint/Runtime/Interpreter/Expressions/JintFunctionExpression.cs

@@ -0,0 +1,37 @@
+using Esprima.Ast;
+using Jint.Native.Function;
+using Jint.Runtime.Environments;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintFunctionExpression : JintExpression
+    {
+        private readonly IFunction _function;
+        private readonly string _name;
+
+        public JintFunctionExpression(Engine engine, IFunction function) : base(engine, ArrowParameterPlaceHolder.Empty)
+        {
+            _function = function;
+            _name = !string.IsNullOrEmpty(function.Id?.Name) ? function.Id.Name : null;
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var funcEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
+            var envRec = (DeclarativeEnvironmentRecord) funcEnv._record;
+
+            var closure = new ScriptFunctionInstance(
+                _engine,
+                _function,
+                funcEnv,
+                _function.Strict);
+
+            if (_name != null)
+            {
+                envRec.CreateMutableBinding(_name, closure);
+            }
+
+            return closure;
+        }
+    }
+}

+ 46 - 0
Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs

@@ -0,0 +1,46 @@
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Runtime.Environments;
+using Jint.Runtime.References;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintIdentifierExpression : JintExpression
+    {
+        internal readonly string _expressionName;
+        private readonly JsValue _calculatedValue;
+
+        public JintIdentifierExpression(Engine engine, Identifier expression) : base(engine, expression)
+        {
+            _expressionName = expression.Name;
+            if (_expressionName == "undefined")
+            {
+                _calculatedValue = JsValue.Undefined;
+            }
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var env = _engine.ExecutionContext.LexicalEnvironment;
+            var strict = StrictModeScope.IsStrictModeCode;
+            return LexicalEnvironment.GetIdentifierReference(env, _expressionName, strict);
+        }
+
+        public override JsValue GetValue()
+        {
+            // need to notify correct node when taking shortcut
+            _engine._lastSyntaxNode = _expression;
+
+            if (!(_calculatedValue is null))
+            {
+                return _calculatedValue;
+            }
+
+            var env = _engine.ExecutionContext.LexicalEnvironment;
+            var strict = StrictModeScope.IsStrictModeCode;
+            return LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(env, _expressionName, strict, out _, out var value)
+                ? value
+                : _engine.GetValue(new Reference(JsValue.Undefined, _expressionName, strict), true);
+        }
+    }
+}

+ 64 - 0
Jint/Runtime/Interpreter/Expressions/JintLiteralExpression.cs

@@ -0,0 +1,64 @@
+using Esprima;
+using Esprima.Ast;
+using Jint.Native;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal class JintLiteralExpression : JintExpression
+    {
+        internal readonly JsValue _cachedValue;
+
+        public JintLiteralExpression(Engine engine, Literal expression) : base(engine, expression)
+        {
+            _cachedValue = ConvertToJsValue(expression);
+        }
+
+        internal static JsValue ConvertToJsValue(Literal literal)
+        {
+            if (literal.TokenType == TokenType.BooleanLiteral)
+            {
+                return literal.NumericValue > 0.0 ? JsBoolean.True : JsBoolean.False;
+            }
+
+            if (literal.TokenType == TokenType.NullLiteral)
+            {
+                return JsValue.Null;
+            }
+
+            if (literal.TokenType == TokenType.NumericLiteral)
+            {
+                return JsNumber.Create(literal.NumericValue);
+            }
+
+            if (literal.TokenType == TokenType.StringLiteral)
+            {
+                return JsString.Create((string) literal.Value);
+            }
+
+            return null;
+        }
+
+        public override JsValue GetValue()
+        {
+            // need to notify correct node when taking shortcut
+            _engine._lastSyntaxNode = _expression;
+            return _cachedValue ?? ResolveValue();
+        }
+
+        protected override object EvaluateInternal()
+        {
+            return GetValue();
+        }
+
+        private JsValue ResolveValue()
+        {
+            var expression = (Literal) _expression;
+            if (expression.TokenType == TokenType.RegularExpression)
+            {
+                return _engine.RegExp.Construct((System.Text.RegularExpressions.Regex) expression.Value, expression.Regex.Flags);
+            }
+
+            return JsValue.FromObject(_engine, expression.Value);
+        }
+    }
+}

+ 34 - 0
Jint/Runtime/Interpreter/Expressions/JintLogicalAndExpression.cs

@@ -0,0 +1,34 @@
+using Esprima.Ast;
+using Jint.Native;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintLogicalAndExpression : JintExpression
+    {
+        private readonly JintExpression _left;
+        private readonly JintExpression _right;
+
+        public JintLogicalAndExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
+        {
+            _left = Build(engine, expression.Left);
+            _right = Build(engine, expression.Right);
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var left = _left.GetValue();
+
+            if (left is JsBoolean b && !b._value)
+            {
+                return b;
+            }
+
+            if (!TypeConverter.ToBoolean(left))
+            {
+                return left;
+            }
+
+            return _right.GetValue();
+        }
+    }
+}

+ 34 - 0
Jint/Runtime/Interpreter/Expressions/JintLogicalOrExpression.cs

@@ -0,0 +1,34 @@
+using Esprima.Ast;
+using Jint.Native;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintLogicalOrExpression : JintExpression
+    {
+        private readonly JintExpression _left;
+        private readonly JintExpression _right;
+
+        public JintLogicalOrExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
+        {
+            _left = Build(engine, expression.Left);
+            _right = Build(engine, expression.Right);
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var left = _left.GetValue();
+
+            if (left is JsBoolean b && b._value)
+            {
+                return b;
+            }
+
+            if (TypeConverter.ToBoolean(left))
+            {
+                return left;
+            }
+
+            return _right.GetValue();
+        }
+    }
+}

+ 52 - 0
Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs

@@ -0,0 +1,52 @@
+using Esprima.Ast;
+using Jint.Runtime.References;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.1
+    /// </summary>
+    internal sealed class JintMemberExpression : JintExpression
+    {
+        private readonly JintExpression _objectExpression;
+        private readonly JintExpression _propertyExpression;
+        private readonly string _determinedPropertyNameString;
+
+        public JintMemberExpression(Engine engine, MemberExpression expression) : base(engine, expression)
+        {
+            _objectExpression = Build(engine, expression.Object);
+            if (!expression.Computed)
+            {
+                _determinedPropertyNameString = ((Identifier) expression.Property).Name;
+            }
+            else
+            {
+                _determinedPropertyNameString = null;
+                _propertyExpression = Build(engine, expression.Property);
+            }
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var baseReference = _objectExpression.Evaluate();
+            var baseValue = _engine.GetValue(baseReference, false);
+
+            string propertyNameString = _determinedPropertyNameString;
+            if (propertyNameString == null)
+            {
+                var propertyNameReference = _propertyExpression.Evaluate();
+                var propertyNameValue = _engine.GetValue(propertyNameReference, true);
+                propertyNameString = TypeConverter.ToPropertyKey(propertyNameValue);
+            }
+
+            TypeConverter.CheckObjectCoercible(_engine, baseValue, (MemberExpression) _expression, baseReference);
+
+            if (baseReference is Reference r)
+            {
+                _engine._referencePool.Return(r);
+            }
+
+            return _engine._referencePool.Rent(baseValue, propertyNameString, StrictModeScope.IsStrictModeCode);
+        }
+    }
+}

+ 47 - 0
Jint/Runtime/Interpreter/Expressions/JintNewExpression.cs

@@ -0,0 +1,47 @@
+using Esprima.Ast;
+using Jint.Native;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintNewExpression : JintExpression
+    {
+        private readonly JintExpression _calleeExpression;
+        private JintExpression[] _jintArguments;
+
+        public JintNewExpression(Engine engine, NewExpression expression) : base(engine, expression)
+        {
+            _initialized = false;
+            _calleeExpression = Build(engine, expression.Callee);
+        }
+
+        protected override void Initialize()
+        {
+            var expression = (NewExpression) _expression;
+            _jintArguments = new JintExpression[expression.Arguments.Count];
+            for (var i = 0; i < _jintArguments.Length; i++)
+            {
+                _jintArguments[i] = Build(_engine, (Expression) expression.Arguments[i]);
+            }
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var arguments = _engine._jsValueArrayPool.RentArray(_jintArguments.Length);
+
+            BuildArguments(_jintArguments, arguments);
+
+            // todo: optimize by defining a common abstract class or interface
+            if (!(_calleeExpression.GetValue() is IConstructor callee))
+            {
+                return ExceptionHelper.ThrowTypeError<object>(_engine, "The object can't be used as constructor.");
+            }
+
+            // construct the new instance using the Function's constructor method
+            var instance = callee.Construct(arguments);
+
+            _engine._jsValueArrayPool.ReturnArray(arguments);
+
+            return instance;
+        }
+    }
+}

+ 196 - 0
Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs

@@ -0,0 +1,196 @@
+using System.Collections.Generic;
+using System.Threading;
+using Esprima.Ast;
+using Jint.Collections;
+using Jint.Native.Function;
+using Jint.Native.Object;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-11.1.5
+    /// </summary>
+    internal sealed class JintObjectExpression : JintExpression
+    {
+        // cache key container for array iteration for less allocations
+        private static readonly ThreadLocal<HashSet<string>> _nameDuplicateChecks = new ThreadLocal<HashSet<string>>(() => new HashSet<string>());
+
+        private JintExpression[] _valueExpressions;
+        private ObjectProperty[] _properties;
+
+        // check if we can do a shortcut when all are object properties
+        // and don't require duplicate checking
+        private bool _canBuildFast;
+
+        private class ObjectProperty
+        {
+            internal string _name;
+            internal Property _value;
+        }
+
+        public JintObjectExpression(Engine engine, ObjectExpression expression) : base(engine, expression)
+        {
+            _initialized = false;
+        }
+
+        protected override void Initialize()
+        {
+            var expression = (ObjectExpression) _expression;
+            _valueExpressions = new JintExpression[expression.Properties.Count];
+            _properties = new ObjectProperty[expression.Properties.Count];
+
+            var propertyNames = _nameDuplicateChecks.Value;
+            propertyNames.Clear();
+
+            _canBuildFast = true;
+            for (var i = 0; i < _properties.Length; i++)
+            {
+                var property = expression.Properties[i];
+                var propName = property.Key.GetKey();
+                _properties[i] = new ObjectProperty
+                {
+                    _name = propName, _value = property
+                };
+
+                if (property.Kind == PropertyKind.Init || property.Kind == PropertyKind.Data)
+                {
+                    _valueExpressions[i] = Build(_engine, (Expression) property.Value);
+                }
+                else
+                {
+                    _canBuildFast = false;
+                }
+
+                _canBuildFast &= propertyNames.Add(propName);
+            }
+        }
+
+        protected override object EvaluateInternal()
+        {
+            return _canBuildFast
+                ? BuildObjectFast()
+                : BuildObjectNormal();
+        }
+
+        /// <summary>
+        /// Version that can safely build plain object with only normal init/data fields fast.
+        /// </summary>
+        private object BuildObjectFast()
+        {
+            var obj = _engine.Object.Construct(0);
+            var properties = _properties.Length > 1
+                ? new StringDictionarySlim<PropertyDescriptor>(_properties.Length)
+                : new StringDictionarySlim<PropertyDescriptor>();
+
+            for (var i = 0; i < _properties.Length; i++)
+            {
+                var objectProperty = _properties[i];
+                var propValue = _valueExpressions[i].GetValue();
+                properties[objectProperty._name] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
+            }
+
+            obj._properties = properties;
+            return obj;
+        }
+                                                
+        private object BuildObjectNormal()
+        {
+            var obj = _engine.Object.Construct(System.Math.Max(2, _properties.Length));
+            bool isStrictModeCode = StrictModeScope.IsStrictModeCode;
+            for (var i = 0; i < _properties.Length; i++)
+            {
+                var objectProperty = _properties[i];
+                var property = objectProperty._value;
+                var propName = objectProperty._name;
+                if (!obj._properties.TryGetValue(propName, out var previous))
+                {
+                    previous = PropertyDescriptor.Undefined;
+                }
+
+                PropertyDescriptor propDesc;
+
+                if (property.Kind == PropertyKind.Init || property.Kind == PropertyKind.Data)
+                {
+                    var expr = _valueExpressions[i];
+                    var propValue = expr.GetValue();
+                    propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
+                }
+                else if (property.Kind == PropertyKind.Get || property.Kind == PropertyKind.Set)
+                {
+                    var function = property.Value as IFunction ?? ExceptionHelper.ThrowSyntaxError<IFunction>(_engine);
+
+                    ScriptFunctionInstance functionInstance;
+                    using (new StrictModeScope(function.Strict))
+                    {
+                        functionInstance = new ScriptFunctionInstance(
+                            _engine,
+                            function,
+                            _engine.ExecutionContext.LexicalEnvironment,
+                            isStrictModeCode
+                        );
+                    }
+
+                    propDesc = new GetSetPropertyDescriptor(
+                        get: property.Kind == PropertyKind.Get ? functionInstance : null,
+                        set: property.Kind == PropertyKind.Set ? functionInstance : null,
+                        PropertyFlag.Enumerable | PropertyFlag.Configurable);
+                }
+                else
+                {
+                    return ExceptionHelper.ThrowArgumentOutOfRangeException<object>();
+                }
+
+                if (previous != PropertyDescriptor.Undefined)
+                {
+                    DefinePropertySlow(isStrictModeCode, previous, propDesc, obj, propName);
+                }
+                else
+                {
+                    // do faster direct set
+                    obj._properties[propName] = propDesc;
+                }
+            }
+
+            return obj;
+        }
+
+        private void DefinePropertySlow(
+            bool isStrictModeCode,
+            PropertyDescriptor previous,
+            PropertyDescriptor propDesc, ObjectInstance obj, string propName)
+        {
+            var previousIsDataDescriptor = previous.IsDataDescriptor();
+            if (isStrictModeCode && previousIsDataDescriptor && propDesc.IsDataDescriptor())
+            {
+                ExceptionHelper.ThrowSyntaxError(_engine);
+            }
+
+            if (previousIsDataDescriptor && propDesc.IsAccessorDescriptor())
+            {
+                ExceptionHelper.ThrowSyntaxError(_engine);
+            }
+
+            if (previous.IsAccessorDescriptor() && propDesc.IsDataDescriptor())
+            {
+                ExceptionHelper.ThrowSyntaxError(_engine);
+            }
+
+            if (previous.IsAccessorDescriptor() && propDesc.IsAccessorDescriptor())
+            {
+                if (!ReferenceEquals(propDesc.Set, null) && !ReferenceEquals(previous.Set, null))
+                {
+                    ExceptionHelper.ThrowSyntaxError(_engine);
+                }
+
+                if (!ReferenceEquals(propDesc.Get, null) && !ReferenceEquals(previous.Get, null))
+                {
+                    ExceptionHelper.ThrowSyntaxError(_engine);
+                }
+            }
+
+            obj.DefineOwnProperty(propName, propDesc, false);
+        }
+    }
+}

+ 38 - 0
Jint/Runtime/Interpreter/Expressions/JintSequenceExpression.cs

@@ -0,0 +1,38 @@
+using Esprima.Ast;
+using Jint.Native;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintSequenceExpression : JintExpression
+    {
+        private JintExpression[] _expressions;
+
+        public JintSequenceExpression(Engine engine, SequenceExpression expression) : base(engine, expression)
+        {
+            _initialized = false;
+        }
+
+        protected override void Initialize()
+        {
+            var expression = (SequenceExpression) _expression;
+            _expressions = new JintExpression[expression.Expressions.Count];
+            for (var i = 0; i < expression.Expressions.Count; i++)
+            {
+                _expressions[i] = Build(_engine, expression.Expressions[i]);
+            }
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var result = Undefined.Instance;
+            var expressions = _expressions;
+            for (var i = 0; i < (uint) expressions.Length; i++)
+            {
+                var expression = expressions[i];
+                result = expression.GetValue();
+            }
+
+            return result;
+        }
+    }
+}

+ 16 - 0
Jint/Runtime/Interpreter/Expressions/JintThisExpression.cs

@@ -0,0 +1,16 @@
+using Esprima.Ast;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintThisExpression : JintExpression
+    {
+        public JintThisExpression(Engine engine, ThisExpression expression) : base(engine, expression)
+        {
+        }
+
+        protected override object EvaluateInternal()
+        {
+            return _engine.ExecutionContext.ThisBinding;
+        }
+    }
+}

+ 120 - 0
Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs

@@ -0,0 +1,120 @@
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Runtime.Environments;
+using Jint.Runtime.References;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintUnaryExpression : JintExpression
+    {
+        private readonly JintExpression _argument;
+        private readonly UnaryOperator _operator;
+
+        public JintUnaryExpression(Engine engine, UnaryExpression expression) : base(engine, expression)
+        {
+            _argument = Build(engine, expression.Argument);
+            _operator = expression.Operator;
+        }
+
+        protected override object EvaluateInternal()
+        {
+            switch (_operator)
+            {
+                case UnaryOperator.Plus:
+                    return JsNumber.Create(TypeConverter.ToNumber(_argument.GetValue()));
+
+                case UnaryOperator.Minus:
+                    var n = TypeConverter.ToNumber(_argument.GetValue());
+                    return JsNumber.Create(double.IsNaN(n) ? double.NaN : n * -1);
+
+                case UnaryOperator.BitwiseNot:
+                    return JsNumber.Create(~TypeConverter.ToInt32(_argument.GetValue()));
+
+                case UnaryOperator.LogicalNot:
+                    return !TypeConverter.ToBoolean(_argument.GetValue()) ? JsBoolean.True : JsBoolean.False;
+
+                case UnaryOperator.Delete:
+                    var r = _argument.Evaluate() as Reference;
+                    if (r == null)
+                    {
+                        return JsBoolean.True;
+                    }
+
+                    if (r.IsUnresolvableReference())
+                    {
+                        if (r._strict)
+                        {
+                            ExceptionHelper.ThrowSyntaxError(_engine);
+                        }
+
+                        _engine._referencePool.Return(r);
+                        return JsBoolean.True;
+                    }
+
+                    if (r.IsPropertyReference())
+                    {
+                        var o = TypeConverter.ToObject(_engine, r.GetBase());
+                        var jsValue = o.Delete(r._name, r._strict);
+                        _engine._referencePool.Return(r);
+                        return jsValue ? JsBoolean.True : JsBoolean.False;
+                    }
+
+                    if (r._strict)
+                    {
+                        ExceptionHelper.ThrowSyntaxError(_engine);
+                    }
+
+                    var bindings = r.GetBase().TryCast<EnvironmentRecord>();
+                    var referencedName = r.GetReferencedName();
+                    _engine._referencePool.Return(r);
+
+                    return bindings.DeleteBinding(referencedName) ? JsBoolean.True : JsBoolean.False;
+
+                case UnaryOperator.Void:
+                    _argument.GetValue();
+                    return Undefined.Instance;
+
+                case UnaryOperator.TypeOf:
+                    var value = _argument.Evaluate();
+                    r = value as Reference;
+                    if (r != null)
+                    {
+                        if (r.IsUnresolvableReference())
+                        {
+                            _engine._referencePool.Return(r);
+                            return JsString.UndefinedString;
+                        }
+                    }
+
+                    var v = _argument.GetValue();
+
+                    if (v.IsUndefined())
+                    {
+                        return JsString.UndefinedString;
+                    }
+
+                    if (v.IsNull())
+                    {
+                        return JsString.ObjectString;
+                    }
+
+                    switch (v.Type)
+                    {
+                        case Types.Boolean: return JsString.BooleanString;
+                        case Types.Number: return JsString.NumberString;
+                        case Types.String: return JsString.StringString;
+                    }
+
+                    if (v.TryCast<ICallable>() != null)
+                    {
+                        return JsString.FunctionString;
+                    }
+
+                    return JsString.ObjectString;
+
+                default:
+                    return ExceptionHelper.ThrowArgumentException<object>();
+            }
+        }
+    }
+}

+ 89 - 0
Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs

@@ -0,0 +1,89 @@
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Runtime.Environments;
+using Jint.Runtime.References;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintUpdateExpression : JintExpression
+    {
+        private readonly JintExpression _argument;
+        private readonly int _change;
+        private readonly bool _prefix;
+
+        private readonly JintIdentifierExpression _leftIdentifier;
+        private readonly bool _evalOrArguments;
+
+        public JintUpdateExpression(Engine engine, UpdateExpression expression) : base(engine, expression)
+        {
+            _prefix = expression.Prefix;
+            _argument = Build(engine, expression.Argument);
+            if (expression.Operator == UnaryOperator.Increment)
+            {
+                _change = 1;
+            }
+            else if (expression.Operator == UnaryOperator.Decrement)
+            {
+                _change = - 1;
+            }
+            else
+            {
+                ExceptionHelper.ThrowArgumentException();
+            }
+
+            _leftIdentifier = _argument as JintIdentifierExpression;
+            _evalOrArguments = _leftIdentifier?._expressionName == "eval" || _leftIdentifier?._expressionName == "arguments";
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var fastResult = _leftIdentifier != null
+                ? UpdateIdentifier()
+                : null;
+
+            return fastResult ?? UpdateNonIdentifier();
+        }
+
+        private object UpdateNonIdentifier()
+        {
+            var value = (Reference) _argument.Evaluate();
+            value.AssertValid(_engine);
+
+            var oldValue = TypeConverter.ToNumber(_engine.GetValue(value, false));
+            var newValue = oldValue + _change;
+
+            _engine.PutValue(value, newValue);
+            _engine._referencePool.Return(value);
+
+            return JsNumber.Create(_prefix ? newValue : oldValue);
+        }
+
+        private JsValue UpdateIdentifier()
+        {
+            var env = _engine.ExecutionContext.LexicalEnvironment;
+            var strict = StrictModeScope.IsStrictModeCode;
+            var name = _leftIdentifier._expressionName;
+            if (LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
+                env,
+                name,
+                strict,
+                out var environmentRecord,
+                out var value))
+            {
+                if (strict && _evalOrArguments)
+                {
+                    ExceptionHelper.ThrowSyntaxError(_engine);
+                }
+
+                var oldValue = TypeConverter.ToNumber(value);
+                var newValue = oldValue + _change;
+
+                environmentRecord.SetMutableBinding(name, newValue, strict);
+                return JsNumber.Create(_prefix ? newValue : oldValue);
+            }
+
+            return null;
+        }
+
+    }
+}

+ 115 - 0
Jint/Runtime/Interpreter/JintStatementList.cs

@@ -0,0 +1,115 @@
+using System.Collections.Generic;
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Runtime.Interpreter.Statements;
+
+namespace Jint.Runtime.Interpreter
+{
+    public class JintStatementList
+    {
+        private class Pair
+        {
+            internal JintStatement Statement;
+            internal Completion? Value;
+        }
+
+        private readonly Engine _engine;
+        private readonly Statement _statement;
+        private readonly List<StatementListItem> _statements;
+
+        private Pair[] _jintStatements;
+        private bool _initialized;
+
+        public JintStatementList(Engine engine, Statement statement, List<StatementListItem> statements)
+        {
+            _engine = engine;
+            _statement = statement;
+            _statements = statements;
+        }
+
+        private void Initialize()
+        {
+            _jintStatements = new Pair[_statements.Count];
+            for (var i = 0; i < _jintStatements.Length; i++)
+            {
+                var statement = JintStatement.Build(_engine, (Statement) _statements[i]);
+                _jintStatements[i] = new Pair
+                {
+                    Statement = statement,
+                    Value = JintStatement.FastResolve(_statements[i])
+                };
+            }
+        }
+
+        public Completion Execute()
+        {
+            if (!_initialized)
+            {
+                Initialize();
+                _initialized = true;
+            }
+
+            if (_statement != null)
+            {
+                _engine._lastSyntaxNode = _statement;
+                if (_engine._runBeforeStatementChecks)
+                {
+                    _engine.RunBeforeExecuteStatementChecks(_statement);
+                }
+            }
+
+            JintStatement s = null;
+            var c = new Completion(CompletionType.Normal, null, null);
+            Completion sl = c;
+            try
+            {
+                var statements = _jintStatements;
+                for (var i = 0; i < (uint) statements.Length; i++)
+                {
+                    var pair = statements[i];
+                    s = pair.Statement;
+                    c = pair.Value ?? s.Execute();
+                    if (c.Type != CompletionType.Normal)
+                    {
+                        var executeStatementList = new Completion(
+                            c.Type,
+                            c.Value ?? sl.Value,
+                            c.Identifier,
+                            c.Location);
+
+                        return executeStatementList;
+                    }
+
+                    sl = c;
+                }
+            }
+            catch (JavaScriptException v)
+            {
+                var completion = new Completion(CompletionType.Throw, v.Error, null, v.Location ?? s?.Location);
+                return completion;
+            }
+            catch (TypeErrorException e)
+            {
+                var error = _engine.TypeError.Construct(new JsValue[]
+                {
+                    e.Message
+                });
+                return new Completion(CompletionType.Throw, error, null, s?.Location);
+            }
+            catch (RangeErrorException e)
+            {
+                var error = _engine.RangeError.Construct(new JsValue[]
+                {
+                    e.Message
+                });
+                c = new Completion(CompletionType.Throw, error, null, s?.Location);
+            }
+            return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
+        }
+
+        internal Completion? FastResolve()
+        {
+            return _statements.Count == 1 ? JintStatement.FastResolve(_statements[0]) : null;
+        }
+    }
+}

+ 22 - 0
Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs

@@ -0,0 +1,22 @@
+using Esprima;
+using Esprima.Ast;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    internal sealed class JintBlockStatement : JintStatement<Statement>
+    {
+        private readonly JintStatementList _statementList;
+
+        public JintBlockStatement(Engine engine, JintStatementList statementList) : base(engine, null)
+        {
+            _statementList = statementList;
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            return _statementList.Execute();
+        }
+
+        public override Location Location => null;
+    }
+}

+ 22 - 0
Jint/Runtime/Interpreter/Statements/JintBreakStatement.cs

@@ -0,0 +1,22 @@
+using Esprima.Ast;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.8
+    /// </summary>
+    internal sealed class JintBreakStatement : JintStatement<BreakStatement>
+    {
+        private readonly string _label;
+
+        public JintBreakStatement(Engine engine, BreakStatement statement) : base(engine, statement)
+        {
+            _label = statement.Label?.Name;
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            return new Completion(CompletionType.Break, null, _label);
+        }
+    }
+}

+ 22 - 0
Jint/Runtime/Interpreter/Statements/JintContinueStatement.cs

@@ -0,0 +1,22 @@
+using Esprima.Ast;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.7
+    /// </summary>
+    internal sealed class JintContinueStatement : JintStatement<ContinueStatement>
+    {
+        private readonly string _labelName;
+
+        public JintContinueStatement(Engine engine, ContinueStatement statement) : base(engine, statement)
+        {
+            _labelName = _statement.Label?.Name;
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            return new Completion(CompletionType.Continue, null, _labelName);
+        }
+    }
+}

+ 26 - 0
Jint/Runtime/Interpreter/Statements/JintDebuggerStatement.cs

@@ -0,0 +1,26 @@
+using Esprima.Ast;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    internal sealed class JintDebuggerStatement : JintStatement<DebuggerStatement>
+    {
+        public JintDebuggerStatement(Engine engine, DebuggerStatement statement) : base(engine, statement)
+        {
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            if (_engine.Options._IsDebuggerStatementAllowed)
+            {
+                if (!System.Diagnostics.Debugger.IsAttached)
+                {
+                    System.Diagnostics.Debugger.Launch();
+                }
+
+                System.Diagnostics.Debugger.Break();
+            }
+
+            return new Completion(CompletionType.Normal, null, null);
+        }
+    }
+}

+ 55 - 0
Jint/Runtime/Interpreter/Statements/JintDoWhileStatement.cs

@@ -0,0 +1,55 @@
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.1
+    /// </summary>
+    internal sealed class JintDoWhileStatement : JintStatement<DoWhileStatement>
+    {
+        private readonly JintStatement _body;
+        private readonly string _labelSetName;
+        private readonly JintExpression _test;
+
+        public JintDoWhileStatement(Engine engine, DoWhileStatement statement) : base(engine, statement)
+        {
+            _body = Build(_engine, statement.Body);
+            _test = JintExpression.Build(engine, statement.Test);
+            _labelSetName = statement.LabelSet?.Name;
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            JsValue v = Undefined.Instance;
+            bool iterating;
+
+            do
+            {
+                var completion = _body.Execute();
+                if (!ReferenceEquals(completion.Value, null))
+                {
+                    v = completion.Value;
+                }
+
+                if (completion.Type != CompletionType.Continue || completion.Identifier != _labelSetName)
+                {
+                    if (completion.Type == CompletionType.Break && (completion.Identifier == null || completion.Identifier == _labelSetName))
+                    {
+                        return new Completion(CompletionType.Normal, v, null);
+                    }
+
+                    if (completion.Type != CompletionType.Normal)
+                    {
+                        return completion;
+                    }
+                }
+
+                iterating = TypeConverter.ToBoolean(_test.GetValue());
+            } while (iterating);
+
+            return new Completion(CompletionType.Normal, v, null);
+        }
+    }
+}

+ 16 - 0
Jint/Runtime/Interpreter/Statements/JintEmptyStatement.cs

@@ -0,0 +1,16 @@
+using Esprima.Ast;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    internal sealed class JintEmptyStatement : JintStatement<EmptyStatement>
+    {
+        public JintEmptyStatement(Engine engine, EmptyStatement statement) : base(engine, statement)
+        {
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            return new Completion(CompletionType.Normal, null, null);
+        }
+    }
+}

+ 21 - 0
Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs

@@ -0,0 +1,21 @@
+using Esprima.Ast;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    internal sealed class JintExpressionStatement : JintStatement<ExpressionStatement>
+    {
+        private readonly JintExpression _expression;
+
+        public JintExpressionStatement(Engine engine, ExpressionStatement statement) : base(engine, statement)
+        {
+            _expression = JintExpression.Build(engine, statement.Expression);
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            var value = _expression.GetValue();
+            return new Completion(CompletionType.Normal, value, null);
+        }
+    }
+}

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

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

+ 85 - 0
Jint/Runtime/Interpreter/Statements/JintForStatement.cs

@@ -0,0 +1,85 @@
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.3
+    /// </summary>
+    internal sealed class JintForStatement : JintStatement<ForStatement>
+    {
+        private readonly JintStatement _body;
+        private readonly JintStatement _initStatement;
+        private readonly JintExpression _initExpression;
+        private readonly JintExpression _test;
+        private readonly JintExpression _update;
+
+        public JintForStatement(Engine engine, ForStatement statement) : base(engine, statement)
+        {
+            _body = Build(engine, _statement.Body);
+
+            if (_statement.Init != null)
+            {
+                if (_statement.Init.Type == Nodes.VariableDeclaration)
+                {
+                    _initStatement = Build(engine, (Statement) _statement.Init);
+                }
+                else
+                {
+                    _initExpression = JintExpression.Build(engine, (Expression) statement.Init);
+                }
+            }
+
+            if (_statement.Test != null)
+            {
+                _test = JintExpression.Build(engine, statement.Test);
+            }
+
+            if (_statement.Update != null)
+            {
+                _update = JintExpression.Build(engine, statement.Update);
+            }
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            _initStatement?.Execute();
+            _initExpression?.GetValue();
+
+            JsValue v = Undefined.Instance;
+            while (true)
+            {
+                if (_test != null)
+                {
+                    if (!TypeConverter.ToBoolean(_test.GetValue()))
+                    {
+                        return new Completion(CompletionType.Normal, v, null);
+                    }
+                }
+
+                var stmt = _body.Execute();
+                if (!ReferenceEquals(stmt.Value, null))
+                {
+                    v = stmt.Value;
+                }
+
+                var stmtType = stmt.Type;
+                if (stmtType == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == _statement?.LabelSet?.Name))
+                {
+                    return new Completion(CompletionType.Normal, v, null);
+                }
+
+                if (stmtType != CompletionType.Continue || ((stmt.Identifier != null) && stmt.Identifier != _statement?.LabelSet?.Name))
+                {
+                    if (stmtType != CompletionType.Normal)
+                    {
+                        return stmt;
+                    }
+                }
+
+                _update?.GetValue();
+            }
+        }
+    }
+}

+ 16 - 0
Jint/Runtime/Interpreter/Statements/JintFunctionDeclarationStatement.cs

@@ -0,0 +1,16 @@
+using Esprima.Ast;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    internal sealed class JintFunctionDeclarationStatement : JintStatement<FunctionDeclaration>
+    {
+        public JintFunctionDeclarationStatement(Engine engine, FunctionDeclaration statement) : base(engine, statement)
+        {
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            return new Completion(CompletionType.Normal, null, null);
+        }
+    }
+}

+ 38 - 0
Jint/Runtime/Interpreter/Statements/JintIfStatement.cs

@@ -0,0 +1,38 @@
+using Esprima.Ast;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    internal sealed class JintIfStatement : JintStatement<IfStatement>
+    {
+        private readonly JintStatement _statementConsequent;
+        private readonly JintExpression _test;
+        private readonly JintStatement _alternate;
+
+        public JintIfStatement(Engine engine, IfStatement statement) : base(engine, statement)
+        {
+            _statementConsequent = Build(engine, _statement.Consequent);
+            _test = JintExpression.Build(engine, _statement.Test);
+            _alternate = _statement.Alternate != null ? Build(engine, _statement.Alternate) : null;
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            Completion result;
+            if (TypeConverter.ToBoolean(_test.GetValue()))
+            {
+                result = _statementConsequent.Execute();
+            }
+            else if (_alternate != null)
+            {
+                result = _alternate.Execute();
+            }
+            else
+            {
+                return new Completion(CompletionType.Normal, null, null);
+            }
+
+            return result;
+        }
+    }
+}

+ 31 - 0
Jint/Runtime/Interpreter/Statements/JintLabeledStatement.cs

@@ -0,0 +1,31 @@
+using Esprima.Ast;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    internal sealed class JintLabeledStatement : JintStatement<LabeledStatement>
+    {
+        private readonly JintStatement _body;
+        private readonly string _labelName;
+
+        public JintLabeledStatement(Engine engine, LabeledStatement statement) : base(engine, statement)
+        {
+            _body = Build(engine, statement.Body);
+            _labelName = statement.Label.Name;
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            // TODO: Esprima added Statement.Label, maybe not necessary as this line is finding the
+            // containing label and could keep a table per program with all the labels
+            // labeledStatement.Body.LabelSet = labeledStatement.Label;
+            var result = _body.Execute();
+            if (result.Type == CompletionType.Break && result.Identifier == _labelName)
+            {
+                var value = result.Value;
+                return new Completion(CompletionType.Normal, value, null);
+            }
+
+            return result;
+        }
+    }
+}

+ 19 - 0
Jint/Runtime/Interpreter/Statements/JintProgram.cs

@@ -0,0 +1,19 @@
+using Esprima.Ast;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    internal sealed class JintProgram : JintStatement<Program>
+    {
+        private readonly JintStatementList _list;
+
+        public JintProgram(Engine engine, Program statement) : base(engine, statement)
+        {
+            _list = new JintStatementList(_engine, null, _statement.Body);
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            return _list.Execute();
+        }
+    }
+}

+ 27 - 0
Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs

@@ -0,0 +1,27 @@
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.9
+    /// </summary>
+    internal sealed class JintReturnStatement : JintStatement<ReturnStatement>
+    {
+        private readonly JintExpression _argument;
+
+        public JintReturnStatement(Engine engine, ReturnStatement statement) : base(engine, statement)
+        {
+            _argument = _statement.Argument != null
+                ? JintExpression.Build(engine, _statement.Argument)
+                : null;
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            var jsValue = _argument?.GetValue() ?? Undefined.Instance;
+            return new Completion(CompletionType.Return, jsValue, null);
+        }
+    }
+}

+ 139 - 0
Jint/Runtime/Interpreter/Statements/JintStatement.cs

@@ -0,0 +1,139 @@
+using Esprima;
+using Esprima.Ast;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    internal abstract class JintStatement<T> : JintStatement where T : Statement
+    {
+        protected readonly Engine _engine;
+        protected readonly T _statement;
+        private bool _initialized;
+
+        protected JintStatement(Engine engine, T statement)
+        {
+            _engine = engine;
+            _statement = statement;
+        }
+
+        public sealed override Completion Execute()
+        {
+            _engine._lastSyntaxNode = _statement;
+
+            if (_engine._runBeforeStatementChecks)
+            {
+                _engine.RunBeforeExecuteStatementChecks(_statement);
+            }
+
+            if (!_initialized)
+            {
+                Initialize();
+                _initialized = true;
+            }
+
+            return ExecuteInternal();
+        }
+
+        public override Location Location => _statement.Location;
+
+        /// <summary>
+        /// Opportunity to build one-time structures and caching based on lexical context.
+        /// </summary>
+        protected virtual void Initialize()
+        {
+        }
+    }
+
+    internal abstract class JintStatement
+    {
+        public abstract Location Location { get; }
+
+        public abstract Completion Execute();
+
+        protected abstract Completion ExecuteInternal();
+
+        protected internal static JintStatement Build(Engine engine, Statement statement)
+        {
+            switch (statement.Type)
+            {
+                case Nodes.BlockStatement:
+                    var statementListItems = ((BlockStatement) statement).Body;
+                    return new JintBlockStatement(engine, new JintStatementList(engine, statement, statementListItems));
+
+                case Nodes.ReturnStatement:
+                    return new JintReturnStatement(engine, (ReturnStatement) statement);
+
+                case Nodes.VariableDeclaration:
+                    return new JintVariableDeclaration(engine, (VariableDeclaration) statement);
+
+                case Nodes.BreakStatement:
+                    return new JintBreakStatement(engine, (BreakStatement) statement);
+
+                case Nodes.ContinueStatement:
+                    return new JintContinueStatement(engine, (ContinueStatement) statement);
+
+                case Nodes.DoWhileStatement:
+                    return new JintDoWhileStatement(engine, (DoWhileStatement) statement);
+
+                case Nodes.EmptyStatement:
+                    return new JintEmptyStatement(engine, (EmptyStatement) statement);
+
+                case Nodes.ExpressionStatement:
+                    return new JintExpressionStatement(engine, (ExpressionStatement) statement);
+
+                case Nodes.ForStatement:
+                    return new JintForStatement(engine, (ForStatement) statement);
+
+                case Nodes.ForInStatement:
+                    return new JintForInStatement(engine, (ForInStatement) statement);
+
+                case Nodes.IfStatement:
+                    return new JintIfStatement(engine, (IfStatement) statement);
+
+                case Nodes.LabeledStatement:
+                    return new JintLabeledStatement(engine, (LabeledStatement) statement);
+
+                case Nodes.SwitchStatement:
+                    return new JintSwitchStatement(engine, (SwitchStatement) statement);
+
+                case Nodes.FunctionDeclaration:
+                    return new JintFunctionDeclarationStatement(engine, (FunctionDeclaration) statement);
+
+                case Nodes.ThrowStatement:
+                    return new JintThrowStatement(engine, (ThrowStatement) statement);
+
+                case Nodes.TryStatement:
+                    return new JintTryStatement(engine, (TryStatement) statement);
+
+                case Nodes.WhileStatement:
+                    return new JintWhileStatement(engine, (WhileStatement) statement);
+
+                case Nodes.WithStatement:
+                    return new JintWithStatement(engine, (WithStatement) statement);
+
+                case Nodes.DebuggerStatement:
+                    return new JintDebuggerStatement(engine, (DebuggerStatement) statement);
+
+                case Nodes.Program:
+                    return new JintProgram(engine, (Program) statement);
+
+                default:
+                    return ExceptionHelper.ThrowArgumentOutOfRangeException<JintStatement>();
+            }
+        }
+
+        internal static Completion? FastResolve(StatementListItem statement)
+        {
+            if (statement is ReturnStatement rs && rs.Argument is Literal l)
+            {
+                var jsValue = JintLiteralExpression.ConvertToJsValue(l);
+                if (jsValue != null)
+                {
+                    return new Completion(CompletionType.Return, jsValue, null);
+                }
+            }
+
+            return null;
+        }
+    }
+}

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

@@ -0,0 +1,104 @@
+using System.Collections.Generic;
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    internal sealed class JintSwitchBlock
+    {
+        private readonly Engine _engine;
+        private readonly List<SwitchCase> _switchBlock;
+        private JintSwitchCase[] _jintSwitchBlock;
+        private bool _initialized;
+
+        public JintSwitchBlock(Engine engine, List<SwitchCase> switchBlock)
+        {
+            _engine = engine;
+            _switchBlock = switchBlock;
+        }
+
+        private void Initialize()
+        {
+            _jintSwitchBlock = new JintSwitchCase[_switchBlock.Count];
+            for (var i = 0; i < _jintSwitchBlock.Length; i++)
+            {
+                _jintSwitchBlock[i] = new JintSwitchCase(_engine, _switchBlock[i]);
+            }
+        }
+
+        public Completion Execute(JsValue input)
+        {
+            if (!_initialized)
+            {
+                Initialize();
+                _initialized = true;
+            }
+
+            JsValue v = Undefined.Instance;
+            JintSwitchCase defaultCase = null;
+            bool hit = false;
+
+            for (var i = 0; i < (uint) _jintSwitchBlock.Length; i++)
+            {
+                var clause = _jintSwitchBlock[i];
+                if (clause.Test == null)
+                {
+                    defaultCase = clause;
+                }
+                else
+                {
+                    var clauseSelector = clause.Test.GetValue();
+                    if (JintBinaryExpression.StrictlyEqual(clauseSelector, input))
+                    {
+                        hit = true;
+                    }
+                }
+
+                if (hit && clause.Consequent != null)
+                {
+                    var r = clause.Consequent.Execute();
+                    if (r.Type != CompletionType.Normal)
+                    {
+                        return r;
+                    }
+
+                    v = r.Value ?? Undefined.Instance;
+                }
+            }
+
+            // do we need to execute the default case ?
+            if (hit == false && defaultCase != null)
+            {
+                var r = defaultCase.Consequent.Execute();
+                if (r.Type != CompletionType.Normal)
+                {
+                    return r;
+                }
+
+                v = r.Value ?? Undefined.Instance;
+            }
+
+            return new Completion(CompletionType.Normal, v, null);
+        }
+
+        private sealed class JintSwitchCase
+        {
+            internal readonly JintStatementList Consequent;
+            internal readonly JintExpression Test;
+
+            public JintSwitchCase(Engine engine, SwitchCase switchCase)
+            {
+                if (switchCase.Consequent != null)
+                {
+                    Consequent = new JintStatementList(engine, null, switchCase.Consequent);
+                }
+
+                if (switchCase.Test != null)
+                {
+                    Test = JintExpression.Build(engine, switchCase.Test);
+                }
+            }
+        }
+    }
+}

+ 32 - 0
Jint/Runtime/Interpreter/Statements/JintSwitchStatement.cs

@@ -0,0 +1,32 @@
+using Esprima.Ast;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.11
+    /// </summary>
+    internal sealed class JintSwitchStatement : JintStatement<SwitchStatement>
+    {
+        private readonly JintSwitchBlock _switchBlock;
+        private readonly JintExpression _discriminant;
+
+        public JintSwitchStatement(Engine engine, SwitchStatement statement) : base(engine, statement)
+        {
+            _switchBlock = new JintSwitchBlock(engine, _statement.Cases);
+            _discriminant = JintExpression.Build(engine, _statement.Discriminant);
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            var jsValue = _discriminant.GetValue();
+            var r = _switchBlock.Execute(jsValue);
+            if (r.Type == CompletionType.Break && r.Identifier == _statement.LabelSet?.Name)
+            {
+                return new Completion(CompletionType.Normal, r.Value, null);
+            }
+
+            return r;
+        }
+    }
+}

+ 24 - 0
Jint/Runtime/Interpreter/Statements/JintThrowStatement.cs

@@ -0,0 +1,24 @@
+using Esprima.Ast;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.13
+    /// </summary>
+    internal sealed class JintThrowStatement : JintStatement<ThrowStatement>
+    {
+        private readonly JintExpression _argument;
+
+        public JintThrowStatement(Engine engine, ThrowStatement statement) : base(engine, statement)
+        {
+            _argument = JintExpression.Build(engine, _statement.Argument);
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            var jsValue = _argument.GetValue();
+            return new Completion(CompletionType.Throw, jsValue, null, _statement.Location);
+        }
+    }
+}

+ 64 - 0
Jint/Runtime/Interpreter/Statements/JintTryStatement.cs

@@ -0,0 +1,64 @@
+using Esprima.Ast;
+using Jint.Runtime.Environments;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.14
+    /// </summary>
+    internal sealed class JintTryStatement : JintStatement<TryStatement>
+    {
+        private readonly JintStatement _block;
+        private readonly JintStatement _catch;
+        private readonly string _catchParamName;
+        private readonly JintStatement _finalizer;
+
+        public JintTryStatement(Engine engine, TryStatement statement) : base(engine, statement)
+        {
+            _block = Build(engine, statement.Block);
+            if (_statement.Handler != null)
+            {
+                _catch = Build(engine, _statement.Handler.Body);
+                _catchParamName = ((Identifier) _statement.Handler.Param).Name;
+            }
+
+            if (statement.Finalizer != null)
+            {
+                _finalizer = Build(engine, _statement.Finalizer);
+            }
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            var b = _block.Execute();
+            if (b.Type == CompletionType.Throw)
+            {
+                // execute catch
+                if (_catch != null)
+                {
+                    var c = b.Value;
+                    var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
+                    var catchEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
+                    catchEnv._record.CreateMutableBinding(_catchParamName, c);
+
+                    _engine.UpdateLexicalEnvironment(catchEnv);
+                    b = _catch.Execute();
+                    _engine.UpdateLexicalEnvironment(oldEnv);
+                }
+            }
+
+            if (_finalizer != null)
+            {
+                var f = _finalizer.Execute();
+                if (f.Type == CompletionType.Normal)
+                {
+                    return b;
+                }
+
+                return f;
+            }
+
+            return b;
+        }
+    }
+}

+ 63 - 0
Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs

@@ -0,0 +1,63 @@
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Runtime.Interpreter.Expressions;
+using Jint.Runtime.References;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    internal sealed class JintVariableDeclaration : JintStatement<VariableDeclaration>
+    {
+        private ResolvedDeclaration[] _declarations;
+
+        private sealed class ResolvedDeclaration
+        {
+            internal JintExpression Left;
+            internal JintExpression Init;
+        }
+
+        public JintVariableDeclaration(Engine engine, VariableDeclaration statement) : base(engine, statement)
+        {
+        }
+
+        protected override void Initialize()
+        {
+            _declarations = new ResolvedDeclaration[_statement.Declarations.Count];
+            for (var i = 0; i < _declarations.Length; i++)
+            {
+                var declaration = _statement.Declarations[i];
+                var left = declaration.Init != null
+                    ? JintExpression.Build(_engine, (Expression) declaration.Id)
+                    : null;
+                var init = declaration.Init != null
+                    ? JintExpression.Build(_engine, declaration.Init)
+                    : null;
+
+                _declarations[i] = new ResolvedDeclaration
+                {
+                    Left = left,
+                    Init = init
+                };
+            }
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            var declarations = _declarations;
+            for (var i = 0; i < (uint) declarations.Length; i++)
+            {
+                var declaration = declarations[i];
+                if (declaration.Init != null)
+                {
+                    var lhs = (Reference) declaration.Left.Evaluate();
+                    lhs.AssertValid(_engine);
+
+                    var value = declaration.Init.GetValue();
+                    _engine.PutValue(lhs, value);
+                    _engine._referencePool.Return(lhs);
+                }
+            }
+
+            return new Completion(CompletionType.Normal, Undefined.Instance, null);
+        }
+    }
+}

+ 56 - 0
Jint/Runtime/Interpreter/Statements/JintWhileStatement.cs

@@ -0,0 +1,56 @@
+using Esprima.Ast;
+using Jint.Native;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.2
+    /// </summary>
+    internal sealed class JintWhileStatement : JintStatement<WhileStatement>
+    {
+        private readonly string _labelSetName;
+        private readonly JintStatement _body;
+        private readonly JintExpression _test;
+
+        public JintWhileStatement(Engine engine, WhileStatement statement) : base(engine, statement)
+        {
+            _labelSetName = _statement?.LabelSet?.Name;
+            _body = Build(engine, statement.Body);
+            _test = JintExpression.Build(engine, statement.Test);
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            var v = Undefined.Instance;
+            while (true)
+            {
+                var jsValue = _test.GetValue();
+                if (!TypeConverter.ToBoolean(jsValue))
+                {
+                    return new Completion(CompletionType.Normal, v, null);
+                }
+
+                var completion = _body.Execute();
+
+                if (!ReferenceEquals(completion.Value, null))
+                {
+                    v = completion.Value;
+                }
+
+                if (completion.Type != CompletionType.Continue || completion.Identifier != _labelSetName)
+                {
+                    if (completion.Type == CompletionType.Break && (completion.Identifier == null || completion.Identifier == _labelSetName))
+                    {
+                        return new Completion(CompletionType.Normal, v, null);
+                    }
+
+                    if (completion.Type != CompletionType.Normal)
+                    {
+                        return completion;
+                    }
+                }
+            }
+        }
+    }
+}

+ 46 - 0
Jint/Runtime/Interpreter/Statements/JintWithStatement.cs

@@ -0,0 +1,46 @@
+using Esprima.Ast;
+using Jint.Runtime.Environments;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.10
+    /// </summary>
+    internal sealed class JintWithStatement : JintStatement<WithStatement>
+    {
+        private readonly JintStatement _body;
+        private readonly JintExpression _object;
+
+        public JintWithStatement(Engine engine, WithStatement statement) : base(engine, statement)
+        {
+            _body = Build(engine, statement.Body);
+            _object = JintExpression.Build(engine, _statement.Object);
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            var jsValue = _object.GetValue();
+            var obj = TypeConverter.ToObject(_engine, jsValue);
+            var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
+            var newEnv = LexicalEnvironment.NewObjectEnvironment(_engine, obj, oldEnv, true);
+            _engine.UpdateLexicalEnvironment(newEnv);
+
+            Completion c;
+            try
+            {
+                c = _body.Execute();
+            }
+            catch (JavaScriptException e)
+            {
+                c = new Completion(CompletionType.Throw, e.Error, null, _statement.Location);
+            }
+            finally
+            {
+                _engine.UpdateLexicalEnvironment(oldEnv);
+            }
+
+            return c;
+        }
+    }
+}

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

@@ -70,7 +70,7 @@ namespace Jint.Runtime.References
 
         internal void AssertValid(Engine engine)
         {
-            if(_strict && (_name == "eval" || _name == "arguments") && _baseValue is EnvironmentRecord)
+            if (_strict && (_name == "eval" || _name == "arguments") && _baseValue is EnvironmentRecord)
             {
                 ExceptionHelper.ThrowSyntaxError(engine);
             }

+ 0 - 612
Jint/Runtime/StatementInterpreter.cs

@@ -1,612 +0,0 @@
-using System.Collections.Generic;
-using Esprima.Ast;
-using Jint.Native;
-using Jint.Runtime.Descriptors;
-using Jint.Runtime.Environments;
-using Jint.Runtime.References;
-
-namespace Jint.Runtime
-{
-    public sealed class StatementInterpreter
-    {
-        private readonly Engine _engine;
-
-        public StatementInterpreter(Engine engine)
-        {
-            _engine = engine;
-        }
-
-        private Completion ExecuteStatement(Statement statement)
-        {
-            return _engine.ExecuteStatement(statement);
-        }
-
-        public Completion ExecuteEmptyStatement(EmptyStatement emptyStatement)
-        {
-            return new Completion(CompletionType.Normal, null, null);
-        }
-
-        public Completion ExecuteExpressionStatement(ExpressionStatement expressionStatement)
-        {
-            var exprRef = _engine.EvaluateExpression(expressionStatement.Expression);
-            return new Completion(CompletionType.Normal, _engine.GetValue(exprRef, true), null);
-        }
-
-        public Completion ExecuteIfStatement(IfStatement ifStatement)
-        {
-            Completion result;
-            if (TypeConverter.ToBoolean(_engine.GetValue(_engine.EvaluateExpression(ifStatement.Test), true)))
-            {
-                result = _engine.ExecuteStatement(ifStatement.Consequent);
-            }
-            else if (ifStatement.Alternate != null)
-            {
-                result = _engine.ExecuteStatement(ifStatement.Alternate);
-            }
-            else
-            {
-                return new Completion(CompletionType.Normal, null, null);
-            }
-
-            return result;
-        }
-
-        public Completion ExecuteLabeledStatement(LabeledStatement labeledStatement)
-        {
-            // TODO: Esprima added Statement.Label, maybe not necessary as this line is finding the
-            // containing label and could keep a table per program with all the labels
-            // labeledStatement.Body.LabelSet = labeledStatement.Label;
-            var result = _engine.ExecuteStatement(labeledStatement.Body);
-            if (result.Type == CompletionType.Break && result.Identifier == labeledStatement.Label.Name)
-            {
-                var value = result.Value;
-                return new Completion(CompletionType.Normal, value, null);
-            }
-
-            return result;
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.1
-        /// </summary>
-        /// <param name="doWhileStatement"></param>
-        /// <returns></returns>
-        public Completion ExecuteDoWhileStatement(DoWhileStatement doWhileStatement)
-        {
-            JsValue v = Undefined.Instance;
-            bool iterating;
-
-            do
-            {
-                var stmt = _engine.ExecuteStatement(doWhileStatement.Body);
-                if (!ReferenceEquals(stmt.Value, null))
-                {
-                    v = stmt.Value;
-                }
-                if (stmt.Type != CompletionType.Continue || stmt.Identifier != doWhileStatement?.LabelSet?.Name)
-                {
-                    if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == doWhileStatement?.LabelSet?.Name))
-                    {
-                        return new Completion(CompletionType.Normal, v, null);
-                    }
-
-                    if (stmt.Type != CompletionType.Normal)
-                    {
-                        return stmt;
-                    }
-                }
-
-                var exprRef = _engine.EvaluateExpression(doWhileStatement.Test);
-                iterating = TypeConverter.ToBoolean(_engine.GetValue(exprRef, true));
-
-            } while (iterating);
-
-            return new Completion(CompletionType.Normal, v, null);
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.2
-        /// </summary>
-        /// <param name="whileStatement"></param>
-        /// <returns></returns>
-        public Completion ExecuteWhileStatement(WhileStatement whileStatement)
-        {
-            JsValue v = Undefined.Instance;
-            while (true)
-            {
-                var jsValue = _engine.GetValue(_engine.EvaluateExpression(whileStatement.Test), true);
-                if (!TypeConverter.ToBoolean(jsValue))
-                {
-                    return new Completion(CompletionType.Normal, v, null);
-                }
-
-                var stmt = _engine.ExecuteStatement(whileStatement.Body);
-
-                if (!ReferenceEquals(stmt.Value, null))
-                {
-                    v = stmt.Value;
-                }
-
-                if (stmt.Type != CompletionType.Continue || stmt.Identifier != whileStatement?.LabelSet?.Name)
-                {
-                    if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == whileStatement?.LabelSet?.Name))
-                    {
-                        return new Completion(CompletionType.Normal, v, null);
-                    }
-
-                    if (stmt.Type != CompletionType.Normal)
-                    {
-                        return stmt;
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.3
-        /// </summary>
-        /// <param name="forStatement"></param>
-        /// <returns></returns>
-        public Completion ExecuteForStatement(ForStatement forStatement)
-        {
-            var init = forStatement.Init;
-            if (init != null)
-            {
-                if (init.Type == Nodes.VariableDeclaration)
-                {
-                    var c = _engine.ExecuteStatement((Statement) init);
-
-                }
-                else
-                {
-                    _engine.GetValue(_engine.EvaluateExpression(init), true);
-                }
-            }
-
-            JsValue v = Undefined.Instance;
-            while (true)
-            {
-                if (forStatement.Test != null)
-                {
-                    var testExprRef = _engine.EvaluateExpression(forStatement.Test);
-                    if (!TypeConverter.ToBoolean(_engine.GetValue(testExprRef, true)))
-                    {
-                        return new Completion(CompletionType.Normal, v, null);
-                    }
-                }
-
-                var stmt = _engine.ExecuteStatement(forStatement.Body);
-                if (!ReferenceEquals(stmt.Value, null))
-                {
-                    v = stmt.Value;
-                }
-
-                var stmtType = stmt.Type;
-                if (stmtType == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == forStatement?.LabelSet?.Name))
-                {
-                    return new Completion(CompletionType.Normal, v, null);
-                }
-                if (stmtType != CompletionType.Continue || ((stmt.Identifier != null) && stmt.Identifier != forStatement?.LabelSet?.Name))
-                {
-                    if (stmtType != CompletionType.Normal)
-                    {
-                        return stmt;
-                    }
-                }
-                if (forStatement.Update != null)
-                {
-                    _engine.GetValue(_engine.EvaluateExpression(forStatement.Update), true);
-                }
-            }
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.4
-        /// </summary>
-        /// <param name="forInStatement"></param>
-        /// <returns></returns>
-        public Completion ExecuteForInStatement(ForInStatement forInStatement)
-        {
-            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 (experValue.IsNullOrUndefined())
-            {
-                return new Completion(CompletionType.Normal, null, null);
-            }
-
-            var obj = TypeConverter.ToObject(_engine, experValue);
-            JsValue v = Null.Instance;
-
-            // keys are constructed using the prototype chain
-            var cursor = obj;
-            var processedKeys = new HashSet<string>();
-
-            while (!ReferenceEquals(cursor, null))
-            {
-                var keys = _engine.Object.GetOwnPropertyNames(Undefined.Instance, Arguments.From(cursor)).AsArray();
-
-                var length = keys.GetLength();
-                for (var i = 0; i < length; i++)
-                {
-                    var p = keys.GetOwnProperty(TypeConverter.ToString(i)).Value.AsStringWithoutTypeCheck();
-
-                    if (processedKeys.Contains(p))
-                    {
-                        continue;
-                    }
-
-                    processedKeys.Add(p);
-
-                    // collection might be modified by inner statement
-                    if (cursor.GetOwnProperty(p) == PropertyDescriptor.Undefined)
-                    {
-                        continue;
-                    }
-
-                    var value = cursor.GetOwnProperty(p);
-                    if (!value.Enumerable)
-                    {
-                        continue;
-                    }
-
-                    _engine.PutValue(varRef, p);
-
-                    var stmt = _engine.ExecuteStatement(forInStatement.Body);
-                    if (!ReferenceEquals(stmt.Value, null))
-                    {
-                        v = stmt.Value;
-                    }
-                    if (stmt.Type == CompletionType.Break)
-                    {
-                        return new Completion(CompletionType.Normal, v, null);
-                    }
-                    if (stmt.Type != CompletionType.Continue)
-                    {
-                        if (stmt.Type != CompletionType.Normal)
-                        {
-                            return stmt;
-                        }
-                    }
-                }
-
-                cursor = cursor.Prototype;
-            }
-
-            return new Completion(CompletionType.Normal, v, null);
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.7
-        /// </summary>
-        /// <param name="continueStatement"></param>
-        /// <returns></returns>
-        public Completion ExecuteContinueStatement(ContinueStatement continueStatement)
-        {
-            return new Completion(
-                CompletionType.Continue,
-                null,
-                continueStatement.Label?.Name);
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.8
-        /// </summary>
-        /// <param name="breakStatement"></param>
-        /// <returns></returns>
-        public Completion ExecuteBreakStatement(BreakStatement breakStatement)
-        {
-            return new Completion(
-                CompletionType.Break,
-                null,
-                breakStatement.Label?.Name);
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.9
-        /// </summary>
-        /// <param name="statement"></param>
-        /// <returns></returns>
-        public Completion ExecuteReturnStatement(ReturnStatement statement)
-        {
-            if (statement.Argument == null)
-            {
-                return new Completion(CompletionType.Return, Undefined.Instance, null);
-            }
-
-            var jsValue = _engine.GetValue(_engine.EvaluateExpression(statement.Argument), true);
-            return new Completion(CompletionType.Return, jsValue, null);
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.10
-        /// </summary>
-        /// <param name="withStatement"></param>
-        /// <returns></returns>
-        public Completion ExecuteWithStatement(WithStatement withStatement)
-        {
-            var jsValue = _engine.GetValue(_engine.EvaluateExpression(withStatement.Object), true);
-            var obj = TypeConverter.ToObject(_engine, jsValue);
-            var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
-            var newEnv = LexicalEnvironment.NewObjectEnvironment(_engine, obj, oldEnv, true);
-            _engine.UpdateLexicalEnvironment(newEnv);
-
-            Completion c;
-            try
-            {
-                c = _engine.ExecuteStatement(withStatement.Body);
-            }
-            catch (JavaScriptException e)
-            {
-                c = new Completion(CompletionType.Throw, e.Error, null, withStatement.Location);
-            }
-            catch (TypeErrorException e)
-            {
-                var error = _engine.TypeError.Construct(new JsValue[] {e.Message});
-                c = new Completion(CompletionType.Throw, error, null, withStatement.Location);
-            }
-            catch (RangeErrorException e)
-            {
-                var error = _engine.RangeError.Construct(new JsValue[] {e.Message});
-                c = new Completion(CompletionType.Throw, error, null, withStatement.Location);
-            }
-            finally
-            {
-                _engine.UpdateLexicalEnvironment(oldEnv);
-            }
-
-            return c;
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.11
-        /// </summary>
-        /// <param name="switchStatement"></param>
-        /// <returns></returns>
-        public Completion ExecuteSwitchStatement(SwitchStatement switchStatement)
-        {
-            var jsValue = _engine.GetValue(_engine.EvaluateExpression(switchStatement.Discriminant), true);
-            var r = ExecuteSwitchBlock(switchStatement.Cases, jsValue);
-            if (r.Type == CompletionType.Break && r.Identifier == switchStatement.LabelSet?.Name)
-            {
-                return new Completion(CompletionType.Normal, r.Value, null);
-            }
-            return r;
-        }
-
-        public Completion ExecuteSwitchBlock(List<SwitchCase> switchBlock, JsValue input)
-        {
-            JsValue v = Undefined.Instance;
-            SwitchCase defaultCase = null;
-            bool hit = false;
-
-            var switchBlockCount = switchBlock.Count;
-            for (var i = 0; i < switchBlockCount; i++)
-            {
-                var clause = switchBlock[i];
-                if (clause.Test == null)
-                {
-                    defaultCase = clause;
-                }
-                else
-                {
-                    var clauseSelector = _engine.GetValue(_engine.EvaluateExpression(clause.Test), true);
-                    if (ExpressionInterpreter.StrictlyEqual(clauseSelector, input))
-                    {
-                        hit = true;
-                    }
-                }
-
-                if (hit && clause.Consequent != null)
-                {
-                    var r = ExecuteStatementList(clause.Consequent);
-                    if (r.Type != CompletionType.Normal)
-                    {
-                        return r;
-                    }
-
-                    v = r.Value ?? Undefined.Instance;
-                }
-            }
-
-            // do we need to execute the default case ?
-            if (hit == false && defaultCase != null)
-            {
-                var r = ExecuteStatementList(defaultCase.Consequent);
-                if (r.Type != CompletionType.Normal)
-                {
-                    return r;
-                }
-
-                v = r.Value ?? Undefined.Instance;
-            }
-
-            return new Completion(CompletionType.Normal, v, null);
-        }
-
-        public Completion ExecuteStatementList(List<StatementListItem> statementList)
-        {
-            // optimize common case without loop
-            return statementList.Count == 1 
-                ? ExecuteSingleStatement((Statement) statementList[0]) 
-                : ExecuteMultipleStatements(statementList);
-        }
-
-        private Completion ExecuteMultipleStatements(List<StatementListItem> statementList)
-        {
-            Statement s = null;
-            var c = new Completion(CompletionType.Normal, null, null);
-            Completion sl = c;
-            try
-            {
-                var statementListCount = statementList.Count;
-                for (var i = 0; i < statementListCount; i++)
-                {
-                    s = (Statement) statementList[i];
-                    c = _engine.ExecuteStatement(s);
-                    if (c.Type != CompletionType.Normal)
-                    {
-                        var executeStatementList = new Completion(
-                            c.Type,
-                            c.Value ?? sl.Value,
-                            c.Identifier,
-                            c.Location);
-
-                        return executeStatementList;
-                    }
-
-                    sl = c;
-                }
-            }
-            catch (JavaScriptException v)
-            {
-                var completion = new Completion(CompletionType.Throw, v.Error, null, v.Location ?? s?.Location);
-                return completion;
-            }
-            catch (TypeErrorException e)
-            {
-                var error = _engine.TypeError.Construct(new JsValue[] {e.Message});
-                c = new Completion(CompletionType.Throw, error, null, s?.Location);
-            }
-            catch (RangeErrorException e)
-            {
-                var error = _engine.RangeError.Construct(new JsValue[] {e.Message});
-                c = new Completion(CompletionType.Throw, error, null, s?.Location);
-            }
-
-            return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
-        }
-
-        private Completion ExecuteSingleStatement(Statement s)
-        {
-            try
-            {
-                var c = _engine.ExecuteStatement(s);
-                if (c.Type != CompletionType.Normal)
-                {
-                    var completion = new Completion(
-                        c.Type,
-                        c.Value,
-                        c.Identifier,
-                        c.Location);
-
-                    return completion;
-                }
-
-                return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
-            }
-            catch (JavaScriptException v)
-            {
-                return new Completion(CompletionType.Throw, v.Error, null, v.Location ?? s?.Location);
-            }
-            catch (TypeErrorException e)
-            {
-                var error = _engine.TypeError.Construct(new JsValue[] {e.Message});
-                return new Completion(CompletionType.Throw, error, null, s?.Location);
-            }
-            catch (RangeErrorException e)
-            {
-                var error = _engine.RangeError.Construct(new JsValue[] {e.Message});
-                return new Completion(CompletionType.Throw, error, null, s?.Location);
-            }
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.13
-        /// </summary>
-        /// <param name="throwStatement"></param>
-        /// <returns></returns>
-        public Completion ExecuteThrowStatement(ThrowStatement throwStatement)
-        {
-            var jsValue = _engine.GetValue(_engine.EvaluateExpression(throwStatement.Argument), true);
-            return new Completion(CompletionType.Throw, jsValue, null, throwStatement.Location);
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.14
-        /// </summary>
-        /// <param name="tryStatement"></param>
-        /// <returns></returns>
-        public Completion ExecuteTryStatement(TryStatement tryStatement)
-        {
-            var b = _engine.ExecuteStatement(tryStatement.Block);
-            if (b.Type == CompletionType.Throw)
-            {
-                // execute catch
-                var catchClause = tryStatement.Handler;
-                if (catchClause != null)
-                {
-                    var c = b.Value;
-                    var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
-                    var catchEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
-                    catchEnv._record.CreateMutableBinding(((Identifier) catchClause.Param).Name, c);
-
-                    _engine.UpdateLexicalEnvironment(catchEnv);
-                    b = _engine.ExecuteStatement(catchClause.Body);
-                    _engine.UpdateLexicalEnvironment(oldEnv);
-                }
-            }
-
-            if (tryStatement.Finalizer != null)
-            {
-                var f = _engine.ExecuteStatement(tryStatement.Finalizer);
-                if (f.Type == CompletionType.Normal)
-                {
-                    return b;
-                }
-
-                return f;
-            }
-
-            return b;
-        }
-
-        public Completion ExecuteProgram(Program program)
-        {
-            return ExecuteStatementList(program.Body);
-        }
-
-        public Completion ExecuteVariableDeclaration(VariableDeclaration statement)
-        {
-            var declarationsCount = statement.Declarations.Count;
-            for (var i = 0; i < declarationsCount; i++)
-            {
-                var declaration = statement.Declarations[i];
-                if (declaration.Init != null)
-                {
-                    var lhs = (Reference) _engine.EvaluateExpression(declaration.Id);
-                    lhs.AssertValid(_engine);
-
-                    var value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init), true);
-                    _engine.PutValue(lhs, value);
-                    _engine._referencePool.Return(lhs);
-                }
-            }
-
-            return new Completion(CompletionType.Normal, Undefined.Instance, null);
-        }
-
-        public Completion ExecuteBlockStatement(BlockStatement blockStatement)
-        {
-            return ExecuteStatementList(blockStatement.Body);
-        }
-
-        public Completion ExecuteDebuggerStatement(DebuggerStatement debuggerStatement)
-        {
-            if (_engine.Options._IsDebuggerStatementAllowed)
-            {
-                if (!System.Diagnostics.Debugger.IsAttached)
-                {
-                    System.Diagnostics.Debugger.Launch();
-                }
-
-                System.Diagnostics.Debugger.Break();
-            }
-
-            return new Completion(CompletionType.Normal, null, null);
-        }
-    }
-}

+ 25 - 0
Jint/Runtime/TypeConverter.cs

@@ -112,6 +112,24 @@ namespace Jint.Runtime
             }
         }
 
+        internal static bool CanBeIndex(string input)
+        {
+            if (string.IsNullOrEmpty(input))
+            {
+                return false;
+            }
+
+            char first = input[0];
+            if (first < 32 || (first > 57 && first != 73))
+            {
+                // does not start with space, +, -, number or I
+                return false;
+            }
+
+            // might be
+            return true;
+        }
+
         private static double ToNumber(string input)
         {
             // eager checks to save time and trimming
@@ -120,6 +138,13 @@ namespace Jint.Runtime
                 return 0;
             }
 
+            char first = input[0];
+            if (input.Length == 1 && first >= '0' && first <= '9')
+            {
+                // simple constant number
+                return first - '0';
+            }
+
             var s = StringPrototype.IsWhiteSpaceEx(input[0]) || StringPrototype.IsWhiteSpaceEx(input[input.Length - 1])
                 ? StringPrototype.TrimEx(input)
                 : input;