Browse Source

Introduce EvaluationContext for statement and expression evaluation (#979)

* Introduce EvaluationContext parameter to evaluation process

* Introduce ExpressionResult and Completion as results in evaluation
Marko Lahma 3 years ago
parent
commit
bf140bff22
99 changed files with 1567 additions and 1191 deletions
  1. 3 1
      Jint.Tests/Runtime/Debugger/DebugHandlerTests.cs
  2. 2 3
      Jint.Tests/Runtime/Debugger/ScopeTests.cs
  3. 9 17
      Jint.Tests/Runtime/EngineTests.cs
  4. 1 1
      Jint/Collections/ListDictionary.cs
  5. 103 81
      Jint/Engine.cs
  6. 4 11
      Jint/EsprimaExtensions.cs
  7. 1 1
      Jint/Native/Array/ArrayConstructor.cs
  8. 8 5
      Jint/Native/Function/ClassDefinition.cs
  9. 2 2
      Jint/Native/Function/EvalFunctionInstance.cs
  10. 3 3
      Jint/Native/Function/FunctionInstance.cs
  11. 3 2
      Jint/Native/Function/ScriptFunctionInstance.cs
  12. 1 1
      Jint/Native/Iterator/IIterator.cs
  13. 5 5
      Jint/Native/Iterator/IteratorInstance.cs
  14. 2 2
      Jint/Native/Iterator/IteratorProtocol.cs
  15. 45 0
      Jint/Native/Iterator/IteratorResult.cs
  16. 5 8
      Jint/Native/JsValue.cs
  17. 3 3
      Jint/Native/Map/MapPrototype.cs
  18. 1 1
      Jint/Native/Number/Dtoa/CachePowers.cs
  19. 1 1
      Jint/Native/Number/Dtoa/DoubleHelper.cs
  20. 1 1
      Jint/Native/Number/Dtoa/DtoaBuilder.cs
  21. 26 0
      Jint/Native/RegExp/RegExpStringIteratorPrototypeObject.cs
  22. 1 1
      Jint/Native/Set/SetConstructor.cs
  23. 1 1
      Jint/Native/String/StringExecutionContext.cs
  24. 1 1
      Jint/Native/TypedArray/IntrinsicTypedArrayConstructor.cs
  25. 1 1
      Jint/Native/WeakMap/WeakMapInstance.cs
  26. 1 1
      Jint/Pooling/ConcurrentObjectPool.cs
  27. 1 1
      Jint/Pooling/ObjectPool.cs
  28. 2 2
      Jint/Runtime/CallStack/CallStackElement.cs
  29. 1 1
      Jint/Runtime/CallStack/JintCallStack.cs
  30. 38 10
      Jint/Runtime/Completion.cs
  31. 4 2
      Jint/Runtime/Environments/Binding.cs
  32. 9 18
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  33. 2 0
      Jint/Runtime/Environments/EnvironmentRecord.cs
  34. 33 22
      Jint/Runtime/Environments/FunctionEnvironmentRecord.cs
  35. 3 3
      Jint/Runtime/Environments/JintEnvironment.cs
  36. 2 2
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  37. 1 1
      Jint/Runtime/Interop/MethodDescriptor.cs
  38. 1 1
      Jint/Runtime/Interop/MethodInfoFunctionInstance.cs
  39. 1 1
      Jint/Runtime/Interop/Reflection/ExtensionMethodCache.cs
  40. 15 1
      Jint/Runtime/Interop/Reflection/IndexerAccessor.cs
  41. 1 1
      Jint/Runtime/Interop/TypeDescriptor.cs
  42. 1 1
      Jint/Runtime/Interop/TypeReference.cs
  43. 25 0
      Jint/Runtime/Interpreter/EvaluationContext.cs
  44. 90 55
      Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs
  45. 11 9
      Jint/Runtime/Interpreter/Expressions/JintArrayExpression.cs
  46. 8 7
      Jint/Runtime/Interpreter/Expressions/JintArrowFunctionExpression.cs
  47. 64 54
      Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs
  48. 143 170
      Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs
  49. 40 35
      Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs
  50. 7 7
      Jint/Runtime/Interpreter/Expressions/JintClassExpression.cs
  51. 5 5
      Jint/Runtime/Interpreter/Expressions/JintConditionalExpression.cs
  52. 5 10
      Jint/Runtime/Interpreter/Expressions/JintConstantExpression.cs
  53. 112 37
      Jint/Runtime/Interpreter/Expressions/JintExpression.cs
  54. 8 8
      Jint/Runtime/Interpreter/Expressions/JintFunctionExpression.cs
  55. 17 16
      Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs
  56. 15 15
      Jint/Runtime/Interpreter/Expressions/JintLiteralExpression.cs
  57. 9 9
      Jint/Runtime/Interpreter/Expressions/JintLogicalAndExpression.cs
  58. 6 6
      Jint/Runtime/Interpreter/Expressions/JintLogicalOrExpression.cs
  59. 23 18
      Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs
  60. 5 5
      Jint/Runtime/Interpreter/Expressions/JintMetaPropertyExpression.cs
  61. 17 13
      Jint/Runtime/Interpreter/Expressions/JintNewExpression.cs
  62. 36 21
      Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs
  63. 7 6
      Jint/Runtime/Interpreter/Expressions/JintSequenceExpression.cs
  64. 12 12
      Jint/Runtime/Interpreter/Expressions/JintSpreadExpression.cs
  65. 4 4
      Jint/Runtime/Interpreter/Expressions/JintSuperExpression.cs
  66. 18 16
      Jint/Runtime/Interpreter/Expressions/JintTaggedTemplateExpression.cs
  67. 18 18
      Jint/Runtime/Interpreter/Expressions/JintTemplateLiteralExpression.cs
  68. 6 7
      Jint/Runtime/Interpreter/Expressions/JintThisExpression.cs
  69. 39 33
      Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs
  70. 23 21
      Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs
  71. 9 9
      Jint/Runtime/Interpreter/Expressions/NullishCoalescingExpression.cs
  72. 6 6
      Jint/Runtime/Interpreter/JintFunctionDefinition.cs
  73. 26 17
      Jint/Runtime/Interpreter/JintStatementList.cs
  74. 13 12
      Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs
  75. 2 2
      Jint/Runtime/Interpreter/Statements/JintBreakStatement.cs
  76. 6 5
      Jint/Runtime/Interpreter/Statements/JintClassDeclarationStatement.cs
  77. 3 3
      Jint/Runtime/Interpreter/Statements/JintContinueStatement.cs
  78. 5 4
      Jint/Runtime/Interpreter/Statements/JintDebuggerStatement.cs
  79. 17 13
      Jint/Runtime/Interpreter/Statements/JintDoWhileStatement.cs
  80. 2 2
      Jint/Runtime/Interpreter/Statements/JintEmptyStatement.cs
  81. 17 6
      Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs
  82. 72 56
      Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs
  83. 37 36
      Jint/Runtime/Interpreter/Statements/JintForStatement.cs
  84. 4 3
      Jint/Runtime/Interpreter/Statements/JintFunctionDeclarationStatement.cs
  85. 16 12
      Jint/Runtime/Interpreter/Statements/JintIfStatement.cs
  86. 12 8
      Jint/Runtime/Interpreter/Statements/JintLabeledStatement.cs
  87. 9 5
      Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs
  88. 4 4
      Jint/Runtime/Interpreter/Statements/JintScript.cs
  89. 56 38
      Jint/Runtime/Interpreter/Statements/JintStatement.cs
  90. 22 22
      Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs
  91. 10 10
      Jint/Runtime/Interpreter/Statements/JintSwitchStatement.cs
  92. 5 6
      Jint/Runtime/Interpreter/Statements/JintThrowStatement.cs
  93. 29 20
      Jint/Runtime/Interpreter/Statements/JintTryStatement.cs
  94. 24 25
      Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs
  95. 16 12
      Jint/Runtime/Interpreter/Statements/JintWhileStatement.cs
  96. 19 14
      Jint/Runtime/Interpreter/Statements/JintWithStatement.cs
  97. 2 0
      Jint/Runtime/KnownKeys.cs
  98. 2 2
      Jint/Runtime/References/Reference.cs
  99. 0 1
      Jint/Runtime/TypeConverter.cs

+ 3 - 1
Jint.Tests/Runtime/Debugger/DebugHandlerTests.cs

@@ -2,6 +2,8 @@
 using Jint.Runtime.Debugger;
 using Xunit;
 
+#pragma warning disable 618
+
 namespace Jint.Tests.Runtime.Debugger
 {
     public class DebugHandlerTests
@@ -33,7 +35,7 @@ namespace Jint.Tests.Runtime.Debugger
                     var obj = info.CurrentScopeChain.Global.GetBindingValue("obj") as ObjectInstance;
                     var prop = obj.GetOwnProperty("name");
                     // This is where reentrance would occur:
-                    var value = prop.Get.Invoke();
+                    var value = prop.Get.Invoke(engine);
                     didPropertyAccess = true;
                 }
                 return StepMode.Into;

+ 2 - 3
Jint.Tests/Runtime/Debugger/ScopeTests.cs

@@ -194,14 +194,13 @@ namespace Jint.Tests.Runtime.Debugger
             string script = @"
             'dummy statement';
             {
-                debugger; // const is initialized (as undefined) at beginning of block
                 const blockConst = 'block';
+                debugger; // const isn't initialized until declaration
             }";
 
             TestHelpers.TestAtBreak(script, info =>
             {
-                var value = AssertOnlyScopeContains(info.CurrentScopeChain, "blockConst", DebugScopeType.Block);
-                Assert.Equal(JsUndefined.Undefined, value);
+                AssertOnlyScopeContains(info.CurrentScopeChain, "blockConst", DebugScopeType.Block);
             });
         }
 

+ 9 - 17
Jint.Tests/Runtime/EngineTests.cs

@@ -3,7 +3,6 @@ using System.Globalization;
 using System.IO;
 using System.Reflection;
 using Esprima;
-using Esprima.Ast;
 using Jint.Native;
 using Jint.Native.Array;
 using Jint.Native.Object;
@@ -12,6 +11,8 @@ using Jint.Runtime.Debugger;
 using Xunit;
 using Xunit.Abstractions;
 
+#pragma warning disable 618
+
 namespace Jint.Tests.Runtime
 {
     public class EngineTests : IDisposable
@@ -816,7 +817,7 @@ namespace Jint.Tests.Runtime
 
             var add = _engine.GetValue("add");
 
-            Assert.Equal(3, add.Invoke(1, 2));
+            Assert.Equal(3, add.Invoke(_engine, 1, 2));
         }
 
         [Fact]
@@ -828,7 +829,7 @@ namespace Jint.Tests.Runtime
 
             var add = _engine.GetValue("get");
             string str = null;
-            Assert.Equal(Native.JsValue.Null, add.Invoke(str));
+            Assert.Equal(Native.JsValue.Null, add.Invoke(_engine, str));
         }
 
 
@@ -841,7 +842,8 @@ namespace Jint.Tests.Runtime
 
             var x = _engine.GetValue("x");
 
-            Assert.Throws<TypeErrorException>(() => x.Invoke(1, 2));
+            var exception = Assert.Throws<JavaScriptException>(() => x.Invoke(_engine, 1, 2));
+            Assert.Equal("Can only invoke functions", exception.Message);
         }
 
         [Fact]
@@ -1022,16 +1024,6 @@ namespace Jint.Tests.Runtime
             Assert.Equal(3.3d, result);
         }
 
-        [Fact]
-        public void ShouldGetTheLastSyntaxNode()
-        {
-            var engine = new Engine();
-            engine.Evaluate("1.2");
-
-            var result = engine.GetLastSyntaxNode();
-            Assert.Equal(Nodes.Literal, result.Type);
-        }
-
         [Fact]
         public void ShouldGetParseErrorLocation()
         {
@@ -2509,7 +2501,7 @@ var prep = function (fn) { fn(); };
                 }");
 
             var concat = _engine.GetValue("concat");
-            var result = concat.Invoke("concat", "well", "done").ToObject() as string;
+            var result = concat.Invoke(_engine, "concat", "well", "done").ToObject() as string;
             Assert.Equal("concatwelldone", result);
         }
 
@@ -2643,10 +2635,10 @@ function output(x) {
             ");
 
             var function = _engine.GetValue("f");
-            var result = function.Invoke(3).ToString();
+            var result = function.Invoke(_engine, 3).ToString();
             Assert.Equal("15", result);
 
-            result = function.Invoke(3, JsValue.Undefined).ToString();
+            result = function.Invoke(_engine, 3, JsValue.Undefined).ToString();
             Assert.Equal("15", result);
         }
 

+ 1 - 1
Jint/Collections/ListDictionary.cs

@@ -233,7 +233,7 @@ namespace Jint.Collections
             object IEnumerator.Current => _current;
         }
 
-        internal class DictionaryNode
+        internal sealed class DictionaryNode
         {
             public Key Key;
             public TValue Value;

+ 103 - 81
Jint/Engine.cs

@@ -33,7 +33,7 @@ namespace Jint
 
         private readonly ExecutionContextStack _executionContexts;
         private JsValue _completionValue = JsValue.Undefined;
-        internal Node _lastSyntaxNode;
+        internal EvaluationContext _activeEvaluationContext;
 
         private readonly EventLoop _eventLoop = new();
 
@@ -266,8 +266,10 @@ namespace Jint
 
         public Engine Execute(Script script)
         {
+            var ownsContext = _activeEvaluationContext is null;
+            _activeEvaluationContext ??= new EvaluationContext(this);
+
             ResetConstraints();
-            ResetLastStatement();
 
             using (new StrictModeScope(_isStrict || script.Strict))
             {
@@ -275,17 +277,23 @@ namespace Jint
                     script,
                     Realm.GlobalEnv);
 
-                var list = new JintStatementList(this, null, script.Body);
+                var list = new JintStatementList(null, script.Body);
 
                 Completion result;
                 try
                 {
-                    result = list.Execute();
+                    result = list.Execute(_activeEvaluationContext);
                 }
                 catch
                 {
                     // unhandled exception
                     ResetCallStack();
+
+                    if (ownsContext)
+                    {
+                        _activeEvaluationContext = null;
+                    }
+
                     throw;
                 }
 
@@ -294,6 +302,12 @@ namespace Jint
                     var ex = new JavaScriptException(result.GetValueOrDefault())
                         .SetCallstack(this, result.Location);
                     ResetCallStack();
+
+                    if (ownsContext)
+                    {
+                        _activeEvaluationContext = null;
+                    }
+
                     throw ex;
                 }
 
@@ -301,6 +315,12 @@ namespace Jint
                 RunAvailableContinuations(_eventLoop);
 
                 _completionValue = result.GetValueOrDefault();
+
+                if (ownsContext)
+                {
+                    _activeEvaluationContext = null;
+                }
+
             }
 
             return this;
@@ -359,11 +379,6 @@ namespace Jint
             }
         }
 
-        private void ResetLastStatement()
-        {
-            _lastSyntaxNode = null;
-        }
-
         internal void RunBeforeExecuteStatementChecks(Statement statement)
         {
             // Avoid allocating the enumerator because we run this loop very often.
@@ -393,7 +408,7 @@ namespace Jint
                 return jsValue;
             }
 
-            if (!(value is Reference reference))
+            if (value is not Reference reference)
             {
                 return ((Completion) value).Value;
             }
@@ -619,16 +634,28 @@ namespace Jint
                 ExceptionHelper.ThrowTypeError(Realm, "Can only invoke functions");
             }
 
-            var items = _jsValueArrayPool.RentArray(arguments.Length);
-            for (int i = 0; i < arguments.Length; ++i)
-            {
-                items[i] = JsValue.FromObject(this, arguments[i]);
-            }
+            var ownsContext = _activeEvaluationContext is null;
+            _activeEvaluationContext ??= new EvaluationContext(this);
 
-            var result = callable.Call(JsValue.FromObject(this, thisObj), items);
-            _jsValueArrayPool.ReturnArray(items);
+            try
+            {
+                var items = _jsValueArrayPool.RentArray(arguments.Length);
+                for (var i = 0; i < arguments.Length; ++i)
+                {
+                    items[i] = JsValue.FromObject(this, arguments[i]);
+                }
 
-            return result;
+                var result = callable.Call(JsValue.FromObject(this, thisObj), items);
+                _jsValueArrayPool.ReturnArray(items);
+                return result;
+            }
+            finally
+            {
+                if (ownsContext)
+                {
+                    _activeEvaluationContext = null;
+                }
+            }
         }
 
         /// <summary>
@@ -636,19 +663,32 @@ namespace Jint
         /// </summary>
         internal JsValue Invoke(JsValue v, JsValue p, JsValue[] arguments)
         {
-            var func = GetV(v, p);
-            var callable = func as ICallable;
-            if (callable is null)
+            var ownsContext = _activeEvaluationContext is null;
+            _activeEvaluationContext ??= new EvaluationContext(this);
+            try
             {
-                ExceptionHelper.ThrowTypeErrorNoEngine("Can only invoke functions");
+                var func = GetV(v, p);
+                var callable = func as ICallable;
+                if (callable is null)
+                {
+                    ExceptionHelper.ThrowTypeErrorNoEngine("Can only invoke functions");
+                }
+
+                return callable.Call(v, arguments);
+            }
+            finally
+            {
+                if (ownsContext)
+                {
+                    _activeEvaluationContext = null;
+                }
             }
-            return callable.Call(v, arguments);
         }
 
         /// <summary>
         /// https://tc39.es/ecma262/#sec-getv
         /// </summary>
-        internal JsValue GetV(JsValue v, JsValue p)
+        private JsValue GetV(JsValue v, JsValue p)
         {
             var o = TypeConverter.ToObject(Realm, v);
             return o.Get(p);
@@ -668,7 +708,7 @@ namespace Jint
         /// </summary>
         internal Node GetLastSyntaxNode()
         {
-            return _lastSyntaxNode;
+            return _activeEvaluationContext?.LastSyntaxNode;
         }
 
         /// <summary>
@@ -693,7 +733,7 @@ namespace Jint
             return GetIdentifierReference(env, name, StrictModeScope.IsStrictModeCode);
         }
 
-        private Reference GetIdentifierReference(EnvironmentRecord env, string name, bool strict)
+        private static Reference GetIdentifierReference(EnvironmentRecord env, string name, bool strict)
         {
             if (env is null)
             {
@@ -828,7 +868,7 @@ namespace Jint
 
             foreach (var f in functionToInitialize)
             {
-                var fn = f.Function.Id!.Name;
+                var fn = f.Name;
 
                 if (env.HasLexicalDeclaration(fn))
                 {
@@ -851,12 +891,12 @@ namespace Jint
         /// </summary>
         internal ArgumentsInstance FunctionDeclarationInstantiation(
             FunctionInstance functionInstance,
-            JsValue[] argumentsList,
-            EnvironmentRecord env)
+            JsValue[] argumentsList)
         {
+            var calleeContext = ExecutionContext;
             var func = functionInstance._functionDefinition;
 
-            var envRec = (FunctionEnvironmentRecord) env;
+            var env = (FunctionEnvironmentRecord) ExecutionContext.LexicalEnvironment;
             var strict = StrictModeScope.IsStrictModeCode;
 
             var configuration = func.Initialize(functionInstance);
@@ -866,7 +906,7 @@ namespace Jint
             var hasParameterExpressions = configuration.HasParameterExpressions;
 
             var canInitializeParametersOnDeclaration = simpleParameterList && !configuration.HasDuplicates;
-            envRec.InitializeParameters(parameterNames, hasDuplicates,
+            env.InitializeParameters(parameterNames, hasDuplicates,
                 canInitializeParametersOnDeclaration ? argumentsList : null);
 
             ArgumentsInstance ao = null;
@@ -880,23 +920,23 @@ namespace Jint
                 {
                     // NOTE: mapped argument object is only provided for non-strict functions that don't have a rest parameter,
                     // any parameter default value initializers, or any destructured parameters.
-                    ao = CreateMappedArgumentsObject(functionInstance, parameterNames, argumentsList, envRec, configuration.HasRestParameter);
+                    ao = CreateMappedArgumentsObject(functionInstance, parameterNames, argumentsList, env, configuration.HasRestParameter);
                 }
 
                 if (strict)
                 {
-                    envRec.CreateImmutableBindingAndInitialize(KnownKeys.Arguments, strict: false, ao);
+                    env.CreateImmutableBindingAndInitialize(KnownKeys.Arguments, strict: false, ao);
                 }
                 else
                 {
-                    envRec.CreateMutableBindingAndInitialize(KnownKeys.Arguments, canBeDeleted: false, ao);
+                    env.CreateMutableBindingAndInitialize(KnownKeys.Arguments, canBeDeleted: false, ao);
                 }
             }
 
             if (!canInitializeParametersOnDeclaration)
             {
                 // slower set
-                envRec.AddFunctionParameters(func.Function, argumentsList);
+                env.AddFunctionParameters(_activeEvaluationContext, func.Function, argumentsList);
             }
 
             // Let iteratorRecord be CreateListIteratorRecord(argumentsList).
@@ -906,32 +946,30 @@ namespace Jint
             //     Perform ? IteratorBindingInitialization for formals with iteratorRecord and env as arguments.
 
             EnvironmentRecord varEnv;
-            DeclarativeEnvironmentRecord varEnvRec;
             if (!hasParameterExpressions)
             {
                 // NOTE: Only a single lexical environment is needed for the parameters and top-level vars.
                 for (var i = 0; i < configuration.VarsToInitialize.Count; i++)
                 {
                     var pair = configuration.VarsToInitialize[i];
-                    envRec.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, JsValue.Undefined);
+                    env.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, JsValue.Undefined);
                 }
 
                 varEnv = env;
-                varEnvRec = envRec;
             }
             else
             {
                 // NOTE: A separate Environment Record is needed to ensure that closures created by expressions
                 // in the formal parameter list do not have visibility of declarations in the function body.
-                varEnv = JintEnvironment.NewDeclarativeEnvironment(this, env);
-                varEnvRec = (DeclarativeEnvironmentRecord) varEnv;
+                var varEnvRec = JintEnvironment.NewDeclarativeEnvironment(this, env);
+                varEnv = varEnvRec;
 
                 UpdateVariableEnvironment(varEnv);
 
                 for (var i = 0; i < configuration.VarsToInitialize.Count; i++)
                 {
                     var pair = configuration.VarsToInitialize[i];
-                    var initialValue = pair.InitialValue ?? envRec.GetBindingValue(pair.Name, strict: false);
+                    var initialValue = pair.InitialValue ?? env.GetBindingValue(pair.Name, strict: false);
                     varEnvRec.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, initialValue);
                 }
             }
@@ -953,56 +991,40 @@ namespace Jint
                 lexEnv = varEnv;
             }
 
-            var lexEnvRec = lexEnv;
-
             UpdateLexicalEnvironment(lexEnv);
 
             if (configuration.LexicalDeclarations.Length > 0)
             {
-                InitializeLexicalDeclarations(configuration.LexicalDeclarations, lexEnvRec);
+                foreach (var d in configuration.LexicalDeclarations)
+                {
+                    for (var j = 0; j < d.BoundNames.Count; j++)
+                    {
+                        var dn = d.BoundNames[j];
+                        if (d.Kind == VariableDeclarationKind.Const)
+                        {
+                            lexEnv.CreateImmutableBinding(dn, strict: true);
+                        }
+                        else
+                        {
+                            lexEnv.CreateMutableBinding(dn, canBeDeleted: false);
+                        }
+                    }
+                }
             }
 
             if (configuration.FunctionsToInitialize != null)
             {
-                InitializeFunctions(configuration.FunctionsToInitialize, lexEnv, varEnvRec);
-            }
-
-            return ao;
-        }
-
-        private void InitializeFunctions(
-            LinkedList<JintFunctionDefinition> functionsToInitialize,
-            EnvironmentRecord lexEnv,
-            DeclarativeEnvironmentRecord varEnvRec)
-        {
-            var realm = Realm;
-            foreach (var f in functionsToInitialize)
-            {
-                var fn = f.Function.Id.Name;
-                var fo = realm.Intrinsics.Function.InstantiateFunctionObject(f, lexEnv);
-                varEnvRec.SetMutableBinding(fn, fo, strict: false);
-            }
-        }
-
-        private static void InitializeLexicalDeclarations(
-            JintFunctionDefinition.State.LexicalVariableDeclaration[] lexicalDeclarations,
-            EnvironmentRecord lexEnvRec)
-        {
-            foreach (var d in lexicalDeclarations)
-            {
-                for (var j = 0; j < d.BoundNames.Count; j++)
+                var privateEnv = calleeContext.PrivateEnvironment;
+                var realm = Realm;
+                foreach (var f in configuration.FunctionsToInitialize)
                 {
-                    var dn = d.BoundNames[j];
-                    if (d.Kind == VariableDeclarationKind.Const)
-                    {
-                        lexEnvRec.CreateImmutableBinding(dn, strict: true);
-                    }
-                    else
-                    {
-                        lexEnvRec.CreateMutableBinding(dn, canBeDeleted: false);
-                    }
+                    var fn = f.Name;
+                    var fo = realm.Intrinsics.Function.InstantiateFunctionObject(f, lexEnv);
+                    varEnv.SetMutableBinding(fn, fo, strict: false);
                 }
             }
+
+            return ao;
         }
 
         private ArgumentsInstance CreateMappedArgumentsObject(
@@ -1153,7 +1175,7 @@ namespace Jint
 
             foreach (var f in functionsToInitialize)
             {
-                var fn = f.Function.Id.Name;
+                var fn = f.Name;
                 var fo = realm.Intrinsics.Function.InstantiateFunctionObject(f, lexEnv);
                 if (varEnvRec is GlobalEnvironmentRecord ger)
                 {

+ 4 - 11
Jint/EsprimaExtensions.cs

@@ -50,7 +50,8 @@ namespace Jint
                 or Nodes.UnaryExpression
                 or Nodes.MemberExpression)
             {
-                propertyKey = TypeConverter.ToPropertyKey(JintExpression.Build(engine, expression).GetValue());
+                var context = engine._activeEvaluationContext;
+                propertyKey = TypeConverter.ToPropertyKey(JintExpression.Build(engine, expression).GetValue(context).Value);
                 return true;
             }
 
@@ -201,14 +202,6 @@ namespace Jint
                 else if (parameter is AssignmentPattern assignmentPattern)
                 {
                     parameter = assignmentPattern.Left;
-                    if (assignmentPattern.Right is ClassExpression classExpression)
-                    {
-                        // TODO check if there's more generic rule
-                        if (classExpression.Id is not null)
-                        {
-                            target.Add(classExpression.Id.Name!);
-                        }
-                    }
                     continue;
                 }
                 break;
@@ -217,7 +210,7 @@ namespace Jint
 
         internal static void BindingInitialization(
             this Expression? expression,
-            Engine engine,
+            EvaluationContext context,
             JsValue value,
             EnvironmentRecord env)
         {
@@ -229,7 +222,7 @@ namespace Jint
             else if (expression is BindingPattern bindingPattern)
             {
                 BindingPatternAssignmentExpression.ProcessPatterns(
-                    engine,
+                    context,
                     bindingPattern,
                     value,
                     env);

+ 1 - 1
Jint/Native/Array/ArrayConstructor.cs

@@ -281,7 +281,7 @@ namespace Jint.Native.Array
                 newTarget = this;
             }
 
-            var proto = GetPrototypeFromConstructor(
+            var proto = _realm.Intrinsics.Function.GetPrototypeFromConstructor(
                 newTarget,
                 static intrinsics => intrinsics.Array.PrototypeObject);
 

+ 8 - 5
Jint/Native/Function/ClassDefinition.cs

@@ -6,11 +6,12 @@ using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Environments;
+using Jint.Runtime.Interpreter;
 using Jint.Runtime.Interpreter.Expressions;
 
 namespace Jint.Native.Function
 {
-    internal class ClassDefinition
+    internal sealed class ClassDefinition
     {
         private static readonly MethodDefinition _superConstructor;
         private static readonly MethodDefinition _emptyConstructor;
@@ -47,11 +48,13 @@ namespace Jint.Native.Function
         /// https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
         /// </summary>
         public ScriptFunctionInstance BuildConstructor(
-            Engine engine,
+            EvaluationContext context,
             EnvironmentRecord env)
         {
             // A class definition is always strict mode code.
-            using var _ = (new StrictModeScope(true, true));
+            using var _ = new StrictModeScope(true, true);
+
+            var engine = context.Engine;
 
             var classScope = JintEnvironment.NewDeclarativeEnvironment(engine, env);
 
@@ -70,7 +73,7 @@ namespace Jint.Native.Function
             else
             {
                 engine.UpdateLexicalEnvironment(classScope);
-                var superclass = JintExpression.Build(engine, _superClass).GetValue();
+                var superclass = JintExpression.Build(engine, _superClass).GetValue(context).Value;
                 engine.UpdateLexicalEnvironment(env);
 
                 if (superclass.IsNull())
@@ -78,7 +81,7 @@ namespace Jint.Native.Function
                     protoParent = null;
                     constructorParent = engine.Realm.Intrinsics.Function.PrototypeObject;
                 }
-                else if (!superclass.IsConstructor)
+                else if (!superclass!.IsConstructor)
                 {
                     ExceptionHelper.ThrowTypeError(engine.Realm, "super class is not a constructor");
                 }

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

@@ -137,8 +137,8 @@ namespace Jint.Native.Function
                 {
                     Engine.EvalDeclarationInstantiation(script, varEnv, lexEnv, privateEnv, strictEval);
 
-                    var statement = new JintScript(_engine, script);
-                    var result = statement.Execute();
+                    var statement = new JintScript(script);
+                    var result = statement.Execute(_engine._activeEvaluationContext);
                     var value = result.GetValueOrDefault();
 
                     if (result.Type == CompletionType.Throw)

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

@@ -339,15 +339,15 @@ namespace Jint.Native.Function
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal Completion OrdinaryCallEvaluateBody(
+            EvaluationContext context,
             JsValue[] arguments,
             ExecutionContext calleeContext)
         {
             var argumentsInstance = _engine.FunctionDeclarationInstantiation(
                 functionInstance: this,
-                arguments,
-                calleeContext.LexicalEnvironment);
+                arguments);
 
-            var result = _functionDefinition.Execute();
+            var result = _functionDefinition.Execute(context);
             argumentsInstance?.FunctionWasCalled();
 
             return result;

+ 3 - 2
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -69,7 +69,8 @@ namespace Jint.Native.Function
                     OrdinaryCallBindThis(calleeContext, thisArgument);
 
                     // actual call
-                    var result = OrdinaryCallEvaluateBody(arguments, calleeContext);
+                    var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine);
+                    var result = OrdinaryCallEvaluateBody(context, arguments, calleeContext);
 
                     if (result.Type == CompletionType.Throw)
                     {
@@ -137,7 +138,7 @@ namespace Jint.Native.Function
             {
                 try
                 {
-                    var result = OrdinaryCallEvaluateBody(arguments, calleeContext);
+                    var result = OrdinaryCallEvaluateBody(_engine._activeEvaluationContext, arguments, calleeContext);
 
                     // The DebugHandler needs the current execution context before the return for stepping through the return point
                     if (_engine._isDebugMode && result.Type != CompletionType.Throw)

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

@@ -3,7 +3,7 @@ using Jint.Runtime;
 
 namespace Jint.Native.Iterator
 {
-    public interface IIterator
+    internal interface IIterator
     {
         bool TryIteratorStep(out ObjectInstance nextItem);
         void Close(CompletionType completion);

+ 5 - 5
Jint/Native/Iterator/IteratorInstance.cs

@@ -51,12 +51,12 @@ namespace Jint.Native.Iterator
         {
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-createiterresultobject
+        /// </summary>
         private ObjectInstance CreateIterResultObject(JsValue value, bool done)
         {
-            var obj = _engine.Realm.Intrinsics.Object.Construct(2);
-            obj.SetDataProperty("value", value);
-            obj.SetDataProperty("done", done);
-            return obj;
+            return new IteratorResult(_engine, value, done ? JsBoolean.True :  JsBoolean.False);
         }
 
         internal sealed class KeyValueIteratorPosition : ObjectInstance
@@ -187,7 +187,7 @@ namespace Jint.Native.Iterator
                 }
                 if (completion != CompletionType.Throw && !innerResult.IsObject())
                 {
-                    ExceptionHelper.ThrowTypeError(_target.Engine.Realm);
+                    ExceptionHelper.ThrowTypeError(_target.Engine.Realm, "Iterator returned non-object");
                 }
             }
         }

+ 2 - 2
Jint/Native/Iterator/IteratorProtocol.cs

@@ -28,7 +28,7 @@ namespace Jint.Native.Iterator
             var done = false;
             try
             {
-                do
+                while (ShouldContinue)
                 {
                     if (!_iterator.TryIteratorStep(out var item))
                     {
@@ -39,7 +39,7 @@ namespace Jint.Native.Iterator
                     var currentValue = item.Get(CommonProperties.Value);
 
                     ProcessItem(args, currentValue);
-                } while (ShouldContinue);
+                }
             }
             catch
             {

+ 45 - 0
Jint/Native/Iterator/IteratorResult.cs

@@ -0,0 +1,45 @@
+using Jint.Native.Object;
+using Jint.Runtime;
+
+namespace Jint.Native.Iterator
+{
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-createiterresultobject
+    /// </summary>
+    internal sealed class IteratorResult : ObjectInstance
+    {
+        private readonly JsValue _value;
+        private readonly JsBoolean _done;
+
+        public IteratorResult(Engine engine, JsValue value, JsBoolean done) : base(engine)
+        {
+            _value = value;
+            _done = done;
+        }
+
+        public override JsValue Get(JsValue property, JsValue receiver)
+        {
+            if (property == CommonProperties.Value)
+            {
+                return _value;
+            }
+
+            if (property == CommonProperties.Done)
+            {
+                return _done;
+            }
+
+            return base.Get(property, receiver);
+        }
+
+        public override object ToObject()
+        {
+            return this;
+        }
+
+        public override bool Equals(JsValue other)
+        {
+            return ReferenceEquals(this, other);
+        }
+    }
+}

+ 5 - 8
Jint/Native/JsValue.cs

@@ -128,16 +128,13 @@ namespace Jint.Native
         /// <summary>
         /// Invoke the current value as function.
         /// </summary>
+        /// <param name="engine">The engine handling the invoke.</param>
         /// <param name="arguments">The arguments of the function call.</param>
         /// <returns>The value returned by the function call.</returns>
-        public JsValue Invoke(params JsValue[] arguments)
+        [Obsolete("Should use Engine.Invoke when direct invoking is needed.")]
+        public JsValue Invoke(Engine engine, params JsValue[] arguments)
         {
-            var callable = this as ICallable;
-            if (callable is null)
-            {
-                ExceptionHelper.ThrowTypeErrorNoEngine("Can only invoke functions");
-            }
-            return callable.Call(Undefined, arguments);
+            return engine.Invoke(this, arguments);
         }
 
         public virtual bool HasOwnProperty(JsValue property) => false;
@@ -293,7 +290,7 @@ namespace Jint.Native
             return _type.GetHashCode();
         }
 
-        internal class JsValueDebugView
+        internal sealed class JsValueDebugView
         {
             public string Value;
 

+ 3 - 3
Jint/Native/Map/MapPrototype.cs

@@ -33,7 +33,7 @@ namespace Jint.Native.Map
                 ["constructor"] = new PropertyDescriptor(_mapConstructor, PropertyFlag.NonEnumerable),
                 ["clear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "clear", Clear, 0, PropertyFlag.Configurable), propertyFlags),
                 ["delete"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "delete", Delete, 1, PropertyFlag.Configurable), propertyFlags),
-                ["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Iterator, 0, PropertyFlag.Configurable), propertyFlags),
+                ["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Entries, 0, PropertyFlag.Configurable), propertyFlags),
                 ["forEach"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), propertyFlags),
                 ["get"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "get", Get, 1, PropertyFlag.Configurable), propertyFlags),
                 ["has"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "has", Has, 1, PropertyFlag.Configurable), propertyFlags),
@@ -46,7 +46,7 @@ namespace Jint.Native.Map
 
             var symbols = new SymbolDictionary(2)
             {
-                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Iterator, 1, PropertyFlag.Configurable), propertyFlags),
+                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Entries, 1, PropertyFlag.Configurable), propertyFlags),
                 [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("Map", false, false, true),
             };
             SetSymbols(symbols);
@@ -107,7 +107,7 @@ namespace Jint.Native.Map
             return Undefined;
         }
 
-        private ObjectInstance Iterator(JsValue thisObj, JsValue[] arguments)
+        private ObjectInstance Entries(JsValue thisObj, JsValue[] arguments)
         {
             var map = AssertMapInstance(thisObj);
             return map.Iterator();

+ 1 - 1
Jint/Native/Number/Dtoa/CachePowers.cs

@@ -32,7 +32,7 @@ using System.Diagnostics;
 
 namespace Jint.Native.Number.Dtoa
 {
-    internal class CachedPowers
+    internal sealed class CachedPowers
     {
         private const double Kd1Log210 = 0.30102999566398114; //  1 / lg(10)
 

+ 1 - 1
Jint/Native/Number/Dtoa/DoubleHelper.cs

@@ -35,7 +35,7 @@ namespace Jint.Native.Number.Dtoa
     /// <summary>
     /// Helper functions for doubles.
     /// </summary>
-    internal class DoubleHelper
+    internal sealed class DoubleHelper
     {
         internal const ulong KExponentMask = 0x7FF0000000000000L;
         internal const ulong KSignificandMask = 0x000FFFFFFFFFFFFFL;

+ 1 - 1
Jint/Native/Number/Dtoa/DtoaBuilder.cs

@@ -4,7 +4,7 @@
 
 namespace Jint.Native.Number.Dtoa
 {
-    internal class DtoaBuilder
+    internal sealed class DtoaBuilder
     {
         // allocate buffer for generated digits + extra notation + padding zeroes
         internal readonly char[] _chars;

+ 26 - 0
Jint/Native/RegExp/RegExpStringIteratorPrototypeObject.cs

@@ -0,0 +1,26 @@
+using Jint.Native.Iterator;
+using Jint.Native.Object;
+using Jint.Runtime;
+
+namespace Jint.Native.RegExp
+{
+    internal sealed class RegExpStringIteratorPrototypeObject : IteratorPrototype
+    {
+        internal RegExpStringIteratorPrototypeObject(
+            Engine engine,
+            Realm realm,
+            ObjectPrototype objectPrototype) : base(engine, realm, "RegExp String Iterator", objectPrototype)
+        {
+        }
+
+        internal IteratorInstance Construct(ObjectInstance iteratingRegExp, string iteratedString, bool global, bool unicode)
+        {
+            var instance = new IteratorInstance.RegExpStringIterator(Engine, iteratingRegExp, iteratedString, global, unicode)
+            {
+                _prototype = this
+            };
+
+            return instance;
+        }
+    }
+}

+ 1 - 1
Jint/Native/Set/SetConstructor.cs

@@ -83,7 +83,7 @@ namespace Jint.Native.Set
                             return set;
                         }
 
-                        next.TryGetValue(CommonProperties.Value, out var nextValue);
+                        var nextValue = next.Get(CommonProperties.Value);
                         args[0] = nextValue;
                         adder.Call(set, args);
                     } while (true);

+ 1 - 1
Jint/Native/String/StringExecutionContext.cs

@@ -6,7 +6,7 @@ namespace Jint.Native.String
     /// <summary>
     /// Helper to cache common data structures when manipulating strings.
     /// </summary>
-    internal class StringExecutionContext
+    internal sealed class StringExecutionContext
     {
         private static readonly ThreadLocal<StringExecutionContext> _executionContext = new ThreadLocal<StringExecutionContext>(() => new StringExecutionContext());
 

+ 1 - 1
Jint/Native/TypedArray/IntrinsicTypedArrayConstructor.cs

@@ -11,7 +11,7 @@ namespace Jint.Native.TypedArray
     /// <summary>
     /// https://tc39.es/ecma262/#sec-%typedarray%-intrinsic-object
     /// </summary>
-    internal class IntrinsicTypedArrayConstructor : FunctionInstance, IConstructor
+    internal sealed class IntrinsicTypedArrayConstructor : FunctionInstance, IConstructor
     {
         internal IntrinsicTypedArrayConstructor(
             Engine engine,

+ 1 - 1
Jint/Native/WeakMap/WeakMapInstance.cs

@@ -28,7 +28,7 @@ namespace Jint.Native.WeakMap
         {
             if (key.IsPrimitive())
             {
-                ExceptionHelper.ThrowTypeError(_engine.Realm, "WeakMap key must be an object, got " + key.ToString());
+                ExceptionHelper.ThrowTypeError(_engine.Realm, "WeakMap key must be an object, got " + key);
             }
 
 #if NETSTANDARD2_1

+ 1 - 1
Jint/Pooling/ConcurrentObjectPool.cs

@@ -37,7 +37,7 @@ namespace Jint.Pooling
     /// Rationale:
     ///    If there is no intent for reusing the object, do not use pool - just use "new".
     /// </summary>
-    internal class ConcurrentObjectPool<T> where T : class
+    internal sealed class ConcurrentObjectPool<T> where T : class
     {
         [DebuggerDisplay("{Value,nq}")]
         private struct Element

+ 1 - 1
Jint/Pooling/ObjectPool.cs

@@ -36,7 +36,7 @@ namespace Jint.Pooling
     /// Rationale: 
     ///    If there is no intent for reusing the object, do not use pool - just use "new". 
     /// </summary>
-    internal class ObjectPool<T> where T : class
+    internal sealed class ObjectPool<T> where T : class
     {
         [DebuggerDisplay("{Value,nq}")]
         private struct Element

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

@@ -12,7 +12,7 @@ namespace Jint.Runtime.CallStack
     {
         public CallStackElement(
             FunctionInstance function,
-            JintExpression expression,
+            JintExpression? expression,
             ExecutionContext callingExecutionContext)
         {
             Function = function;
@@ -52,7 +52,7 @@ namespace Jint.Runtime.CallStack
                     name = JintExpression.ToString(Expression._expression);
                 }
             }
-            
+
             return name ?? "(anonymous)";
         }
     }

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

@@ -10,7 +10,7 @@ using Jint.Pooling;
 
 namespace Jint.Runtime.CallStack
 {
-    internal class JintCallStack
+    internal sealed class JintCallStack
     {
         private readonly RefStack<CallStackElement> _stack = new();
         private readonly Dictionary<CallStackElement, int>? _statistics;

+ 38 - 10
Jint/Runtime/Completion.cs

@@ -1,36 +1,64 @@
-using System.Runtime.CompilerServices;
+#nullable enable
+
+using System.Runtime.CompilerServices;
 using Esprima;
 using Jint.Native;
+using Jint.Runtime.Interpreter.Expressions;
 
 namespace Jint.Runtime
 {
     public enum CompletionType
     {
-        Normal,
+        Normal = 0,
+        Return = 1,
+        Throw = 2,
         Break,
-        Continue,
-        Return,
-        Throw
+        Continue
     }
 
     /// <summary>
-    /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.9
+    /// https://tc39.es/ecma262/#sec-completion-record-specification-type
     /// </summary>
     public readonly struct Completion
     {
-        public Completion(CompletionType type, JsValue value, string identifier, Location location)
+        internal Completion(CompletionType type, JsValue value, string? target, in Location location)
         {
             Type = type;
             Value = value;
-            Identifier = identifier;
+            Target = target;
             Location = location;
         }
 
+        public Completion(CompletionType type, JsValue value, in Location location)
+            : this(type, value, null, location)
+        {
+        }
+
+        public Completion(CompletionType type, string target, in Location location)
+            : this(type, null!, target, location)
+        {
+        }
+
+        internal Completion(in ExpressionResult result)
+        {
+            Type = (CompletionType) result.Type;
+            // this cast protects us from getting from type
+            Value = (JsValue) result.Value;
+            Target = null;
+            Location = result.Location;
+        }
+
         public readonly CompletionType Type;
         public readonly JsValue Value;
-        public readonly string Identifier;
+        public readonly string? Target;
         public readonly Location Location;
 
+        public static Completion Normal(JsValue value, in Location location)
+            => new Completion(CompletionType.Normal, value, location);
+
+        public static Completion Empty()
+            => new Completion(CompletionType.Normal, null!, default);
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue GetValueOrDefault()
         {
@@ -53,7 +81,7 @@ namespace Jint.Runtime
                 return this;
             }
 
-            return new Completion(Type, value, Identifier, Location);
+            return new Completion(Type, value, Target, Location);
         }
     }
 }

+ 4 - 2
Jint/Runtime/Environments/Binding.cs

@@ -1,7 +1,9 @@
-using Jint.Native;
+using System.Diagnostics;
+using Jint.Native;
 
 namespace Jint.Runtime.Environments
 {
+    [DebuggerDisplay("Mutable: {Mutable}, Strict: {Strict}, CanBeDeleted: {CanBeDeleted}, Value: {Value}")]
     public readonly struct Binding
     {
         public Binding(
@@ -26,6 +28,6 @@ namespace Jint.Runtime.Environments
             return new Binding(argument, CanBeDeleted, Mutable, Strict);
         }
 
-        public bool IsInitialized() => !(Value is null);
+        public bool IsInitialized() => Value is not null;
     }
 }

+ 9 - 18
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -10,7 +10,7 @@ namespace Jint.Runtime.Environments
     /// </summary>
     internal class DeclarativeEnvironmentRecord : EnvironmentRecord
     {
-        internal readonly HybridDictionary<Binding> _dictionary = new HybridDictionary<Binding>();
+        internal readonly HybridDictionary<Binding> _dictionary = new();
         internal bool _hasBindings;
         internal readonly bool _catchEnvironment;
 
@@ -32,7 +32,7 @@ namespace Jint.Runtime.Environments
         {
             binding = default;
             var success = _dictionary.TryGetValue(name.Key, out binding);
-            value = success && binding.IsInitialized() ? UnwrapBindingValue(strict, binding) : default;
+            value = success && binding.IsInitialized() ? binding.Value : default;
             return success;
         }
 
@@ -113,28 +113,19 @@ namespace Jint.Runtime.Environments
         public sealed override JsValue GetBindingValue(string name, bool strict)
         {
             _dictionary.TryGetValue(name, out var binding);
-            return UnwrapBindingValue(strict, binding);
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private JsValue UnwrapBindingValue(bool strict, in Binding binding)
-        {
-            if (!binding.Mutable && !binding.IsInitialized())
+            if (binding.IsInitialized())
             {
-                if (strict)
-                {
-                    ThrowUninitializedBindingException();
-                }
-
-                return Undefined;
+                return binding.Value;
             }
 
-            return binding.Value;
+            ThrowUninitializedBindingError(name);
+            return null;
         }
 
-        private void ThrowUninitializedBindingException()
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private void ThrowUninitializedBindingError(string name)
         {
-            throw new JavaScriptException(_engine.Realm.Intrinsics.ReferenceError, "Can't access an uninitialized immutable binding.");
+            throw new JavaScriptException(_engine.Realm.Intrinsics.ReferenceError, $"Cannot access '{name}' before initialization");
         }
 
         public sealed override bool DeleteBinding(string name)

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

@@ -1,5 +1,6 @@
 #nullable enable
 
+using System.Diagnostics;
 using Jint.Native;
 
 namespace Jint.Runtime.Environments
@@ -108,6 +109,7 @@ namespace Jint.Runtime.Environments
         /// <summary>
         /// Helper to cache JsString/Key when environments use different lookups.
         /// </summary>
+        [DebuggerDisplay("\"{Key.Name}\"")]
         internal readonly struct BindingName
         {
             public readonly Key Key;

+ 33 - 22
Jint/Runtime/Environments/FunctionEnvironmentRecord.cs

@@ -6,6 +6,7 @@ using Jint.Native.Array;
 using Jint.Native.Function;
 using Jint.Native.Iterator;
 using Jint.Native.Object;
+using Jint.Runtime.Interpreter;
 using Jint.Runtime.Interpreter.Expressions;
 
 namespace Jint.Runtime.Environments
@@ -33,7 +34,7 @@ namespace Jint.Runtime.Environments
         {
             _functionObject = functionObject;
             NewTarget = newTarget;
-            if (functionObject._functionDefinition.Function is ArrowFunctionExpression)
+            if (functionObject._functionDefinition?.Function is ArrowFunctionExpression)
             {
                 _thisBindingStatus = ThisBindingStatus.Lexical;
             }
@@ -104,19 +105,20 @@ namespace Jint.Runtime.Environments
             }
         }
 
-        internal void AddFunctionParameters(IFunction functionDeclaration, JsValue[] arguments)
+        internal void AddFunctionParameters(EvaluationContext context, IFunction functionDeclaration, JsValue[] arguments)
         {
             bool empty = _dictionary.Count == 0;
             ref readonly var parameters = ref functionDeclaration.Params;
             var count = parameters.Count;
             for (var i = 0; i < count; i++)
             {
-                SetFunctionParameter(parameters[i], arguments, i, empty);
+                SetFunctionParameter(context, parameters[i], arguments, i, empty);
             }
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private void SetFunctionParameter(
+            EvaluationContext context,
             Node parameter,
             JsValue[] arguments,
             int index,
@@ -129,11 +131,12 @@ namespace Jint.Runtime.Environments
             }
             else
             {
-                SetFunctionParameterUnlikely(parameter, arguments, index, initiallyEmpty);
+                SetFunctionParameterUnlikely(context, parameter, arguments, index, initiallyEmpty);
             }
         }
 
         private void SetFunctionParameterUnlikely(
+            EvaluationContext context,
             Node parameter,
             JsValue[] arguments,
             int index,
@@ -143,27 +146,27 @@ namespace Jint.Runtime.Environments
 
             if (parameter is RestElement restElement)
             {
-                HandleRestElementArray(restElement, arguments, index, initiallyEmpty);
+                HandleRestElementArray(context, restElement, arguments, index, initiallyEmpty);
             }
             else if (parameter is ArrayPattern arrayPattern)
             {
-                HandleArrayPattern(initiallyEmpty, argument, arrayPattern);
+                HandleArrayPattern(context, initiallyEmpty, argument, arrayPattern);
             }
             else if (parameter is ObjectPattern objectPattern)
             {
-                HandleObjectPattern(initiallyEmpty, argument, objectPattern);
+                HandleObjectPattern(context, initiallyEmpty, argument, objectPattern);
             }
             else if (parameter is AssignmentPattern assignmentPattern)
             {
-                HandleAssignmentPatternOrExpression(assignmentPattern.Left, assignmentPattern.Right, argument, initiallyEmpty);
+                HandleAssignmentPatternOrExpression(context, assignmentPattern.Left, assignmentPattern.Right, argument, initiallyEmpty);
             }
             else if (parameter is AssignmentExpression assignmentExpression)
             {
-                HandleAssignmentPatternOrExpression(assignmentExpression.Left, assignmentExpression.Right, argument, initiallyEmpty);
+                HandleAssignmentPatternOrExpression(context, assignmentExpression.Left, assignmentExpression.Right, argument, initiallyEmpty);
             }
         }
 
-        private void HandleObjectPattern(bool initiallyEmpty, JsValue argument, ObjectPattern objectPattern)
+        private void HandleObjectPattern(EvaluationContext context, bool initiallyEmpty, JsValue argument, ObjectPattern objectPattern)
         {
             if (argument.IsNullOrUndefined())
             {
@@ -205,8 +208,8 @@ namespace Jint.Runtime.Environments
                         }
                         else if (p.Key is CallExpression callExpression)
                         {
-                            var jintCallExpression = new JintCallExpression(_engine, callExpression);
-                            var jsValue = jintCallExpression.GetValue();
+                            var jintCallExpression = new JintCallExpression(callExpression);
+                            var jsValue = jintCallExpression.GetValue(context).Value;
                             propertyName = TypeConverter.ToJsString(jsValue);
                         }
                         else
@@ -216,7 +219,7 @@ namespace Jint.Runtime.Environments
 
                         processedProperties?.Add(propertyName.ToString());
                         jsValues[0] = argumentObject.Get(propertyName);
-                        SetFunctionParameter(p.Value, jsValues, 0, initiallyEmpty);
+                        SetFunctionParameter(context, p.Value, jsValues, 0, initiallyEmpty);
                     }
                     else
                     {
@@ -241,7 +244,7 @@ namespace Jint.Runtime.Environments
             _engine._jsValueArrayPool.ReturnArray(jsValues);
         }
 
-        private void HandleArrayPattern(bool initiallyEmpty, JsValue argument, ArrayPattern arrayPattern)
+        private void HandleArrayPattern(EvaluationContext context, bool initiallyEmpty, JsValue argument, ArrayPattern arrayPattern)
         {
             if (argument.IsNull())
             {
@@ -257,7 +260,13 @@ namespace Jint.Runtime.Environments
             else if (argument.IsObject() && argument.TryGetIterator(_functionObject._realm, out var iterator))
             {
                 array = _engine.Realm.Intrinsics.Array.ConstructFast(0);
-                var protocol = new ArrayPatternProtocol(_engine, array, iterator, arrayPattern.Elements.Count);
+                var max = arrayPattern.Elements.Count;
+                if (max > 0 && arrayPattern.Elements[max - 1]?.Type == Nodes.RestElement)
+                {
+                    // need to consume all
+                    max = int.MaxValue;
+                }
+                var protocol = new ArrayPatternProtocol(_engine, array, iterator, max);
                 protocol.Execute();
             }
 
@@ -271,13 +280,14 @@ namespace Jint.Runtime.Environments
                 }
             }
 
-            for (uint arrayIndex = 0; arrayIndex < arrayPattern.Elements.Count; arrayIndex++)
+            for (var i = 0; i < arrayPattern.Elements.Count; i++)
             {
-                SetFunctionParameter(arrayPattern.Elements[(int) arrayIndex], arrayContents, (int) arrayIndex, initiallyEmpty);
+                SetFunctionParameter(context, arrayPattern.Elements[i], arrayContents, i, initiallyEmpty);
             }
         }
 
         private void HandleRestElementArray(
+            EvaluationContext context,
             RestElement restElement,
             JsValue[] arguments,
             int index,
@@ -301,7 +311,7 @@ namespace Jint.Runtime.Environments
             }
             else if (restElement.Argument is BindingPattern bindingPattern)
             {
-                SetFunctionParameter(bindingPattern, new JsValue[]
+                SetFunctionParameter(context, bindingPattern, new JsValue[]
                 {
                     rest
                 }, index, initiallyEmpty);
@@ -313,6 +323,7 @@ namespace Jint.Runtime.Environments
         }
 
         private void HandleAssignmentPatternOrExpression(
+            EvaluationContext context,
             Node left,
             Node right,
             JsValue argument,
@@ -337,7 +348,7 @@ namespace Jint.Runtime.Environments
                 _engine.EnterExecutionContext(new ExecutionContext(paramVarEnv, paramVarEnv, null, _engine.Realm, null));
                 try
                 {
-                    argument = jintExpression.GetValue();
+                    argument = jintExpression.GetValue(context).Value;
                 }
                 finally
                 {
@@ -350,7 +361,7 @@ namespace Jint.Runtime.Environments
                 }
             }
 
-            SetFunctionParameter(left, new[]
+            SetFunctionParameter(context, left, new[]
             {
                 argument
             }, 0, initiallyEmpty);
@@ -392,7 +403,7 @@ namespace Jint.Runtime.Environments
         {
             private readonly ArrayInstance _instance;
             private readonly int _max;
-            private long _index = -1;
+            private long _index = 0;
 
             public ArrayPatternProtocol(
                 Engine engine,
@@ -414,7 +425,7 @@ namespace Jint.Runtime.Environments
 
             protected override void IterationEnd()
             {
-                if (_index >= 0)
+                if (_index > 0)
                 {
                     _instance.SetLength((uint) _index);
                     IteratorClose(CompletionType.Normal);

+ 3 - 3
Jint/Runtime/Environments/JintEnvironment.cs

@@ -86,14 +86,14 @@ namespace Jint.Runtime.Environments
             {
                 _outerEnv = outer
             };
-        } 
+        }
 
         /// <summary>
         /// https://tc39.es/ecma262/#sec-newprivateenvironment
         /// </summary>
-        internal static PrivateEnvironmentRecord NewPrivateEnvironment(Engine engine, PrivateEnvironmentRecord outerPriv)
+        internal static PrivateEnvironmentRecord NewPrivateEnvironment(Engine engine, PrivateEnvironmentRecord? outerPriv)
         {
             return new PrivateEnvironmentRecord(outerPriv);
-        } 
+        }
     }
 }

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

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

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

@@ -7,7 +7,7 @@ using Jint.Extensions;
 
 namespace Jint.Runtime.Interop
 {
-    internal class MethodDescriptor
+    internal sealed class MethodDescriptor
     {
         internal MethodDescriptor(MethodBase method)
         {

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

@@ -48,7 +48,7 @@ namespace Jint.Runtime.Interop
             var converter = Engine.ClrTypeConverter;
 
             object[] parameters = null;
-            foreach (var tuple in TypeConverter.FindBestMatch(_engine, _methods, ArgumentProvider))
+            foreach (var tuple in TypeConverter.FindBestMatch(_methods, ArgumentProvider))
             {
                 var method = tuple.Item1;
                 var arguments = tuple.Item2;

+ 1 - 1
Jint/Runtime/Interop/Reflection/ExtensionMethodCache.cs

@@ -10,7 +10,7 @@ namespace Jint.Runtime.Interop.Reflection
     /// <summary>
     /// A extension method lookup that can be shared between engines, build based on extension methods provided via options.
     /// </summary>
-    internal class ExtensionMethodCache
+    internal sealed class ExtensionMethodCache
     {
         internal static readonly ExtensionMethodCache Empty = new(new Dictionary<Type, MethodInfo[]>());
 

+ 15 - 1
Jint/Runtime/Interop/Reflection/IndexerAccessor.cs

@@ -40,7 +40,21 @@ namespace Jint.Runtime.Interop.Reflection
 
             IndexerAccessor ComposeIndexerFactory(PropertyInfo candidate, Type paramType)
             {
-                if (engine.ClrTypeConverter.TryConvert(propertyName, paramType, CultureInfo.InvariantCulture, out var key))
+                object key = null;
+                // int key is quite common case
+                if (paramType == typeof(int))
+                {
+                    if (int.TryParse(propertyName, out var intValue))
+                    {
+                        key = intValue;
+                    }
+                }
+                else
+                {
+                    engine.ClrTypeConverter.TryConvert(propertyName, paramType, CultureInfo.InvariantCulture, out key);
+                }
+
+                if (key is not null)
                 {
                     // the key can be converted for this indexer
                     var indexerProperty = candidate;

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

@@ -6,7 +6,7 @@ using System.Reflection;
 
 namespace Jint.Runtime.Interop
 {
-    internal class TypeDescriptor
+    internal sealed class TypeDescriptor
     {
         private static readonly ConcurrentDictionary<Type, TypeDescriptor> _cache = new();
 

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

@@ -55,7 +55,7 @@ namespace Jint.Runtime.Interop
                 ReferenceType,
                 t => MethodDescriptor.Build(t.GetConstructors(BindingFlags.Public | BindingFlags.Instance)));
 
-            foreach (var tuple in TypeConverter.FindBestMatch(_engine, constructors, _ => arguments))
+            foreach (var tuple in TypeConverter.FindBestMatch(constructors, _ => arguments))
             {
                 var method = tuple.Item1;
                 var retVal = method.Call(Engine, null, arguments);

+ 25 - 0
Jint/Runtime/Interpreter/EvaluationContext.cs

@@ -0,0 +1,25 @@
+using Esprima.Ast;
+
+namespace Jint.Runtime.Interpreter
+{
+    /// <summary>
+    /// Per Engine.Evalute() call context.
+    /// </summary>
+    internal sealed class EvaluationContext
+    {
+        public EvaluationContext(Engine engine, in Completion? resumedCompletion = null)
+        {
+            Engine = engine;
+            DebugMode = engine._isDebugMode;
+            ResumedCompletion = resumedCompletion ?? default; // TODO later
+            OperatorOverloadingAllowed = engine.Options.Interop.OperatorOverloadingAllowed;
+        }
+
+        public Engine Engine { get; }
+        public Completion ResumedCompletion { get; }
+        public bool DebugMode { get; }
+
+        public Node LastSyntaxNode { get; set; }
+        public bool OperatorOverloadingAllowed { get; }
+    }
+}

+ 90 - 55
Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs

@@ -15,28 +15,36 @@ namespace Jint.Runtime.Interpreter.Expressions
         private readonly BindingPattern _pattern;
         private JintExpression _right;
 
-        public BindingPatternAssignmentExpression(
-            Engine engine,
-            AssignmentExpression expression) : base(engine, expression)
+        public BindingPatternAssignmentExpression(AssignmentExpression expression) : base(expression)
         {
             _pattern = (BindingPattern) expression.Left;
             _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
-            _right = Build(_engine, ((AssignmentExpression) _expression).Right);
+            _right = Build(context.Engine, ((AssignmentExpression) _expression).Right);
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            var rightValue = _right.GetValue();
-            ProcessPatterns(_engine, _pattern, rightValue, null);
+            var rightValue = _right.GetValue(context);
+            if (rightValue.IsAbrupt())
+            {
+                return rightValue;
+            }
+
+            var completion = ProcessPatterns(context, _pattern, rightValue.Value, null);
+            if (completion.IsAbrupt())
+            {
+                return completion;
+            }
+
             return rightValue;
         }
 
-        internal static void ProcessPatterns(
-            Engine engine,
+        internal static Completion ProcessPatterns(
+            EvaluationContext context,
             BindingPattern pattern,
             JsValue argument,
             EnvironmentRecord environment,
@@ -44,12 +52,16 @@ namespace Jint.Runtime.Interpreter.Expressions
         {
             if (pattern is ArrayPattern ap)
             {
-                HandleArrayPattern(engine, ap, argument, environment);
+                return HandleArrayPattern(context, ap, argument, environment);
             }
-            else if (pattern is ObjectPattern op)
+
+            if (pattern is ObjectPattern op)
             {
-                HandleObjectPattern(engine, op, argument, environment, checkObjectPatternPropertyReference);
+                return HandleObjectPattern(context, op, argument, environment, checkObjectPatternPropertyReference);
             }
+
+            ExceptionHelper.ThrowArgumentException("Not a pattern");
+            return default;
         }
 
         private static bool ConsumeFromIterator(IIterator it, out JsValue value, out bool done)
@@ -63,12 +75,17 @@ namespace Jint.Runtime.Interpreter.Expressions
                 return false;
             }
 
-            d.TryGetValue(CommonProperties.Value, out value);
+            value = d.Get(CommonProperties.Value);
             return true;
         }
 
-        private static void HandleArrayPattern(Engine engine, ArrayPattern pattern, JsValue argument, EnvironmentRecord environment)
+        private static Completion HandleArrayPattern(
+            EvaluationContext context,
+            ArrayPattern pattern,
+            JsValue argument,
+            EnvironmentRecord environment)
         {
+            var engine = context.Engine;
             var realm = engine.Realm;
             var obj = TypeConverter.ToObject(realm, argument);
             ArrayOperations arrayOperations = null;
@@ -81,11 +98,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
             else
             {
-                if (!obj.TryGetIterator(realm, out iterator))
-                {
-                    ExceptionHelper.ThrowTypeError(realm);
-                    return;
-                }
+                iterator = obj.GetIterator(realm);
             }
 
             var completionType = CompletionType.Normal;
@@ -94,9 +107,10 @@ namespace Jint.Runtime.Interpreter.Expressions
             uint i = 0;
             try
             {
-                for (; i < pattern.Elements.Count; i++)
+                ref readonly var elements = ref pattern.Elements;
+                for (; i < elements.Count; i++)
                 {
-                    var left = pattern.Elements[(int) i];
+                    var left = elements[(int) i];
 
                     if (left is null)
                     {
@@ -124,10 +138,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         }
                         else
                         {
-                            if (!ConsumeFromIterator(iterator, out value, out done))
-                            {
-                                break;
-                            }
+                            ConsumeFromIterator(iterator, out value, out done);
                         }
 
                         AssignToIdentifier(engine, identifier.Name, value, environment);
@@ -135,7 +146,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     else if (left is MemberExpression me)
                     {
                         close = true;
-                        var reference = GetReferenceFromMember(engine, me);
+                        var reference = GetReferenceFromMember(context, me);
                         JsValue value;
                         if (arrayOperations != null)
                         {
@@ -160,7 +171,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                             iterator.TryIteratorStep(out var temp);
                             value = temp;
                         }
-                        ProcessPatterns(engine, bindingPattern, value, environment);
+                        ProcessPatterns(context, bindingPattern, value, environment);
                     }
                     else if (left is RestElement restElement)
                     {
@@ -168,7 +179,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         Reference reference = null;
                         if (restElement.Argument is MemberExpression memberExpression)
                         {
-                            reference = GetReferenceFromMember(engine, memberExpression);
+                            reference = GetReferenceFromMember(context, memberExpression);
                         }
 
                         ArrayInstance array;
@@ -208,7 +219,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         }
                         else if (restElement.Argument is BindingPattern bp)
                         {
-                            ProcessPatterns(engine, bp, array, environment);
+                            ProcessPatterns(context, bp, array, environment);
                         }
                         else
                         {
@@ -230,7 +241,12 @@ namespace Jint.Runtime.Interpreter.Expressions
                         if (value.IsUndefined())
                         {
                             var jintExpression = Build(engine, assignmentPattern.Right);
-                            value = jintExpression.GetValue();
+                            var completion = jintExpression.GetValue(context);
+                            if (completion.IsAbrupt())
+                            {
+                                return completion;
+                            }
+                            value = completion.Value;
                         }
 
                         if (assignmentPattern.Left is Identifier leftIdentifier)
@@ -244,7 +260,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         }
                         else if (assignmentPattern.Left is BindingPattern bp)
                         {
-                            ProcessPatterns(engine, bp, value, environment);
+                            ProcessPatterns(context, bp, value, environment);
                         }
                     }
                     else
@@ -269,15 +285,22 @@ namespace Jint.Runtime.Interpreter.Expressions
                     iterator?.Close(completionType);
                 }
             }
+
+            return new Completion(CompletionType.Normal, JsValue.Undefined, pattern.Location);
         }
 
-        private static void HandleObjectPattern(Engine engine, ObjectPattern pattern, JsValue argument, EnvironmentRecord environment, bool checkReference)
+        private static Completion HandleObjectPattern(
+            EvaluationContext context,
+            ObjectPattern pattern,
+            JsValue argument,
+            EnvironmentRecord environment,
+            bool checkReference)
         {
             var processedProperties = pattern.Properties.Count > 0 && pattern.Properties[pattern.Properties.Count - 1] is RestElement
                 ? new HashSet<JsValue>()
                 : null;
 
-            var source = TypeConverter.ToObject(engine.Realm, argument);
+            var source = TypeConverter.ToObject(context.Engine.Realm, argument);
             for (var i = 0; i < pattern.Properties.Count; i++)
             {
                 if (pattern.Properties[i] is Property p)
@@ -286,8 +309,13 @@ namespace Jint.Runtime.Interpreter.Expressions
                     var identifier = p.Key as Identifier;
                     if (identifier == null || p.Computed)
                     {
-                        var keyExpression = Build(engine, p.Key);
-                        sourceKey = TypeConverter.ToPropertyKey(keyExpression.GetValue());
+                        var keyExpression = Build(context.Engine, p.Key);
+                        var completion = keyExpression.GetValue(context);
+                        if (completion.IsAbrupt())
+                        {
+                            return completion;
+                        }
+                        sourceKey = TypeConverter.ToPropertyKey(completion.Value);
                     }
                     else
                     {
@@ -300,13 +328,18 @@ namespace Jint.Runtime.Interpreter.Expressions
                         source.TryGetValue(sourceKey, out var value);
                         if (value.IsUndefined())
                         {
-                            var jintExpression = Build(engine, assignmentPattern.Right);
-                            value = jintExpression.GetValue();
+                            var jintExpression = Build(context.Engine, assignmentPattern.Right);
+                            var completion = jintExpression.GetValue(context);
+                            if (completion.IsAbrupt())
+                            {
+                                return completion;
+                            }
+                            value = completion.Value;
                         }
 
                         if (assignmentPattern.Left is BindingPattern bp)
                         {
-                            ProcessPatterns(engine, bp, value, environment);
+                            ProcessPatterns(context, bp, value, environment);
                             continue;
                         }
 
@@ -317,25 +350,25 @@ namespace Jint.Runtime.Interpreter.Expressions
                             ((FunctionInstance) value).SetFunctionName(target.Name);
                         }
 
-                        AssignToIdentifier(engine, target.Name, value, environment);
+                        AssignToIdentifier(context.Engine, target.Name, value, environment);
                     }
                     else if (p.Value is BindingPattern bindingPattern)
                     {
                         source.TryGetValue(sourceKey, out var value);
-                        ProcessPatterns(engine, bindingPattern, value, environment);
+                        ProcessPatterns(context, bindingPattern, value, environment);
                     }
                     else if (p.Value is MemberExpression memberExpression)
                     {
-                        var reference = GetReferenceFromMember(engine, memberExpression);
+                        var reference = GetReferenceFromMember(context, memberExpression);
                         source.TryGetValue(sourceKey, out var value);
-                        AssignToReference(engine, reference, value, environment);
+                        AssignToReference(context.Engine, reference, value, environment);
                     }
                     else
                     {
                         var identifierReference = p.Value as Identifier;
                         var target = identifierReference ?? identifier;
                         source.TryGetValue(sourceKey, out var v);
-                        AssignToIdentifier(engine, target.Name, v, environment, checkReference);
+                        AssignToIdentifier(context.Engine, target.Name, v, environment, checkReference);
                     }
                 }
                 else
@@ -344,20 +377,20 @@ namespace Jint.Runtime.Interpreter.Expressions
                     if (restElement.Argument is Identifier leftIdentifier)
                     {
                         var count = Math.Max(0, source.Properties?.Count ?? 0) - processedProperties.Count;
-                        var rest = engine.Realm.Intrinsics.Object.Construct(count);
+                        var rest = context.Engine.Realm.Intrinsics.Object.Construct(count);
                         source.CopyDataProperties(rest, processedProperties);
-                        AssignToIdentifier(engine, leftIdentifier.Name, rest, environment);
+                        AssignToIdentifier(context.Engine, leftIdentifier.Name, rest, environment);
                     }
                     else if (restElement.Argument is BindingPattern bp)
                     {
-                        ProcessPatterns(engine, bp, argument, environment);
+                        ProcessPatterns(context, bp, argument, environment);
                     }
                     else if (restElement.Argument is MemberExpression memberExpression)
                     {
-                        var left = GetReferenceFromMember(engine, memberExpression);
-                        var rest = engine.Realm.Intrinsics.Object.Construct(0);
+                        var left = GetReferenceFromMember(context, memberExpression);
+                        var rest = context.Engine.Realm.Intrinsics.Object.Construct(0);
                         source.CopyDataProperties(rest, processedProperties);
-                        AssignToReference(engine, left, rest, environment);
+                        AssignToReference(context.Engine, left, rest, environment);
                     }
                     else
                     {
@@ -365,6 +398,8 @@ namespace Jint.Runtime.Interpreter.Expressions
                     }
                 }
             }
+
+            return new Completion(CompletionType.Normal, JsValue.Undefined, pattern.Location);
         }
 
         private static void AssignToReference(
@@ -384,15 +419,15 @@ namespace Jint.Runtime.Interpreter.Expressions
             engine._referencePool.Return(lhs);
         }
 
-        private static Reference GetReferenceFromMember(Engine engine, MemberExpression memberExpression)
+        private static Reference GetReferenceFromMember(EvaluationContext context, MemberExpression memberExpression)
         {
-            var expression = new JintMemberExpression(engine, memberExpression);
-            var reference = expression.Evaluate() as Reference;
+            var expression = new JintMemberExpression(memberExpression);
+            var reference = expression.Evaluate(context).Value as Reference;
             if (reference is null)
             {
-                ExceptionHelper.ThrowReferenceError(engine.Realm, "invalid reference");
+                ExceptionHelper.ThrowReferenceError(context.Engine.Realm, "invalid reference");
             }
-            reference.AssertValid(engine);
+            reference.AssertValid(context.Engine.Realm);
             return reference;
         }
 

+ 11 - 9
Jint/Runtime/Interpreter/Expressions/JintArrayExpression.cs

@@ -10,13 +10,14 @@ namespace Jint.Runtime.Interpreter.Expressions
         private JintExpression[] _expressions;
         private bool _hasSpreads;
 
-        public JintArrayExpression(Engine engine, ArrayExpression expression) : base(engine, expression)
+        public JintArrayExpression(ArrayExpression expression) : base(expression)
         {
             _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
+            var engine = context.Engine;
             var node = (ArrayExpression) _expression;
             _expressions = new JintExpression[node.Elements.Count];
             for (var n = 0; n < _expressions.Length; n++)
@@ -24,7 +25,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var expr = node.Elements[n];
                 if (expr != null)
                 {
-                    var expression = Build(_engine, expr);
+                    var expression = Build(engine, expr);
                     _expressions[n] = expression;
                     _hasSpreads |= expr.Type == Nodes.SpreadElement;
                 }
@@ -34,9 +35,10 @@ namespace Jint.Runtime.Interpreter.Expressions
             _initialized = true;
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            var a = _engine.Realm.Intrinsics.Array.ConstructFast(_hasSpreads ? 0 : (uint) _expressions.Length);
+            var engine = context.Engine;
+            var a = engine.Realm.Intrinsics.Array.ConstructFast(_hasSpreads ? 0 : (uint) _expressions.Length);
 
             uint arrayIndexCounter = 0;
             foreach (var expr in _expressions)
@@ -49,7 +51,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                 if (_hasSpreads && expr is JintSpreadExpression jse)
                 {
-                    jse.GetValueAndCheckIterator(out var objectInstance, out var iterator);
+                    jse.GetValueAndCheckIterator(context, out var objectInstance, out var iterator);
                     // optimize for array
                     if (objectInstance is ArrayInstance ai)
                     {
@@ -62,14 +64,14 @@ namespace Jint.Runtime.Interpreter.Expressions
                     }
                     else
                     {
-                        var protocol = new ArraySpreadProtocol(_engine, a, iterator, arrayIndexCounter);
+                        var protocol = new ArraySpreadProtocol(engine, a, iterator, arrayIndexCounter);
                         protocol.Execute();
                         arrayIndexCounter += protocol._addedCount;
                     }
                 }
                 else
                 {
-                    var value = expr.GetValue();
+                    var value = expr.GetValue(context).Value;
                     a.SetIndexValue(arrayIndexCounter++, value, updateLength: false);
                 }
             }
@@ -79,7 +81,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 a.SetLength(arrayIndexCounter);
             }
 
-            return a;
+            return NormalCompletion(a);
         }
 
         private sealed class ArraySpreadProtocol : IteratorProtocol

+ 8 - 7
Jint/Runtime/Interpreter/Expressions/JintArrowFunctionExpression.cs

@@ -7,24 +7,25 @@ namespace Jint.Runtime.Interpreter.Expressions
     {
         private readonly JintFunctionDefinition _function;
 
-        public JintArrowFunctionExpression(Engine engine, IFunction function)
-            : base(engine, ArrowParameterPlaceHolder.Empty)
+        public JintArrowFunctionExpression(Engine engine, ArrowFunctionExpression function)
+            : base(ArrowParameterPlaceHolder.Empty)
         {
             _function = new JintFunctionDefinition(engine, function);
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            var scope = _engine.ExecutionContext.LexicalEnvironment;
+            var engine = context.Engine;
+            var scope = engine.ExecutionContext.LexicalEnvironment;
 
             var closure = new ScriptFunctionInstance(
-                _engine,
+                engine,
                 _function,
                 scope,
                 FunctionThisMode.Lexical,
-                proto: _engine.Realm.Intrinsics.Function.PrototypeObject);
+                proto: engine.Realm.Intrinsics.Function.PrototypeObject);
 
-            return closure;
+            return NormalCompletion(closure);
         }
     }
 }

+ 64 - 54
Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs

@@ -12,7 +12,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         private readonly JintExpression _right;
         private readonly AssignmentOperator _operator;
 
-        private JintAssignmentExpression(Engine engine, AssignmentExpression expression) : base(engine, expression)
+        private JintAssignmentExpression(Engine engine, AssignmentExpression expression) : base(expression)
         {
             _left = Build(engine, expression.Left);
             _right = Build(engine, expression.Right);
@@ -25,27 +25,28 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
                 if (expression.Left is BindingPattern)
                 {
-                    return new BindingPatternAssignmentExpression(engine, expression);
+                    return new BindingPatternAssignmentExpression(expression);
                 }
 
-                return new SimpleAssignmentExpression(engine, expression);
+                return new SimpleAssignmentExpression(expression);
             }
 
             return new JintAssignmentExpression(engine, expression);
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            var lref = _left.Evaluate() as Reference;
+            var lref = _left.Evaluate(context).Value as Reference;
             if (lref is null)
             {
-                ExceptionHelper.ThrowReferenceError(_engine.Realm, "not a valid reference");
+                ExceptionHelper.ThrowReferenceError(context.Engine.Realm, "not a valid reference");
             }
 
-            var lval = _engine.GetValue(lref, false);
+            var engine = context.Engine;
+            var lval = context.Engine.GetValue(lref, false);
             var handledByOverload = false;
 
-            if (_engine.Options.Interop.OperatorOverloadingAllowed)
+            if (context.OperatorOverloadingAllowed)
             {
                 string operatorClrName = null;
                 switch (_operator)
@@ -91,10 +92,10 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                 if (operatorClrName != null)
                 {
-                    var rval = _right.GetValue();
-                    if (JintBinaryExpression.TryOperatorOverloading(_engine, lval, rval, operatorClrName, out var result))
+                    var rval = _right.GetValue(context).Value;
+                    if (JintBinaryExpression.TryOperatorOverloading(context, lval, rval, operatorClrName, out var result))
                     {
-                        lval = JsValue.FromObject(_engine, result);
+                        lval = JsValue.FromObject(context.Engine, result);
                         handledByOverload = true;
                     }
                 }
@@ -106,7 +107,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 {
                     case AssignmentOperator.PlusAssign:
                     {
-                        var rval = _right.GetValue();
+                        var rval = _right.GetValue(context).Value;
                         if (AreIntegerOperands(lval, rval))
                         {
                             lval = (long) lval.AsInteger() + rval.AsInteger();
@@ -136,7 +137,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                     case AssignmentOperator.MinusAssign:
                     {
-                        var rval = _right.GetValue();
+                        var rval = _right.GetValue(context).Value;
                         lval = AreIntegerOperands(lval, rval)
                             ? JsNumber.Create(lval.AsInteger() - rval.AsInteger())
                             : JsNumber.Create(TypeConverter.ToNumber(lval) - TypeConverter.ToNumber(rval));
@@ -145,7 +146,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                     case AssignmentOperator.TimesAssign:
                     {
-                        var rval = _right.GetValue();
+                        var rval = _right.GetValue(context).Value;
                         if (AreIntegerOperands(lval, rval))
                         {
                             lval = (long) lval.AsInteger() * rval.AsInteger();
@@ -164,14 +165,14 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                     case AssignmentOperator.DivideAssign:
                     {
-                        var rval = _right.GetValue();
+                        var rval = _right.GetValue(context).Value;
                         lval = Divide(lval, rval);
                         break;
                     }
 
                     case AssignmentOperator.ModuloAssign:
                     {
-                        var rval = _right.GetValue();
+                        var rval = _right.GetValue(context).Value;
                         if (lval.IsUndefined() || rval.IsUndefined())
                         {
                             lval = Undefined.Instance;
@@ -186,42 +187,42 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                     case AssignmentOperator.BitwiseAndAssign:
                     {
-                        var rval = _right.GetValue();
+                        var rval = _right.GetValue(context).Value;
                         lval = TypeConverter.ToInt32(lval) & TypeConverter.ToInt32(rval);
                         break;
                     }
 
                     case AssignmentOperator.BitwiseOrAssign:
                     {
-                        var rval = _right.GetValue();
+                        var rval = _right.GetValue(context).Value;
                         lval = TypeConverter.ToInt32(lval) | TypeConverter.ToInt32(rval);
                         break;
                     }
 
                     case AssignmentOperator.BitwiseXOrAssign:
                     {
-                        var rval = _right.GetValue();
+                        var rval = _right.GetValue(context).Value;
                         lval = TypeConverter.ToInt32(lval) ^ TypeConverter.ToInt32(rval);
                         break;
                     }
 
                     case AssignmentOperator.LeftShiftAssign:
                     {
-                        var rval = _right.GetValue();
+                        var rval = _right.GetValue(context).Value;
                         lval = TypeConverter.ToInt32(lval) << (int) (TypeConverter.ToUint32(rval) & 0x1F);
                         break;
                     }
 
                     case AssignmentOperator.RightShiftAssign:
                     {
-                        var rval = _right.GetValue();
+                        var rval = _right.GetValue(context).Value;
                         lval = TypeConverter.ToInt32(lval) >> (int) (TypeConverter.ToUint32(rval) & 0x1F);
                         break;
                     }
 
                     case AssignmentOperator.UnsignedRightShiftAssign:
                     {
-                        var rval = _right.GetValue();
+                        var rval = _right.GetValue(context).Value;
                         lval = (uint) TypeConverter.ToInt32(lval) >> (int) (TypeConverter.ToUint32(rval) & 0x1F);
                         break;
                     }
@@ -230,10 +231,10 @@ namespace Jint.Runtime.Interpreter.Expressions
                     {
                         if (!lval.IsNullOrUndefined())
                         {
-                            return lval;
+                            return NormalCompletion(lval);
                         }
 
-                        var rval = NamedEvaluation(_right);
+                        var rval = NamedEvaluation(context, _right);
                         lval = rval;
                         break;
                     }
@@ -242,10 +243,10 @@ namespace Jint.Runtime.Interpreter.Expressions
                     {
                         if (!TypeConverter.ToBoolean(lval))
                         {
-                            return lval;
+                            return NormalCompletion(lval);
                         }
 
-                        var rval = NamedEvaluation(_right);
+                        var rval = NamedEvaluation(context, _right);
                         lval = rval;
                         break;
                     }
@@ -254,29 +255,29 @@ namespace Jint.Runtime.Interpreter.Expressions
                     {
                         if (TypeConverter.ToBoolean(lval))
                         {
-                            return lval;
+                            return NormalCompletion(lval);
                         }
 
-                        var rval = NamedEvaluation(_right);
+                        var rval = NamedEvaluation(context, _right);
                         lval = rval;
                         break;
                     }
 
                     default:
                         ExceptionHelper.ThrowNotImplementedException();
-                        return null;
+                        return default;
                 }
             }
 
-            _engine.PutValue(lref, lval);
+            engine.PutValue(lref, lval);
 
-            _engine._referencePool.Return(lref);
-            return lval;
+            engine._referencePool.Return(lref);
+            return NormalCompletion(lval);
         }
 
-        private JsValue NamedEvaluation(JintExpression expression)
+        private JsValue NamedEvaluation(EvaluationContext context, JintExpression expression)
         {
-            var rval = expression.GetValue();
+            var rval = expression.GetValue(context).Value;
             if (expression._expression.IsAnonymousFunctionDefinition() && _left._expression.Type == Nodes.Identifier)
             {
                 ((FunctionInstance) rval).SetFunctionName(((Identifier) _left._expression).Name);
@@ -293,54 +294,57 @@ namespace Jint.Runtime.Interpreter.Expressions
             private JintIdentifierExpression _leftIdentifier;
             private bool _evalOrArguments;
 
-            public SimpleAssignmentExpression(Engine engine, AssignmentExpression expression) : base(engine, expression)
+            public SimpleAssignmentExpression(AssignmentExpression expression) : base(expression)
             {
                 _initialized = false;
             }
 
-            protected override void Initialize()
+            protected override void Initialize(EvaluationContext context)
             {
                 var assignmentExpression = ((AssignmentExpression) _expression);
-                _left = Build(_engine, assignmentExpression.Left);
+                _left = Build(context.Engine, assignmentExpression.Left);
                 _leftIdentifier = _left as JintIdentifierExpression;
                 _evalOrArguments = _leftIdentifier?.HasEvalOrArguments == true;
 
-                _right = Build(_engine, assignmentExpression.Right);
+                _right = Build(context.Engine, assignmentExpression.Right);
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                JsValue rval = null;
+                ExpressionResult? completion = null;
                 if (_leftIdentifier != null)
                 {
-                    rval = AssignToIdentifier(_engine, _leftIdentifier, _right, _evalOrArguments);
+                    completion = AssignToIdentifier(context, _leftIdentifier, _right, _evalOrArguments);
                 }
-                return rval ?? SetValue();
+                return completion ?? SetValue(context);
             }
 
-            private JsValue SetValue()
+            private ExpressionResult SetValue(EvaluationContext context)
             {
                 // slower version
-                var lref = _left.Evaluate() as Reference;
+                var engine = context.Engine;
+                var lref = _left.Evaluate(context).Value as Reference;
                 if (lref is null)
                 {
-                    ExceptionHelper.ThrowReferenceError(_engine.Realm, "not a valid reference");
+                    ExceptionHelper.ThrowReferenceError(engine.Realm, "not a valid reference");
                 }
-                lref.AssertValid(_engine);
 
-                var rval = _right.GetValue();
+                lref.AssertValid(engine.Realm);
 
-                _engine.PutValue(lref, rval);
-                _engine._referencePool.Return(lref);
-                return rval;
+                var rval = _right.GetValue(context).GetValueOrDefault();
+
+                engine.PutValue(lref, rval);
+                engine._referencePool.Return(lref);
+                return NormalCompletion(rval);
             }
 
-            internal static JsValue AssignToIdentifier(
-                Engine engine,
+            internal static ExpressionResult? AssignToIdentifier(
+                EvaluationContext context,
                 JintIdentifierExpression left,
                 JintExpression right,
                 bool hasEvalOrArguments)
             {
+                var engine = context.Engine;
                 var env = engine.ExecutionContext.LexicalEnvironment;
                 var strict = StrictModeScope.IsStrictModeCode;
                 if (JintEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
@@ -356,7 +360,13 @@ namespace Jint.Runtime.Interpreter.Expressions
                         ExceptionHelper.ThrowSyntaxError(engine.Realm);
                     }
 
-                    var rval = right.GetValue().Clone();
+                    var completion = right.GetValue(context);
+                    if (completion.IsAbrupt())
+                    {
+                        return completion;
+                    }
+
+                    var rval = completion.Value.Clone();
 
                     if (right._expression.IsFunctionDefinition())
                     {
@@ -364,7 +374,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     }
 
                     environmentRecord.SetMutableBinding(left._expressionName, rval, strict);
-                    return rval;
+                    return new Completion(CompletionType.Normal, rval, default);
                 }
 
                 return null;

+ 143 - 170
Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs

@@ -21,18 +21,13 @@ namespace Jint.Runtime.Interpreter.Expressions
         private readonly JintExpression _left;
         private readonly JintExpression _right;
 
-        private JintBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
+        private JintBinaryExpression(Engine engine, BinaryExpression expression) : base(expression)
         {
-            _left = Build(_engine, expression.Left);
-            _right = Build(_engine, expression.Right);
+            _left = Build(engine, expression.Left);
+            _right = Build(engine, expression.Right);
         }
 
-        protected bool TryOperatorOverloading(JsValue left, JsValue right, string clrName, out object result)
-        {
-            return TryOperatorOverloading(_engine, left, right, clrName, out result);
-        }
-
-        internal static bool TryOperatorOverloading(Engine engine, JsValue leftValue, JsValue rightValue, string clrName, out object result)
+        internal static bool TryOperatorOverloading(EvaluationContext context, JsValue leftValue, JsValue rightValue, string clrName, out object result)
         {
             var left = leftValue.ToObject();
             var right = rightValue.ToObject();
@@ -56,15 +51,16 @@ namespace Jint.Runtime.Interpreter.Expressions
                     var methods = leftMethods.Concat(rightMethods).Where(x => x.Name == clrName && x.GetParameters().Length == 2);
                     var _methods = MethodDescriptor.Build(methods.ToArray());
 
-                    return TypeConverter.FindBestMatch(engine, _methods, _ => arguments).FirstOrDefault()?.Item1;
+                    return TypeConverter.FindBestMatch(_methods, _ => arguments).FirstOrDefault()?.Item1;
                 });
 
                 if (method != null)
                 {
-                    result = method.Call(engine, null, arguments);
+                    result = method.Call(context.Engine, null, arguments);
                     return true;
                 }
             }
+
             result = null;
             return false;
         }
@@ -146,22 +142,14 @@ namespace Jint.Runtime.Interpreter.Expressions
                 if (lval is not null && rval is not null)
                 {
                     // we have fixed result
-                    return new JintConstantExpression(engine, expression, result.GetValue());
+                    var context = new EvaluationContext(engine);
+                    return new JintConstantExpression(expression, result.GetValue(context).Value);
                 }
             }
 
             return result;
         }
 
-        public override JsValue GetValue()
-        {
-            // need to notify correct node when taking shortcut
-            _engine._lastSyntaxNode = _expression;
-
-            // we always create a JsValue
-            return (JsValue) EvaluateInternal();
-        }
-
         public static bool SameValueZero(JsValue x, JsValue y)
         {
             return x == y || (x is JsNumber xNum && y is JsNumber yNum && double.IsNaN(xNum._value) && double.IsNaN(yNum._value));
@@ -234,12 +222,12 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
                 var equal = StrictlyEqual(left, right);
-                return equal ? JsBoolean.True : JsBoolean.False;
+                return NormalCompletion(equal ? JsBoolean.True : JsBoolean.False);
             }
         }
 
@@ -249,13 +237,11 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
-                return StrictlyEqual(left, right)
-                    ? JsBoolean.False
-                    : JsBoolean.True;
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
+                return NormalCompletion(StrictlyEqual(left, right) ? JsBoolean.False : JsBoolean.True);
             }
         }
 
@@ -265,21 +251,20 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
 
-                if (_engine.Options.Interop.OperatorOverloadingAllowed
-                    && TryOperatorOverloading(left, right, "op_LessThan", out var opResult))
+                if (context.OperatorOverloadingAllowed
+                    && TryOperatorOverloading(context, left, right, "op_LessThan", out var opResult))
                 {
-                    return opResult;
+                    return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
+
                 var value = Compare(left, right);
 
-                return value._type == InternalTypes.Undefined
-                    ? JsBoolean.False
-                    : value;
+                return NormalCompletion(value._type == InternalTypes.Undefined ? JsBoolean.False : value);
             }
         }
 
@@ -289,22 +274,20 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
 
-                if (_engine.Options.Interop.OperatorOverloadingAllowed
-                    && TryOperatorOverloading(left, right, "op_GreaterThan", out var opResult))
+                if (context.OperatorOverloadingAllowed
+                    && TryOperatorOverloading(context, left, right, "op_GreaterThan", out var opResult))
                 {
-                    return opResult;
+                    return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
 
                 var value = Compare(right, left, false);
 
-                return value._type == InternalTypes.Undefined
-                    ? JsBoolean.False
-                    : value;
+                return NormalCompletion(value._type == InternalTypes.Undefined ? JsBoolean.False : value);
             }
         }
 
@@ -314,49 +297,54 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
 
-                if (_engine.Options.Interop.OperatorOverloadingAllowed
-                    && TryOperatorOverloading(left, right, "op_Addition", out var opResult))
+                if (context.OperatorOverloadingAllowed
+                    && TryOperatorOverloading(context, left, right, "op_Addition", out var opResult))
                 {
-                    return opResult;
+                    return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
 
                 if (AreIntegerOperands(left, right))
                 {
-                    return JsNumber.Create(left.AsInteger() + right.AsInteger());
+                    return NormalCompletion(JsNumber.Create(left.AsInteger() + right.AsInteger()));
                 }
 
                 var lprim = TypeConverter.ToPrimitive(left);
                 var rprim = TypeConverter.ToPrimitive(right);
-                return lprim.IsString() || rprim.IsString()
+                JsValue result = lprim.IsString() || rprim.IsString()
                     ? JsString.Create(TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim))
                     : JsNumber.Create(TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim));
+
+                return NormalCompletion(result);
             }
         }
+
         private sealed class MinusBinaryExpression : JintBinaryExpression
         {
             public MinusBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
             {
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
 
-                if (_engine.Options.Interop.OperatorOverloadingAllowed
-                    && TryOperatorOverloading(left, right, "op_Subtraction", out var opResult))
+                if (context.OperatorOverloadingAllowed
+                    && TryOperatorOverloading(context, left, right, "op_Subtraction", out var opResult))
                 {
-                    return opResult;
+                    return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
 
-                return AreIntegerOperands(left, right)
+                var number = AreIntegerOperands(left, right)
                     ? JsNumber.Create(left.AsInteger() - right.AsInteger())
                     : JsNumber.Create(TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right));
+
+                return NormalCompletion(number);
             }
         }
 
@@ -366,28 +354,28 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
 
-                if (_engine.Options.Interop.OperatorOverloadingAllowed
-                    && TryOperatorOverloading(left, right, "op_Multiply", out var opResult))
+                if (context.OperatorOverloadingAllowed
+                    && TryOperatorOverloading(context, left, right, "op_Multiply", out var opResult))
                 {
-                    return opResult;
+                    return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
 
                 if (AreIntegerOperands(left, right))
                 {
-                    return JsNumber.Create((long) left.AsInteger() * right.AsInteger());
+                    return NormalCompletion(JsNumber.Create((long) left.AsInteger() * right.AsInteger()));
                 }
 
                 if (left.IsUndefined() || right.IsUndefined())
                 {
-                    return Undefined.Instance;
+                    return NormalCompletion(Undefined.Instance);
                 }
 
-                return JsNumber.Create(TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right));
+                return NormalCompletion(JsNumber.Create(TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right)));
             }
         }
 
@@ -397,18 +385,18 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
 
-                if (_engine.Options.Interop.OperatorOverloadingAllowed
-                    && TryOperatorOverloading(left, right, "op_Division", out var opResult))
+                if (context.OperatorOverloadingAllowed
+                    && TryOperatorOverloading(context, left, right, "op_Division", out var opResult))
                 {
-                    return opResult;
+                    return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
 
-                return Divide(left, right);
+                return NormalCompletion(Divide(left, right));
             }
         }
 
@@ -421,20 +409,18 @@ namespace Jint.Runtime.Interpreter.Expressions
                 _invert = invert;
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
 
-                if (_engine.Options.Interop.OperatorOverloadingAllowed
-                    && TryOperatorOverloading(left, right, _invert ? "op_Inequality" : "op_Equality", out var opResult))
+                if (context.OperatorOverloadingAllowed
+                    && TryOperatorOverloading(context, left, right, _invert ? "op_Inequality" : "op_Equality", out var opResult))
                 {
-                    return opResult;
+                    return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
 
-                return Equal(left, right) == !_invert
-                    ? JsBoolean.True
-                    : JsBoolean.False;
+                return NormalCompletion(Equal(left, right) == !_invert ? JsBoolean.True : JsBoolean.False);
             }
         }
 
@@ -447,24 +433,22 @@ namespace Jint.Runtime.Interpreter.Expressions
                 _leftFirst = leftFirst;
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var leftValue = _left.GetValue();
-                var rightValue = _right.GetValue();
+                var leftValue = _left.GetValue(context).Value;
+                var rightValue = _right.GetValue(context).Value;
 
-                if (_engine.Options.Interop.OperatorOverloadingAllowed
-                    && TryOperatorOverloading(leftValue, rightValue, _leftFirst ? "op_GreaterThanOrEqual" : "op_LessThanOrEqual", out var opResult))
+                if (context.OperatorOverloadingAllowed
+                    && TryOperatorOverloading(context, leftValue, rightValue, _leftFirst ? "op_GreaterThanOrEqual" : "op_LessThanOrEqual", out var opResult))
                 {
-                    return opResult;
+                    return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
 
                 var left = _leftFirst ? leftValue : rightValue;
                 var right = _leftFirst ? rightValue : leftValue;
 
                 var value = Compare(left, right, _leftFirst);
-                return value.IsUndefined() || ((JsBoolean) value)._value
-                    ? JsBoolean.False
-                    : JsBoolean.True;
+                return NormalCompletion(value.IsUndefined() || ((JsBoolean) value)._value ? JsBoolean.False : JsBoolean.True);
             }
         }
 
@@ -474,12 +458,11 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var value = _left.GetValue();
-                return value.InstanceofOperator(_right.GetValue())
-                    ? JsBoolean.True
-                    : JsBoolean.False;
+                var leftValue = _left.GetValue(context).Value;
+                var rightValue = _right.GetValue(context).Value;
+                return NormalCompletion(leftValue.InstanceofOperator(rightValue) ? JsBoolean.True : JsBoolean.False);
             }
         }
 
@@ -489,32 +472,33 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
 
-                return JsNumber.Create(Math.Pow(TypeConverter.ToNumber(left), TypeConverter.ToNumber(right)));
+                return NormalCompletion(JsNumber.Create(Math.Pow(TypeConverter.ToNumber(left), TypeConverter.ToNumber(right))));
             }
         }
+
         private sealed class InBinaryExpression : JintBinaryExpression
         {
             public InBinaryExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
             {
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
 
                 var oi = right as ObjectInstance;
                 if (oi is null)
                 {
-                    ExceptionHelper.ThrowTypeError(_engine.Realm, "in can only be used with an object");
+                    ExceptionHelper.ThrowTypeError(context.Engine.Realm, "in can only be used with an object");
                 }
 
-                return oi.HasProperty(left) ? JsBoolean.True : JsBoolean.False;
+                return NormalCompletion(oi.HasProperty(left) ? JsBoolean.True : JsBoolean.False);
             }
         }
 
@@ -524,15 +508,15 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
 
-                if (_engine.Options.Interop.OperatorOverloadingAllowed
-                    && TryOperatorOverloading(left, right, "op_Modulus", out var opResult))
+                if (context.OperatorOverloadingAllowed
+                    && TryOperatorOverloading(context, left, right, "op_Modulus", out var opResult))
                 {
-                    return opResult;
+                    return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
 
                 if (AreIntegerOperands(left, right))
@@ -541,16 +525,16 @@ namespace Jint.Runtime.Interpreter.Expressions
                     var rightInteger = right.AsInteger();
                     if (leftInteger > 0 && rightInteger != 0)
                     {
-                        return JsNumber.Create(leftInteger % rightInteger);
+                        return NormalCompletion(JsNumber.Create(leftInteger % rightInteger));
                     }
                 }
 
                 if (left.IsUndefined() || right.IsUndefined())
                 {
-                    return Undefined.Instance;
+                    return NormalCompletion(Undefined.Instance);
                 }
 
-                return JsNumber.Create(TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right));
+                return NormalCompletion(JsNumber.Create(TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right)));
             }
         }
 
@@ -560,23 +544,16 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
                 get
                 {
-                    switch (_operator)
+                    return _operator switch
                     {
-                        case BinaryOperator.BitwiseAnd:
-                            return "op_BitwiseAnd";
-                        case BinaryOperator.BitwiseOr:
-                            return "op_BitwiseOr";
-                        case BinaryOperator.BitwiseXOr:
-                            return "op_ExclusiveOr";
-                        case BinaryOperator.LeftShift:
-                            return "op_LeftShift";
-                        case BinaryOperator.RightShift:
-                            return "op_RightShift";
-                        case BinaryOperator.UnsignedRightShift:
-                            return "op_UnsignedRightShift";
-                        default:
-                            return null;
-                    }
+                        BinaryOperator.BitwiseAnd => "op_BitwiseAnd",
+                        BinaryOperator.BitwiseOr => "op_BitwiseOr",
+                        BinaryOperator.BitwiseXOr => "op_ExclusiveOr",
+                        BinaryOperator.LeftShift => "op_LeftShift",
+                        BinaryOperator.RightShift => "op_RightShift",
+                        BinaryOperator.UnsignedRightShift => "op_UnsignedRightShift",
+                        _ => null
+                    };
                 }
             }
 
@@ -587,15 +564,15 @@ namespace Jint.Runtime.Interpreter.Expressions
                 _operator = expression.Operator;
             }
 
-            protected override object EvaluateInternal()
+            protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
-                var left = _left.GetValue();
-                var right = _right.GetValue();
+                var left = _left.GetValue(context).Value;
+                var right = _right.GetValue(context).Value;
 
-                if (_engine.Options.Interop.OperatorOverloadingAllowed
-                    && TryOperatorOverloading(left, right, OperatorClrName, out var opResult))
+                if (context.OperatorOverloadingAllowed
+                    && TryOperatorOverloading(context, left, right, OperatorClrName, out var opResult))
                 {
-                    return opResult;
+                    return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
 
                 if (AreIntegerOperands(left, right))
@@ -603,38 +580,40 @@ namespace Jint.Runtime.Interpreter.Expressions
                     int leftValue = left.AsInteger();
                     int rightValue = right.AsInteger();
 
+                    JsValue result;
                     switch (_operator)
                     {
                         case BinaryOperator.BitwiseAnd:
-                            return JsNumber.Create(leftValue & rightValue);
-
+                            result = JsNumber.Create(leftValue & rightValue);
+                            break;
                         case BinaryOperator.BitwiseOr:
-                            return
-                                JsNumber.Create(leftValue | rightValue);
-
+                            result = JsNumber.Create(leftValue | rightValue);
+                            break;
                         case BinaryOperator.BitwiseXOr:
-                            return
-                                JsNumber.Create(leftValue ^ rightValue);
-
+                            result = JsNumber.Create(leftValue ^ rightValue);
+                            break;
                         case BinaryOperator.LeftShift:
-                            return JsNumber.Create(leftValue << (int) ((uint) rightValue & 0x1F));
-
+                            result = JsNumber.Create(leftValue << (int) ((uint) rightValue & 0x1F));
+                            break;
                         case BinaryOperator.RightShift:
-                            return JsNumber.Create(leftValue >> (int) ((uint) rightValue & 0x1F));
-
+                            result = JsNumber.Create(leftValue >> (int) ((uint) rightValue & 0x1F));
+                            break;
                         case BinaryOperator.UnsignedRightShift:
-                            return JsNumber.Create((uint) leftValue >> (int) ((uint) rightValue & 0x1F));
+                            result = JsNumber.Create((uint) leftValue >> (int) ((uint) rightValue & 0x1F));
+                            break;
                         default:
                             ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(_operator), "unknown shift operator");
-                            return null;
+                            result = null;
+                            break;
                     }
 
+                    return NormalCompletion(result);
                 }
 
-                return EvaluateNonInteger(left, right);
+                return NormalCompletion(EvaluateNonInteger(left, right));
             }
 
-            private object EvaluateNonInteger(JsValue left, JsValue right)
+            private JsNumber EvaluateNonInteger(JsValue left, JsValue right)
             {
                 switch (_operator)
                 {
@@ -642,30 +621,24 @@ namespace Jint.Runtime.Interpreter.Expressions
                         return JsNumber.Create(TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right));
 
                     case BinaryOperator.BitwiseOr:
-                        return
-                            JsNumber.Create(TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right));
+                        return JsNumber.Create(TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right));
 
                     case BinaryOperator.BitwiseXOr:
-                        return
-                            JsNumber.Create(TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right));
+                        return JsNumber.Create(TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right));
 
                     case BinaryOperator.LeftShift:
-                        return JsNumber.Create(TypeConverter.ToInt32(left) <<
-                                               (int) (TypeConverter.ToUint32(right) & 0x1F));
+                        return JsNumber.Create(TypeConverter.ToInt32(left) << (int) (TypeConverter.ToUint32(right) & 0x1F));
 
                     case BinaryOperator.RightShift:
-                        return JsNumber.Create(TypeConverter.ToInt32(left) >>
-                                               (int) (TypeConverter.ToUint32(right) & 0x1F));
+                        return JsNumber.Create(TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
 
                     case BinaryOperator.UnsignedRightShift:
-                        return JsNumber.Create((uint) TypeConverter.ToInt32(left) >>
-                                               (int) (TypeConverter.ToUint32(right) & 0x1F));
+                        return JsNumber.Create((uint) TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
                     default:
                         ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(_operator), "unknown shift operator");
                         return null;
                 }
             }
         }
-
     }
 }

+ 40 - 35
Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs

@@ -15,15 +15,16 @@ namespace Jint.Runtime.Interpreter.Expressions
         private JintExpression _calleeExpression;
         private bool _hasSpreads;
 
-        public JintCallExpression(Engine engine, CallExpression expression) : base(engine, expression)
+        public JintCallExpression(CallExpression expression) : base(expression)
         {
             _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
+            var engine = context.Engine;
             var expression = (CallExpression) _expression;
-            _calleeExpression = Build(_engine, expression.Callee);
+            _calleeExpression = Build(engine, expression.Callee);
             var cachedArgumentsHolder = new CachedArgumentsHolder
             {
                 JintArguments = new JintExpression[expression.Arguments.Count]
@@ -39,7 +40,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             for (var i = 0; i < expression.Arguments.Count; i++)
             {
                 var expressionArgument = expression.Arguments[i];
-                cachedArgumentsHolder.JintArguments[i] = Build(_engine, expressionArgument);
+                cachedArgumentsHolder.JintArguments[i] = Build(engine, expressionArgument);
                 cacheable &= expressionArgument.Type == Nodes.Literal;
                 _hasSpreads |= CanSpread(expressionArgument);
                 if (expressionArgument is ArrayExpression ae)
@@ -58,7 +59,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 if (cachedArgumentsHolder.JintArguments.Length > 0)
                 {
                     arguments = new JsValue[cachedArgumentsHolder.JintArguments.Length];
-                    BuildArguments(cachedArgumentsHolder.JintArguments, arguments);
+                    BuildArguments(context, cachedArgumentsHolder.JintArguments, arguments);
                 }
 
                 cachedArgumentsHolder.CachedArguments = arguments;
@@ -67,26 +68,28 @@ namespace Jint.Runtime.Interpreter.Expressions
             _cachedArguments = cachedArgumentsHolder;
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            return _calleeExpression is JintSuperExpression
-                ? SuperCall()
-                : Call();
+            return NormalCompletion(_calleeExpression is JintSuperExpression
+                ? SuperCall(context)
+                : Call(context)
+            );
         }
 
-        private object SuperCall()
+        private JsValue SuperCall(EvaluationContext context)
         {
-            var thisEnvironment = (FunctionEnvironmentRecord) _engine.ExecutionContext.GetThisEnvironment();
-            var newTarget = _engine.GetNewTarget(thisEnvironment);
+            var engine = context.Engine;
+            var thisEnvironment = (FunctionEnvironmentRecord) engine.ExecutionContext.GetThisEnvironment();
+            var newTarget = engine.GetNewTarget(thisEnvironment);
             var func = GetSuperConstructor(thisEnvironment);
             if (!func.IsConstructor)
             {
-                ExceptionHelper.ThrowTypeError(_engine.Realm, "Not a constructor");
+                ExceptionHelper.ThrowTypeError(engine.Realm, "Not a constructor");
             }
 
-            var argList = ArgumentListEvaluation();
+            var argList = ArgumentListEvaluation(context);
             var result = ((IConstructor) func).Construct(argList, newTarget);
-            var thisER = (FunctionEnvironmentRecord) _engine.ExecutionContext.GetThisEnvironment();
+            var thisER = (FunctionEnvironmentRecord) engine.ExecutionContext.GetThisEnvironment();
             return thisER.BindThisValue(result);
         }
 
@@ -104,23 +107,24 @@ namespace Jint.Runtime.Interpreter.Expressions
         /// <summary>
         /// https://tc39.es/ecma262/#sec-function-calls
         /// </summary>
-        private object Call()
+        private JsValue Call(EvaluationContext context)
         {
-            var reference = _calleeExpression.Evaluate();
+            var reference = _calleeExpression.Evaluate(context).Value;
 
             if (ReferenceEquals(reference, Undefined.Instance))
             {
                 return Undefined.Instance;
             }
 
-            var func = _engine.GetValue(reference, false);
+            var engine = context.Engine;
+            var func = engine.GetValue(reference, false);
 
             if (reference is Reference referenceRecord
                 && !referenceRecord.IsPropertyReference()
                 && referenceRecord.GetReferencedName() == CommonProperties.Eval
-                && ReferenceEquals(func, _engine.Realm.Intrinsics.Eval))
+                && ReferenceEquals(func, engine.Realm.Intrinsics.Eval))
             {
-                var argList = ArgumentListEvaluation();
+                var argList = ArgumentListEvaluation(context);
                 if (argList.Length == 0)
                 {
                     return Undefined.Instance;
@@ -132,22 +136,23 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var evalRealm = evalFunctionInstance._realm;
                 var direct = !((CallExpression) _expression).Optional;
                 var value = evalFunctionInstance.PerformEval(evalArg, evalRealm, strictCaller, direct);
-                _engine._referencePool.Return(referenceRecord);
+                engine._referencePool.Return(referenceRecord);
                 return value;
             }
 
             var thisCall = (CallExpression) _expression;
             var tailCall = IsInTailPosition(thisCall);
-            return EvaluateCall(func, reference, thisCall.Arguments, tailCall);
+            return EvaluateCall(context, func, reference, thisCall.Arguments, tailCall);
         }
 
         /// <summary>
         /// https://tc39.es/ecma262/#sec-evaluatecall
         /// </summary>
-        private object EvaluateCall(JsValue func, object reference, in NodeList<Expression> arguments, bool tailPosition)
+        private JsValue EvaluateCall(EvaluationContext context, JsValue func, object reference, in NodeList<Expression> arguments, bool tailPosition)
         {
             JsValue thisValue;
             var referenceRecord = reference as Reference;
+            var engine = context.Engine;
             if (referenceRecord is not null)
             {
                 if (referenceRecord.IsPropertyReference())
@@ -160,7 +165,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                     // deviation from the spec to support null-propagation helper
                     if (baseValue.IsNullOrUndefined()
-                        && _engine._referenceResolver.TryUnresolvableReference(_engine, referenceRecord, out var value))
+                        && engine._referenceResolver.TryUnresolvableReference(engine, referenceRecord, out var value))
                     {
                         return value;
                     }
@@ -174,21 +179,21 @@ namespace Jint.Runtime.Interpreter.Expressions
                 thisValue = Undefined.Instance;
             }
 
-            var argList = ArgumentListEvaluation();
+            var argList = ArgumentListEvaluation(context);
 
-            if (!func.IsObject() && !_engine._referenceResolver.TryGetCallable(_engine, reference, out func))
+            if (!func.IsObject() && !engine._referenceResolver.TryGetCallable(engine, reference, out func))
             {
                 var message = referenceRecord == null
                     ? reference + " is not a function"
                     : $"Property '{referenceRecord.GetReferencedName()}' of object is not a function";
-                ExceptionHelper.ThrowTypeError(_engine.Realm, message);
+                ExceptionHelper.ThrowTypeError(engine.Realm, message);
             }
 
             var callable = func as ICallable;
             if (callable is null)
             {
                 var message = $"{referenceRecord?.GetReferencedName() ?? reference} is not a function";
-                ExceptionHelper.ThrowTypeError(_engine.Realm, message);
+                ExceptionHelper.ThrowTypeError(engine.Realm, message);
             }
 
             if (tailPosition)
@@ -197,14 +202,14 @@ namespace Jint.Runtime.Interpreter.Expressions
                 // PrepareForTailCall();
             }
 
-            var result = _engine.Call(callable, thisValue, argList, _calleeExpression);
+            var result = engine.Call(callable, thisValue, argList, _calleeExpression);
 
             if (!_cached && argList.Length > 0)
             {
-                _engine._jsValueArrayPool.ReturnArray(argList);
+                engine._jsValueArrayPool.ReturnArray(argList);
             }
 
-            _engine._referencePool.Return(referenceRecord);
+            engine._referencePool.Return(referenceRecord);
             return result;
         }
 
@@ -217,7 +222,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             return false;
         }
 
-        private JsValue[] ArgumentListEvaluation()
+        private JsValue[] ArgumentListEvaluation(EvaluationContext context)
         {
             var cachedArguments = _cachedArguments;
             var arguments = System.Array.Empty<JsValue>();
@@ -231,12 +236,12 @@ namespace Jint.Runtime.Interpreter.Expressions
                 {
                     if (_hasSpreads)
                     {
-                        arguments = BuildArgumentsWithSpreads(cachedArguments.JintArguments);
+                        arguments = BuildArgumentsWithSpreads(context, cachedArguments.JintArguments);
                     }
                     else
                     {
-                        arguments = _engine._jsValueArrayPool.RentArray(cachedArguments.JintArguments.Length);
-                        BuildArguments(cachedArguments.JintArguments, arguments);
+                        arguments = context.Engine._jsValueArrayPool.RentArray(cachedArguments.JintArguments.Length);
+                        BuildArguments(context, cachedArguments.JintArguments, arguments);
                     }
                 }
             }

+ 7 - 7
Jint/Runtime/Interpreter/Expressions/JintClassExpression.cs

@@ -5,17 +5,17 @@ namespace Jint.Runtime.Interpreter.Expressions
 {
     internal sealed class JintClassExpression : JintExpression
     {
-        public JintClassExpression(Engine engine, ClassExpression expression) : base(engine, expression)
+        private readonly ClassDefinition _classDefinition;
+
+        public JintClassExpression(ClassExpression expression) : base(expression)
         {
+            _classDefinition = new ClassDefinition(expression.Id, expression.SuperClass, expression.Body);
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            var env = _engine.ExecutionContext.LexicalEnvironment;
-            var expression = (ClassExpression) _expression;
-            var classDefinition = new ClassDefinition(expression.Id, expression.SuperClass, expression.Body);
-            var closure = classDefinition.BuildConstructor(_engine, env);
-            return closure;
+            var env = context.Engine.ExecutionContext.LexicalEnvironment;
+            return NormalCompletion(_classDefinition.BuildConstructor(context, env));
         }
     }
 }

+ 5 - 5
Jint/Runtime/Interpreter/Expressions/JintConditionalExpression.cs

@@ -8,18 +8,18 @@ namespace Jint.Runtime.Interpreter.Expressions
         private readonly JintExpression _consequent;
         private readonly JintExpression _alternate;
 
-        public JintConditionalExpression(Engine engine, ConditionalExpression expression) : base(engine, expression)
+        public JintConditionalExpression(Engine engine, ConditionalExpression expression) : base(expression)
         {
             _test = Build(engine, expression.Test);
             _consequent = Build(engine, expression.Consequent);
             _alternate = Build(engine, expression.Alternate);
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            return TypeConverter.ToBoolean(_test.GetValue())
-                ? _consequent.GetValue()
-                : _alternate.GetValue();
+            return TypeConverter.ToBoolean(_test.GetValue(context).Value)
+                ? _consequent.GetValue(context)
+                : _alternate.GetValue(context);
         }
     }
 }

+ 5 - 10
Jint/Runtime/Interpreter/Expressions/JintConstantExpression.cs

@@ -10,24 +10,19 @@ namespace Jint.Runtime.Interpreter.Expressions
     {
         private readonly JsValue _value;
 
-        public JintConstantExpression(Engine engine, Expression expression, JsValue value) : base(engine, expression)
+        public JintConstantExpression(Expression expression, JsValue value) : base(expression)
         {
             _value = value;
         }
 
-        /// <summary>
-        /// Resolves the underlying value for this expression.
-        /// By default uses the Engine for resolving.
-        /// </summary>
-        /// <seealso cref="JintLiteralExpression"/>
-        public override JsValue GetValue()
+        public override Completion GetValue(EvaluationContext context)
         {
             // need to notify correct node when taking shortcut
-            _engine._lastSyntaxNode = _expression;
+            context.LastSyntaxNode = _expression;
 
-            return _value;
+            return Completion.Normal(_value, _expression.Location);
         }
 
-        protected override object EvaluateInternal() => _value;
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context) => NormalCompletion(_value);
     }
 }

+ 112 - 37
Jint/Runtime/Interpreter/Expressions/JintExpression.cs

@@ -1,25 +1,58 @@
 #nullable enable
 
+using System.Diagnostics;
 using System.Runtime.CompilerServices;
+using Esprima;
 using Esprima.Ast;
 using Jint.Native;
 using Jint.Native.Array;
 using Jint.Native.Iterator;
 using Jint.Native.Number;
+using Jint.Runtime.References;
 
 namespace Jint.Runtime.Interpreter.Expressions
 {
+    /// <summary>
+    /// Adapter to get different types of results, including Reference which is not a JsValue.
+    /// </summary>
+    internal readonly struct ExpressionResult
+    {
+        public readonly ExpressionCompletionType Type;
+        public readonly Location Location;
+        public readonly object Value;
+
+        public ExpressionResult(ExpressionCompletionType type, object value, in Location location)
+        {
+            Type = type;
+            Value = value;
+            Location = location;
+        }
+
+        public bool IsAbrupt() => Type != ExpressionCompletionType.Normal && Type != ExpressionCompletionType.Reference;
+
+        public static implicit operator ExpressionResult(in Completion result)
+        {
+            return new ExpressionResult((ExpressionCompletionType) result.Type, result.Value!, result.Location);
+        }
+    }
+
+    internal enum ExpressionCompletionType : byte
+    {
+        Normal = 0,
+        Return = 1,
+        Throw = 2,
+        Reference
+    }
+
     internal abstract class JintExpression
     {
         // require sub-classes to set to false explicitly to skip virtual call
         protected bool _initialized = true;
 
-        protected readonly Engine _engine;
         protected internal readonly Expression _expression;
 
-        protected JintExpression(Engine engine, Expression expression)
+        protected JintExpression(Expression expression)
         {
-            _engine = engine;
             _expression = expression;
         }
 
@@ -27,32 +60,72 @@ namespace Jint.Runtime.Interpreter.Expressions
         /// Resolves the underlying value for this expression.
         /// By default uses the Engine for resolving.
         /// </summary>
+        /// <param name="context"></param>
         /// <seealso cref="JintLiteralExpression"/>
-        public virtual JsValue GetValue()
+        public virtual Completion GetValue(EvaluationContext context)
         {
-            return _engine.GetValue(Evaluate(), true);
+            var result = Evaluate(context);
+            if (result.Type != ExpressionCompletionType.Reference)
+            {
+                return new Completion((CompletionType) result.Type, (JsValue) result.Value, result.Location);
+            }
+
+            var jsValue = context.Engine.GetValue((Reference) result.Value, true);
+            return new Completion(CompletionType.Normal, jsValue, null, _expression.Location);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public object Evaluate()
+        public ExpressionResult Evaluate(EvaluationContext context)
         {
-            _engine._lastSyntaxNode = _expression;
+            context.LastSyntaxNode = _expression;
             if (!_initialized)
             {
-                Initialize();
+                Initialize(context);
                 _initialized = true;
             }
-            return EvaluateInternal();
+            return EvaluateInternal(context);
         }
 
         /// <summary>
         /// Opportunity to build one-time structures and caching based on lexical context.
         /// </summary>
-        protected virtual void Initialize()
+        /// <param name="context"></param>
+        protected virtual void Initialize(EvaluationContext context)
         {
         }
 
-        protected abstract object EvaluateInternal();
+        protected abstract ExpressionResult EvaluateInternal(EvaluationContext context);
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-normalcompletion
+        /// </summary>
+        /// <remarks>
+        /// We use custom type that is translated to Completion later on.
+        /// </remarks>
+        [DebuggerStepThrough]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        protected ExpressionResult NormalCompletion(JsValue value)
+        {
+            return new ExpressionResult(ExpressionCompletionType.Normal, value, _expression.Location);
+        }
+
+        protected ExpressionResult NormalCompletion(Reference value)
+        {
+            return new ExpressionResult(ExpressionCompletionType.Reference, value, _expression.Location);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-throwcompletion
+        /// </summary>
+        /// <remarks>
+        /// We use custom type that is translated to Completion later on.
+        /// </remarks>
+        [DebuggerStepThrough]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        protected ExpressionResult ThrowCompletion(JsValue value)
+        {
+            return new ExpressionResult(ExpressionCompletionType.Throw, value, _expression.Location);
+        }
 
         /// <summary>
         /// If we'd get Esprima source, we would just refer to it, but this makes error messages easier to decipher.
@@ -93,37 +166,37 @@ namespace Jint.Runtime.Interpreter.Expressions
             var result = expression.Type switch
             {
                 Nodes.AssignmentExpression => JintAssignmentExpression.Build(engine, (AssignmentExpression) expression),
-                Nodes.ArrayExpression => new JintArrayExpression(engine, (ArrayExpression) expression),
-                Nodes.ArrowFunctionExpression => new JintArrowFunctionExpression(engine, (IFunction) expression),
+                Nodes.ArrayExpression => new JintArrayExpression((ArrayExpression) expression),
+                Nodes.ArrowFunctionExpression => new JintArrowFunctionExpression(engine, (ArrowFunctionExpression) expression),
                 Nodes.BinaryExpression => JintBinaryExpression.Build(engine, (BinaryExpression) expression),
-                Nodes.CallExpression => new JintCallExpression(engine, (CallExpression) expression),
+                Nodes.CallExpression => new JintCallExpression((CallExpression) expression),
                 Nodes.ConditionalExpression => new JintConditionalExpression(engine, (ConditionalExpression) expression),
-                Nodes.FunctionExpression => new JintFunctionExpression(engine, (IFunction) expression),
-                Nodes.Identifier => new JintIdentifierExpression(engine, (Identifier) expression),
-                Nodes.Literal => JintLiteralExpression.Build(engine, (Literal) expression),
+                Nodes.FunctionExpression => new JintFunctionExpression(engine, (FunctionExpression) expression),
+                Nodes.Identifier => new JintIdentifierExpression((Identifier) expression),
+                Nodes.Literal => JintLiteralExpression.Build((Literal) expression),
                 Nodes.LogicalExpression => ((BinaryExpression) expression).Operator switch
                 {
-                    BinaryOperator.LogicalAnd => new JintLogicalAndExpression(engine, (BinaryExpression) expression),
+                    BinaryOperator.LogicalAnd => new JintLogicalAndExpression((BinaryExpression) expression),
                     BinaryOperator.LogicalOr => new JintLogicalOrExpression(engine, (BinaryExpression) expression),
                     BinaryOperator.NullishCoalescing => new NullishCoalescingExpression(engine, (BinaryExpression) expression),
                     _ => null
                 },
-                Nodes.MemberExpression => new JintMemberExpression(engine, (MemberExpression) expression),
-                Nodes.NewExpression => new JintNewExpression(engine, (NewExpression) expression),
-                Nodes.ObjectExpression => new JintObjectExpression(engine, (ObjectExpression) expression),
-                Nodes.SequenceExpression => new JintSequenceExpression(engine, (SequenceExpression) expression),
-                Nodes.ThisExpression => new JintThisExpression(engine, (ThisExpression) expression),
-                Nodes.UpdateExpression => new JintUpdateExpression(engine, (UpdateExpression) expression),
+                Nodes.MemberExpression => new JintMemberExpression((MemberExpression) expression),
+                Nodes.NewExpression => new JintNewExpression((NewExpression) expression),
+                Nodes.ObjectExpression => new JintObjectExpression((ObjectExpression) expression),
+                Nodes.SequenceExpression => new JintSequenceExpression((SequenceExpression) expression),
+                Nodes.ThisExpression => new JintThisExpression((ThisExpression) expression),
+                Nodes.UpdateExpression => new JintUpdateExpression((UpdateExpression) expression),
                 Nodes.UnaryExpression => JintUnaryExpression.Build(engine, (UnaryExpression) expression),
                 Nodes.SpreadElement => new JintSpreadExpression(engine, (SpreadElement) expression),
-                Nodes.TemplateLiteral => new JintTemplateLiteralExpression(engine, (TemplateLiteral) expression),
-                Nodes.TaggedTemplateExpression => new JintTaggedTemplateExpression(engine, (TaggedTemplateExpression) expression),
-                Nodes.ClassExpression => new JintClassExpression(engine, (ClassExpression) expression),
-                Nodes.Super => new JintSuperExpression(engine, (Super) expression),
-                Nodes.MetaProperty => new JintMetaPropertyExpression(engine, (MetaProperty) expression),
+                Nodes.TemplateLiteral => new JintTemplateLiteralExpression((TemplateLiteral) expression),
+                Nodes.TaggedTemplateExpression => new JintTaggedTemplateExpression((TaggedTemplateExpression) expression),
+                Nodes.ClassExpression => new JintClassExpression((ClassExpression) expression),
+                Nodes.Super => new JintSuperExpression((Super) expression),
+                Nodes.MetaProperty => new JintMetaPropertyExpression((MetaProperty) expression),
                 Nodes.ChainExpression => ((ChainExpression) expression).Expression.Type == Nodes.CallExpression
-                    ? new JintCallExpression(engine, (CallExpression) ((ChainExpression) expression).Expression)
-                    : new JintMemberExpression(engine, (MemberExpression) ((ChainExpression) expression).Expression),
+                    ? new JintCallExpression((CallExpression) ((ChainExpression) expression).Expression)
+                    : new JintMemberExpression((MemberExpression) ((ChainExpression) expression).Expression),
                 _ =>  null
             };
 
@@ -353,15 +426,16 @@ namespace Jint.Runtime.Interpreter.Expressions
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        protected static void BuildArguments(JintExpression[] jintExpressions, JsValue[] targetArray)
+        protected static void BuildArguments(EvaluationContext context, JintExpression[] jintExpressions, JsValue[] targetArray)
         {
             for (var i = 0; i < jintExpressions.Length; i++)
             {
-                targetArray[i] = jintExpressions[i].GetValue().Clone();
+                var completion = jintExpressions[i].GetValue(context);
+                targetArray[i] = completion.Value!.Clone();
             }
         }
 
-        protected JsValue[] BuildArgumentsWithSpreads(JintExpression[] jintExpressions)
+        protected JsValue[] BuildArgumentsWithSpreads(EvaluationContext context, JintExpression[] jintExpressions)
         {
             var args = new System.Collections.Generic.List<JsValue>(jintExpressions.Length);
             for (var i = 0; i < jintExpressions.Length; i++)
@@ -369,7 +443,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var jintExpression = jintExpressions[i];
                 if (jintExpression is JintSpreadExpression jse)
                 {
-                    jse.GetValueAndCheckIterator(out var objectInstance, out var iterator);
+                    jse.GetValueAndCheckIterator(context, out var objectInstance, out var iterator);
                     // optimize for array unless someone has touched the iterator
                     if (objectInstance is ArrayInstance ai && ai.HasOriginalIterator)
                     {
@@ -384,13 +458,14 @@ namespace Jint.Runtime.Interpreter.Expressions
                     }
                     else
                     {
-                        var protocol = new ArraySpreadProtocol(_engine, args, iterator);
+                        var protocol = new ArraySpreadProtocol(context.Engine, args, iterator);
                         protocol.Execute();
                     }
                 }
                 else
                 {
-                    args.Add(jintExpression.GetValue().Clone());
+                    var completion = jintExpression.GetValue(context);
+                    args.Add(completion.Value!.Clone());
                 }
             }
 

+ 8 - 8
Jint/Runtime/Interpreter/Expressions/JintFunctionExpression.cs

@@ -1,5 +1,4 @@
 using Esprima.Ast;
-using Jint.Native;
 using Jint.Native.Function;
 using Jint.Runtime.Environments;
 
@@ -10,22 +9,23 @@ namespace Jint.Runtime.Interpreter.Expressions
         private readonly JintFunctionDefinition _function;
 
         public JintFunctionExpression(Engine engine, IFunction function)
-            : base(engine, ArrowParameterPlaceHolder.Empty)
+            : base(ArrowParameterPlaceHolder.Empty)
         {
             _function = new JintFunctionDefinition(engine, function);
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            return GetValue();
+            return GetValue(context);
         }
 
-        public override JsValue GetValue()
+        public override Completion GetValue(EvaluationContext context)
         {
-            var funcEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
+            var engine = context.Engine;
+            var funcEnv = JintEnvironment.NewDeclarativeEnvironment(engine, engine.ExecutionContext.LexicalEnvironment);
 
             var closure = new ScriptFunctionInstance(
-                _engine,
+                engine,
                 _function,
                 funcEnv,
                 _function.ThisMode);
@@ -37,7 +37,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 funcEnv.CreateMutableBindingAndInitialize(_function.Name, canBeDeleted: false, closure);
             }
 
-            return closure;
+            return Completion.Normal(closure, _expression.Location);
         }
     }
 }

+ 17 - 16
Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs

@@ -10,7 +10,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         internal readonly EnvironmentRecord.BindingName _expressionName;
         private readonly JsValue _calculatedValue;
 
-        public JintIdentifierExpression(Engine engine, Identifier expression) : base(engine, expression)
+        public JintIdentifierExpression(Identifier expression) : base(expression)
         {
             _expressionName = new EnvironmentRecord.BindingName(expression.Name);
             if (expression.Name == "undefined")
@@ -22,48 +22,49 @@ namespace Jint.Runtime.Interpreter.Expressions
         public bool HasEvalOrArguments
             => _expressionName.Key == KnownKeys.Eval || _expressionName.Key == KnownKeys.Arguments;
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            var env = _engine.ExecutionContext.LexicalEnvironment;
+            var engine = context.Engine;
+            var env = engine.ExecutionContext.LexicalEnvironment;
             var strict = StrictModeScope.IsStrictModeCode;
-            var identifierEnvironment = JintEnvironment.TryGetIdentifierEnvironmentWithBindingValue(_engine, env, _expressionName, strict, out var temp, out _)
+            var identifierEnvironment = JintEnvironment.TryGetIdentifierEnvironmentWithBindingValue(engine, env, _expressionName, strict, out var temp, out _)
                 ? temp
                 : JsValue.Undefined;
 
-            return _engine._referencePool.Rent(identifierEnvironment, _expressionName.StringValue, strict, thisValue: null);
+            return NormalCompletion(engine._referencePool.Rent(identifierEnvironment, _expressionName.StringValue, strict, thisValue: null));
         }
 
-        public override JsValue GetValue()
+        public override Completion GetValue(EvaluationContext context)
         {
             // need to notify correct node when taking shortcut
-            _engine._lastSyntaxNode = _expression;
+            context.LastSyntaxNode = _expression;
 
             if (_calculatedValue is not null)
             {
-                return _calculatedValue;
+                return Completion.Normal(_calculatedValue, _expression.Location);
             }
 
             var strict = StrictModeScope.IsStrictModeCode;
-            var env = _engine.ExecutionContext.LexicalEnvironment;
+            var engine = context.Engine;
+            var env = engine.ExecutionContext.LexicalEnvironment;
 
-            JsValue value;
             if (JintEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
-                _engine,
+                engine,
                 env,
                 _expressionName,
                 strict,
                 out _,
-                out value))
+                out var value))
             {
                 if (value is null)
                 {
-                    ExceptionHelper.ThrowReferenceError(_engine.Realm, _expressionName.Key.Name + " has not been initialized");
+                    ExceptionHelper.ThrowReferenceError(engine.Realm, _expressionName.Key.Name + " has not been initialized");
                 }
             }
             else
             {
-                var reference = _engine._referencePool.Rent(JsValue.Undefined, _expressionName.StringValue, strict, thisValue: null);
-                value = _engine.GetValue(reference, true);
+                var reference = engine._referencePool.Rent(JsValue.Undefined, _expressionName.StringValue, strict, thisValue: null);
+                value = engine.GetValue(reference, true);
             }
 
             // make sure arguments access freezes state
@@ -72,7 +73,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 argumentsInstance.Materialize();
             }
 
-            return value;
+            return Completion.Normal(value, _expression.Location);
         }
     }
 }

+ 15 - 15
Jint/Runtime/Interpreter/Expressions/JintLiteralExpression.cs

@@ -5,21 +5,21 @@ using Jint.Native;
 
 namespace Jint.Runtime.Interpreter.Expressions
 {
-    internal class JintLiteralExpression : JintExpression
+    internal sealed class JintLiteralExpression : JintExpression
     {
-        private JintLiteralExpression(Engine engine, Literal expression) : base(engine, expression)
+        private JintLiteralExpression(Literal expression) : base(expression)
         {
         }
 
-        internal static JintExpression Build(Engine engine, Literal expression)
+        internal static JintExpression Build(Literal expression)
         {
             var constantValue = ConvertToJsValue(expression);
-            if (!(constantValue is null))
+            if (constantValue is not null)
             {
-                return new JintConstantExpression(engine, expression, constantValue);
+                return new JintConstantExpression(expression, constantValue);
             }
-            
-            return new JintLiteralExpression(engine, expression);
+
+            return new JintLiteralExpression(expression);
         }
 
         internal static JsValue ConvertToJsValue(Literal literal)
@@ -51,25 +51,25 @@ namespace Jint.Runtime.Interpreter.Expressions
             return null;
         }
 
-        public override JsValue GetValue()
+        public override Completion GetValue(EvaluationContext context)
         {
             // need to notify correct node when taking shortcut
-            _engine._lastSyntaxNode = _expression;
-            
-            return ResolveValue();
+            context.LastSyntaxNode = _expression;
+
+            return Completion.Normal(ResolveValue(context), _expression.Location);
         }
 
-        protected override object EvaluateInternal() => ResolveValue();
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context) => NormalCompletion(ResolveValue(context));
 
-        private JsValue ResolveValue()
+        private JsValue ResolveValue(EvaluationContext context)
         {
             var expression = (Literal) _expression;
             if (expression.TokenType == TokenType.RegularExpression)
             {
-                return _engine.Realm.Intrinsics.RegExp.Construct((System.Text.RegularExpressions.Regex) expression.Value, expression.Regex.Flags);
+                return context.Engine.Realm.Intrinsics.RegExp.Construct((System.Text.RegularExpressions.Regex) expression.Value, expression.Regex.Flags);
             }
 
-            return JsValue.FromObject(_engine, expression.Value);
+            return JsValue.FromObject(context.Engine, expression.Value);
         }
     }
 }

+ 9 - 9
Jint/Runtime/Interpreter/Expressions/JintLogicalAndExpression.cs

@@ -8,33 +8,33 @@ namespace Jint.Runtime.Interpreter.Expressions
         private JintExpression _left;
         private JintExpression _right;
 
-        public JintLogicalAndExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
+        public JintLogicalAndExpression(BinaryExpression expression) : base(expression)
         {
             _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
             var expression = (BinaryExpression) _expression;
-            _left = Build(_engine, expression.Left);
-            _right = Build(_engine, expression.Right);
+            _left = Build(context.Engine, expression.Left);
+            _right = Build(context.Engine, expression.Right);
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            var left = _left.GetValue();
+            var left = _left.GetValue(context).Value;
 
             if (left is JsBoolean b && !b._value)
             {
-                return b;
+                return NormalCompletion(b);
             }
 
             if (!TypeConverter.ToBoolean(left))
             {
-                return left;
+                return NormalCompletion(left);
             }
 
-            return _right.GetValue();
+            return _right.GetValue(context);
         }
     }
 }

+ 6 - 6
Jint/Runtime/Interpreter/Expressions/JintLogicalOrExpression.cs

@@ -8,27 +8,27 @@ namespace Jint.Runtime.Interpreter.Expressions
         private readonly JintExpression _left;
         private readonly JintExpression _right;
 
-        public JintLogicalOrExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
+        public JintLogicalOrExpression(Engine engine, BinaryExpression expression) : base(expression)
         {
             _left = Build(engine, expression.Left);
             _right = Build(engine, expression.Right);
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            var left = _left.GetValue();
+            var left = _left.GetValue(context).Value;
 
             if (left is JsBoolean b && b._value)
             {
-                return b;
+                return NormalCompletion(b);
             }
 
             if (TypeConverter.ToBoolean(left))
             {
-                return left;
+                return NormalCompletion(left);
             }
 
-            return _right.GetValue();
+            return _right.GetValue(context);
         }
     }
 }

+ 23 - 18
Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs

@@ -15,15 +15,15 @@ namespace Jint.Runtime.Interpreter.Expressions
         private JintExpression _propertyExpression;
         private JsValue _determinedProperty;
 
-        public JintMemberExpression(Engine engine, MemberExpression expression) : base(engine, expression)
+        public JintMemberExpression(MemberExpression expression) : base(expression)
         {
             _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
             _memberExpression = (MemberExpression) _expression;
-            _objectExpression = Build(_engine, _memberExpression.Object);
+            _objectExpression = Build(context.Engine, _memberExpression.Object);
 
             if (!_memberExpression.Computed)
             {
@@ -36,24 +36,25 @@ namespace Jint.Runtime.Interpreter.Expressions
 
             if (_determinedProperty is null)
             {
-                _propertyExpression = Build(_engine, _memberExpression.Property);
+                _propertyExpression = Build(context.Engine, _memberExpression.Property);
             }
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
             JsValue actualThis = null;
             string baseReferenceName = null;
             JsValue baseValue = null;
             var isStrictModeCode = StrictModeScope.IsStrictModeCode;
 
+            var engine = context.Engine;
             if (_objectExpression is JintIdentifierExpression identifierExpression)
             {
                 baseReferenceName = identifierExpression._expressionName.Key.Name;
                 var strict = isStrictModeCode;
-                var env = _engine.ExecutionContext.LexicalEnvironment;
+                var env = engine.ExecutionContext.LexicalEnvironment;
                 JintEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
-                    _engine,
+                    engine,
                     env,
                     identifierExpression._expressionName,
                     strict,
@@ -62,11 +63,11 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
             else if (_objectExpression is JintThisExpression thisExpression)
             {
-                baseValue = thisExpression.GetValue();
+                baseValue = thisExpression.GetValue(context).Value;
             }
             else if (_objectExpression is JintSuperExpression)
             {
-                var env = (FunctionEnvironmentRecord) _engine.ExecutionContext.GetThisEnvironment();
+                var env = (FunctionEnvironmentRecord) engine.ExecutionContext.GetThisEnvironment();
                 actualThis = env.GetThisBinding();
                 baseValue = env.GetSuperBase();
             }
@@ -74,29 +75,29 @@ namespace Jint.Runtime.Interpreter.Expressions
             if (baseValue is null)
             {
                 // fast checks failed
-                var baseReference = _objectExpression.Evaluate();
+                var baseReference = _objectExpression.Evaluate(context).Value;
                 if (ReferenceEquals(Undefined.Instance, baseReference))
                 {
-                    return Undefined.Instance;
+                    return NormalCompletion(Undefined.Instance);
                 }
                 if (baseReference is Reference reference)
                 {
                     baseReferenceName = reference.GetReferencedName().ToString();
-                    baseValue = _engine.GetValue(reference, false);
-                    _engine._referencePool.Return(reference);
+                    baseValue = engine.GetValue(reference, false);
+                    engine._referencePool.Return(reference);
                 }
                 else
                 {
-                    baseValue = _engine.GetValue(baseReference, false);
+                    baseValue = engine.GetValue(baseReference, false);
                 }
             }
 
             if (baseValue.IsNullOrUndefined() && (_memberExpression.Optional || _objectExpression._expression.IsOptional()))
             {
-                return Undefined.Instance;
+                return NormalCompletion(Undefined.Instance);
             }
 
-            var property = _determinedProperty ?? _propertyExpression.GetValue();
+            var property = _determinedProperty ?? _propertyExpression.GetValue(context).Value;
             if (baseValue.IsNullOrUndefined())
             {
                 // we can use base data types securely, object evaluation can mess things up
@@ -104,7 +105,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     ? TypeConverter.ToString(property)
                     : _determinedProperty?.ToString() ?? baseReferenceName;
 
-                TypeConverter.CheckObjectCoercible(_engine, baseValue, _memberExpression.Property, referenceName);
+                TypeConverter.CheckObjectCoercible(engine, baseValue, _memberExpression.Property, referenceName);
             }
 
             // only convert if necessary
@@ -112,7 +113,11 @@ namespace Jint.Runtime.Interpreter.Expressions
                 ? property
                 : TypeConverter.ToPropertyKey(property);
 
-            return _engine._referencePool.Rent(baseValue, propertyKey, isStrictModeCode, thisValue: actualThis);
+            var rent = context.Engine._referencePool.Rent(baseValue, propertyKey, isStrictModeCode, thisValue: actualThis);
+            return new ExpressionResult(
+                ExpressionCompletionType.Reference,
+                rent,
+                _expression.Location);
         }
     }
 }

+ 5 - 5
Jint/Runtime/Interpreter/Expressions/JintMetaPropertyExpression.cs

@@ -6,20 +6,20 @@ namespace Jint.Runtime.Interpreter.Expressions
     {
         private readonly bool _newTarget;
 
-        public JintMetaPropertyExpression(Engine engine, MetaProperty expression) : base(engine, expression)
+        public JintMetaPropertyExpression(MetaProperty expression) : base(expression)
         {
             _newTarget = expression.Meta.Name == "new" && expression.Property.Name == "target";
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
             if (_newTarget)
             {
-                return _engine.GetNewTarget();
+                return NormalCompletion(context.Engine.GetNewTarget());
             }
-            
+
             ExceptionHelper.ThrowNotImplementedException();
-            return null;
+            return default;
         }
     }
 }

+ 17 - 13
Jint/Runtime/Interpreter/Expressions/JintNewExpression.cs

@@ -10,15 +10,17 @@ namespace Jint.Runtime.Interpreter.Expressions
         private JintExpression[] _jintArguments = Array.Empty<JintExpression>();
         private bool _hasSpreads;
 
-        public JintNewExpression(Engine engine, NewExpression expression) : base(engine, expression)
+        public JintNewExpression(NewExpression expression) : base(expression)
         {
             _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
+            var engine = context.Engine;
+
             var expression = (NewExpression) _expression;
-            _calleeExpression = Build(_engine, expression.Callee);
+            _calleeExpression = Build(engine, expression.Callee);
 
             if (expression.Arguments.Count <= 0)
             {
@@ -29,15 +31,17 @@ namespace Jint.Runtime.Interpreter.Expressions
             for (var i = 0; i < _jintArguments.Length; i++)
             {
                 var argument = expression.Arguments[i];
-                _jintArguments[i] = Build(_engine, argument);
+                _jintArguments[i] = Build(engine, argument);
                 _hasSpreads |= argument.Type == Nodes.SpreadElement;
             }
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
+            var engine = context.Engine;
+
             // todo: optimize by defining a common abstract class or interface
-            var jsValue = _calleeExpression.GetValue();
+            var jsValue = _calleeExpression.GetValue(context).Value;
 
             JsValue[] arguments;
             if (_jintArguments.Length == 0)
@@ -46,25 +50,25 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
             else if (_hasSpreads)
             {
-                arguments = BuildArgumentsWithSpreads(_jintArguments);
+                arguments = BuildArgumentsWithSpreads(context, _jintArguments);
             }
             else
             {
-                arguments = _engine._jsValueArrayPool.RentArray(_jintArguments.Length);
-                BuildArguments(_jintArguments, arguments);
+                arguments = engine._jsValueArrayPool.RentArray(_jintArguments.Length);
+                BuildArguments(context, _jintArguments, arguments);
             }
 
             if (!jsValue.IsConstructor)
             {
-                ExceptionHelper.ThrowTypeError(_engine.Realm,  _calleeExpression.SourceText + " is not a constructor");
+                ExceptionHelper.ThrowTypeError(engine.Realm,  _calleeExpression.SourceText + " is not a constructor");
             }
 
             // construct the new instance using the Function's constructor method
-            var instance = _engine.Construct((IConstructor) jsValue, arguments, jsValue, _calleeExpression);
+            var instance = engine.Construct((IConstructor) jsValue, arguments, jsValue, _calleeExpression);
 
-            _engine._jsValueArrayPool.ReturnArray(arguments);
+            engine._jsValueArrayPool.ReturnArray(arguments);
 
-            return instance;
+            return NormalCompletion(instance);
         }
     }
 }

+ 36 - 21
Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs

@@ -54,12 +54,12 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
         }
 
-        public JintObjectExpression(Engine engine, ObjectExpression expression) : base(engine, expression)
+        public JintObjectExpression(ObjectExpression expression) : base(expression)
         {
             _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
             _canBuildFast = true;
             var expression = (ObjectExpression) _expression;
@@ -69,6 +69,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 return;
             }
 
+            var engine = context.Engine;
             _valueExpressions = new JintExpression[expression.Properties.Count];
             _properties = new ObjectProperty[expression.Properties.Count];
 
@@ -93,7 +94,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     if (p.Kind == PropertyKind.Init || p.Kind == PropertyKind.Data)
                     {
                         var propertyValue = p.Value;
-                        _valueExpressions[i] = Build(_engine, propertyValue);
+                        _valueExpressions[i] = Build(engine, propertyValue);
                         _canBuildFast &= !propertyValue.IsFunctionDefinition();
                     }
                     else
@@ -105,7 +106,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 {
                     _canBuildFast = false;
                     _properties[i] = null;
-                    _valueExpressions[i] = Build(_engine, spreadElement.Argument);
+                    _valueExpressions[i] = Build(engine, spreadElement.Argument);
                 }
                 else
                 {
@@ -116,22 +117,22 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
             return _canBuildFast
-                ? BuildObjectFast()
-                : BuildObjectNormal();
+                ? BuildObjectFast(context)
+                : BuildObjectNormal(context);
         }
 
         /// <summary>
         /// Version that can safely build plain object with only normal init/data fields fast.
         /// </summary>
-        private object BuildObjectFast()
+        private ExpressionResult BuildObjectFast(EvaluationContext context)
         {
-            var obj = _engine.Realm.Intrinsics.Object.Construct(0);
+            var obj = context.Engine.Realm.Intrinsics.Object.Construct(0);
             if (_properties.Length == 0)
             {
-                return obj;
+                return NormalCompletion(obj);
             }
 
             var properties = new PropertyDictionary(_properties.Length, checkExistingKeys: true);
@@ -139,19 +140,21 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
                 var objectProperty = _properties[i];
                 var valueExpression = _valueExpressions[i];
-                var propValue = valueExpression.GetValue().Clone();
+                var propValue = valueExpression.GetValue(context).Value!.Clone();
                 properties[objectProperty!._key] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
             }
+
             obj.SetProperties(properties);
-            return obj;
+            return NormalCompletion(obj);
         }
 
         /// <summary>
         /// https://tc39.es/ecma262/#sec-object-initializer-runtime-semantics-propertydefinitionevaluation
         /// </summary>
-        private object BuildObjectNormal()
+        private ExpressionResult BuildObjectNormal(EvaluationContext context)
         {
-            var obj = _engine.Realm.Intrinsics.Object.Construct(_properties.Length);
+            var engine = context.Engine;
+            var obj = engine.Realm.Intrinsics.Object.Construct(_properties.Length);
 
             for (var i = 0; i < _properties.Length; i++)
             {
@@ -160,10 +163,11 @@ namespace Jint.Runtime.Interpreter.Expressions
                 if (objectProperty is null)
                 {
                     // spread
-                    if (_valueExpressions[i].GetValue() is ObjectInstance source)
+                    if (_valueExpressions[i].GetValue(context).Value is ObjectInstance source)
                     {
                         source.CopyDataProperties(obj, null);
                     }
+
                     continue;
                 }
 
@@ -178,11 +182,22 @@ namespace Jint.Runtime.Interpreter.Expressions
                     continue;
                 }
 
-                var propName = objectProperty.KeyJsString ?? property.GetKey(_engine);
+                JsValue? propName = objectProperty.KeyJsString;
+                if (propName is null)
+                {
+                    propName = TypeConverter.ToPropertyKey(property.GetKey(engine));
+                }
+
                 if (property.Kind == PropertyKind.Init || property.Kind == PropertyKind.Data)
                 {
                     var expr = _valueExpressions[i];
-                    JsValue propValue = expr.GetValue().Clone();
+                    var completion = expr.GetValue(context);
+                    if (completion.IsAbrupt())
+                    {
+                        return completion;
+                    }
+
+                    var propValue = completion.Value!.Clone();
                     if (expr._expression.IsFunctionDefinition())
                     {
                         var closure = (FunctionInstance) propValue;
@@ -194,11 +209,11 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
                 else if (property.Kind == PropertyKind.Get || property.Kind == PropertyKind.Set)
                 {
-                    var function = objectProperty.GetFunctionDefinition(_engine);
+                    var function = objectProperty.GetFunctionDefinition(engine);
                     var closure = new ScriptFunctionInstance(
-                        _engine,
+                        engine,
                         function,
-                        _engine.ExecutionContext.LexicalEnvironment,
+                        engine.ExecutionContext.LexicalEnvironment,
                         function.ThisMode);
 
                     closure.SetFunctionName(propName, property.Kind == PropertyKind.Get ? "get" : "set");
@@ -213,7 +228,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
             }
 
-            return obj;
+            return NormalCompletion(obj);
         }
     }
 }

+ 7 - 6
Jint/Runtime/Interpreter/Expressions/JintSequenceExpression.cs

@@ -7,33 +7,34 @@ namespace Jint.Runtime.Interpreter.Expressions
     {
         private JintExpression[] _expressions;
 
-        public JintSequenceExpression(Engine engine, SequenceExpression expression) : base(engine, expression)
+        public JintSequenceExpression(SequenceExpression expression) : base(expression)
         {
             _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
+            var engine = context.Engine;
             var expression = (SequenceExpression) _expression;
             ref readonly var expressions = ref expression.Expressions;
             var temp = new JintExpression[expressions.Count];
             for (var i = 0; i < (uint) temp.Length; i++)
             {
-                temp[i] = Build(_engine, expressions[i]);
+                temp[i] = Build(engine, expressions[i]);
             }
 
             _expressions = temp;
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
             var result = Undefined.Instance;
             foreach (var expression in _expressions)
             {
-                result = expression.GetValue();
+                result = expression.GetValue(context).Value;
             }
 
-            return result;
+            return NormalCompletion(result);
         }
     }
 }

+ 12 - 12
Jint/Runtime/Interpreter/Expressions/JintSpreadExpression.cs

@@ -9,34 +9,34 @@ namespace Jint.Runtime.Interpreter.Expressions
         private readonly JintExpression _argument;
         private readonly string _argumentName;
 
-        public JintSpreadExpression(Engine engine, SpreadElement expression) : base(engine, expression)
+        public JintSpreadExpression(Engine engine, SpreadElement expression) : base(expression)
         {
             _argument = Build(engine, expression.Argument);
             _argumentName = (expression.Argument as Identifier)?.Name;
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            GetValueAndCheckIterator(out var objectInstance, out var iterator);
-            return objectInstance;
+            GetValueAndCheckIterator(context, out var objectInstance, out var iterator);
+            return NormalCompletion(objectInstance);
         }
 
-        public override JsValue GetValue()
+        public override Completion GetValue(EvaluationContext context)
         {
             // need to notify correct node when taking shortcut
-            _engine._lastSyntaxNode = _expression;
+            context.LastSyntaxNode = _expression;
 
-            GetValueAndCheckIterator(out var objectInstance, out var iterator);
-            return objectInstance;
+            GetValueAndCheckIterator(context, out var objectInstance, out var iterator);
+            return Completion.Normal(objectInstance, _expression.Location);
         }
 
-        internal void GetValueAndCheckIterator(out JsValue instance, out IIterator iterator)
+        internal void GetValueAndCheckIterator(EvaluationContext context, out JsValue instance, out IIterator iterator)
         {
-            instance = _argument.GetValue();
-            if (instance is null || !instance.TryGetIterator(_engine.Realm, out iterator))
+            instance = _argument.GetValue(context).Value;
+            if (instance is null || !instance.TryGetIterator(context.Engine.Realm, out iterator))
             {
                 iterator = null;
-                ExceptionHelper.ThrowTypeError(_engine.Realm, _argumentName + " is not iterable");
+                ExceptionHelper.ThrowTypeError(context.Engine.Realm, _argumentName + " is not iterable");
             }
         }
     }

+ 4 - 4
Jint/Runtime/Interpreter/Expressions/JintSuperExpression.cs

@@ -5,16 +5,16 @@ namespace Jint.Runtime.Interpreter.Expressions
 {
     internal sealed class JintSuperExpression : JintExpression
     {
-        public JintSuperExpression(Engine engine, Super expression) : base(engine, expression)
+        public JintSuperExpression(Super expression) : base(expression)
         {
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            var envRec = (FunctionEnvironmentRecord) _engine.ExecutionContext.GetThisEnvironment();
+            var envRec = (FunctionEnvironmentRecord) context.Engine.ExecutionContext.GetThisEnvironment();
             var activeFunction = envRec._functionObject;
             var superConstructor = activeFunction.GetPrototypeOf();
-            return superConstructor;
+            return NormalCompletion(superConstructor);
         }
     }
 }

+ 18 - 16
Jint/Runtime/Interpreter/Expressions/JintTaggedTemplateExpression.cs

@@ -13,53 +13,55 @@ namespace Jint.Runtime.Interpreter.Expressions
         private JintExpression _tagIdentifier;
         private JintTemplateLiteralExpression _quasi;
 
-        public JintTaggedTemplateExpression(Engine engine, TaggedTemplateExpression expression) : base(engine, expression)
+        public JintTaggedTemplateExpression(TaggedTemplateExpression expression) : base(expression)
         {
             _taggedTemplateExpression = expression;
             _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
-            _tagIdentifier = Build(_engine, _taggedTemplateExpression.Tag);
-            _quasi = (JintTemplateLiteralExpression) Build(_engine, _taggedTemplateExpression.Quasi);
-            _quasi.DoInitialize();
+            var engine = context.Engine;
+            _tagIdentifier = Build(engine, _taggedTemplateExpression.Tag);
+            _quasi = new JintTemplateLiteralExpression(_taggedTemplateExpression.Quasi);
+            _quasi.DoInitialize(context);
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            var tagger = _engine.GetValue(_tagIdentifier.GetValue()) as ICallable;
+            var engine = context.Engine;
+            var tagger = engine.GetValue(_tagIdentifier.GetValue(context)) as ICallable;
             if (tagger is null)
             {
-                ExceptionHelper.ThrowTypeError(_engine.Realm, "Argument must be callable");
+                ExceptionHelper.ThrowTypeError(engine.Realm, "Argument must be callable");
             }
 
             var expressions = _quasi._expressions;
 
-            var args = _engine._jsValueArrayPool.RentArray(expressions.Length + 1);
+            var args = engine._jsValueArrayPool.RentArray(expressions.Length + 1);
 
-            var template = GetTemplateObject();
+            var template = GetTemplateObject(context);
             args[0] = template;
 
             for (int i = 0; i < expressions.Length; ++i)
             {
-                args[i + 1] = expressions[i].GetValue();
+                args[i + 1] = expressions[i].GetValue(context).Value;
             }
 
             var result = tagger.Call(JsValue.Undefined, args);
-            _engine._jsValueArrayPool.ReturnArray(args);
+            engine._jsValueArrayPool.ReturnArray(args);
 
-            return result;
+            return NormalCompletion(result);
         }
 
         /// <summary>
         /// https://www.ecma-international.org/ecma-262/6.0/#sec-gettemplateobject
         /// </summary>
-        private ArrayInstance GetTemplateObject()
+        private ArrayInstance GetTemplateObject(EvaluationContext context)
         {
             var count = (uint) _quasi._templateLiteralExpression.Quasis.Count;
-            var template = _engine.Realm.Intrinsics.Array.ConstructFast(count);
-            var rawObj = _engine.Realm.Intrinsics.Array.ConstructFast(count);
+            var template = context.Engine.Realm.Intrinsics.Array.ConstructFast(count);
+            var rawObj = context.Engine.Realm.Intrinsics.Array.ConstructFast(count);
             for (uint i = 0; i < _quasi._templateLiteralExpression.Quasis.Count; ++i)
             {
                 var templateElementValue = _quasi._templateLiteralExpression.Quasis[(int) i].Value;

+ 18 - 18
Jint/Runtime/Interpreter/Expressions/JintTemplateLiteralExpression.cs

@@ -9,50 +9,50 @@ namespace Jint.Runtime.Interpreter.Expressions
         internal readonly TemplateLiteral _templateLiteralExpression;
         internal JintExpression[] _expressions;
 
-        public JintTemplateLiteralExpression(Engine engine, TemplateLiteral expression) : base(engine, expression)
+        public JintTemplateLiteralExpression(TemplateLiteral expression) : base(expression)
         {
             _templateLiteralExpression = expression;
             _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
-            DoInitialize();
+            DoInitialize(context);
         }
 
-        internal void DoInitialize()
+        internal void DoInitialize(EvaluationContext context)
         {
+            var engine = context.Engine;
             _expressions = new JintExpression[_templateLiteralExpression.Expressions.Count];
             for (var i = 0; i < _templateLiteralExpression.Expressions.Count; i++)
             {
                 var exp = _templateLiteralExpression.Expressions[i];
-                _expressions[i] = Build(_engine, exp);
+                _expressions[i] = Build(engine, exp);
             }
 
             _initialized = true;
         }
 
-        private JsString BuildString()
+        private JsString BuildString(EvaluationContext context)
         {
-            using (var sb = StringBuilderPool.Rent())
+            using var sb = StringBuilderPool.Rent();
+            for (var i = 0; i < _templateLiteralExpression.Quasis.Count; i++)
             {
-                for (var i = 0; i < _templateLiteralExpression.Quasis.Count; i++)
+                var quasi = _templateLiteralExpression.Quasis[i];
+                sb.Builder.Append(quasi.Value.Cooked);
+                if (i < _expressions.Length)
                 {
-                    var quasi = _templateLiteralExpression.Quasis[i];
-                    sb.Builder.Append(quasi.Value.Cooked);
-                    if (i < _expressions.Length)
-                    {
-                        sb.Builder.Append(_expressions[i].GetValue());
-                    }
+                    var completion = _expressions[i].GetValue(context);
+                    sb.Builder.Append(completion.Value);
                 }
-
-                return JsString.Create(sb.ToString());
             }
+
+            return JsString.Create(sb.ToString());
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            return BuildString();
+            return NormalCompletion(BuildString(context));
         }
     }
 }

+ 6 - 7
Jint/Runtime/Interpreter/Expressions/JintThisExpression.cs

@@ -1,25 +1,24 @@
 using Esprima.Ast;
-using Jint.Native;
 
 namespace Jint.Runtime.Interpreter.Expressions
 {
     internal sealed class JintThisExpression : JintExpression
     {
-        public JintThisExpression(Engine engine, ThisExpression expression) : base(engine, expression)
+        public JintThisExpression(ThisExpression expression) : base(expression)
         {
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            return _engine.ResolveThisBinding();
+            return NormalCompletion(context.Engine.ResolveThisBinding());
         }
 
-        public override JsValue GetValue()
+        public override Completion GetValue(EvaluationContext context)
         {
             // need to notify correct node when taking shortcut
-            _engine._lastSyntaxNode = _expression;
+            context.LastSyntaxNode = _expression;
 
-            return _engine.ResolveThisBinding();
+            return Completion.Normal(context.Engine.ResolveThisBinding(), _expression.Location);
         }
     }
 }

+ 39 - 33
Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs

@@ -21,7 +21,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         private readonly JintExpression _argument;
         private readonly UnaryOperator _operator;
 
-        private JintUnaryExpression(Engine engine, UnaryExpression expression) : base(engine, expression)
+        private JintUnaryExpression(Engine engine, UnaryExpression expression) : base(expression)
         {
             _argument = Build(engine, expression.Argument);
             _operator = expression.Operator;
@@ -36,30 +36,36 @@ namespace Jint.Runtime.Interpreter.Expressions
                 if (!(value is null))
                 {
                     // valid for caching
-                    return new JintConstantExpression(engine, expression, EvaluateMinus(value));
+                    return new JintConstantExpression(expression, EvaluateMinus(value));
                 }
             }
 
             return new JintUnaryExpression(engine, expression);
         }
 
-        public override JsValue GetValue()
+        public override Completion GetValue(EvaluationContext context)
         {
             // need to notify correct node when taking shortcut
-            _engine._lastSyntaxNode = _expression;
+            context.LastSyntaxNode = _expression;
 
-            return (JsValue) EvaluateInternal();
+            return Completion.Normal(EvaluateJsValue(context), _expression.Location);
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
+            return NormalCompletion(EvaluateJsValue(context));
+        }
+
+        private JsValue EvaluateJsValue(EvaluationContext context)
+        {
+            var engine = context.Engine;
             switch (_operator)
             {
                 case UnaryOperator.Plus:
                 {
-                    var v = _argument.GetValue();
-                    if (_engine.Options.Interop.OperatorOverloadingAllowed &&
-                        TryOperatorOverloading(_engine, v, "op_UnaryPlus", out var result))
+                    var v = _argument.GetValue(context).Value;
+                    if (context.OperatorOverloadingAllowed &&
+                        TryOperatorOverloading(context, v, "op_UnaryPlus", out var result))
                     {
                         return result;
                     }
@@ -70,9 +76,9 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
                 case UnaryOperator.Minus:
                 {
-                    var v = _argument.GetValue();
-                    if (_engine.Options.Interop.OperatorOverloadingAllowed &&
-                        TryOperatorOverloading(_engine, v, "op_UnaryNegation", out var result))
+                    var v = _argument.GetValue(context).Value;
+                    if (context.OperatorOverloadingAllowed &&
+                        TryOperatorOverloading(context, v, "op_UnaryNegation", out var result))
                     {
                         return result;
                     }
@@ -81,9 +87,9 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
                 case UnaryOperator.BitwiseNot:
                 {
-                    var v = _argument.GetValue();
-                    if (_engine.Options.Interop.OperatorOverloadingAllowed &&
-                        TryOperatorOverloading(_engine, v, "op_OnesComplement", out var result))
+                    var v = _argument.GetValue(context).Value;
+                    if (context.OperatorOverloadingAllowed &&
+                        TryOperatorOverloading(context, v, "op_OnesComplement", out var result))
                     {
                         return result;
                     }
@@ -92,9 +98,9 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
                 case UnaryOperator.LogicalNot:
                 {
-                    var v = _argument.GetValue();
-                    if (_engine.Options.Interop.OperatorOverloadingAllowed &&
-                        TryOperatorOverloading(_engine, v, "op_LogicalNot", out var result))
+                    var v = _argument.GetValue(context).Value;
+                    if (context.OperatorOverloadingAllowed &&
+                        TryOperatorOverloading(context, v, "op_LogicalNot", out var result))
                     {
                         return result;
                     }
@@ -103,7 +109,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
 
                 case UnaryOperator.Delete:
-                    var r = _argument.Evaluate() as Reference;
+                    var r = _argument.Evaluate(context).Value as Reference;
                     if (r == null)
                     {
                         return JsBoolean.True;
@@ -113,10 +119,10 @@ namespace Jint.Runtime.Interpreter.Expressions
                     {
                         if (r.IsStrictReference())
                         {
-                            ExceptionHelper.ThrowSyntaxError(_engine.Realm);
+                            ExceptionHelper.ThrowSyntaxError(engine.Realm);
                         }
 
-                        _engine._referencePool.Return(r);
+                        engine._referencePool.Return(r);
                         return JsBoolean.True;
                     }
 
@@ -124,50 +130,50 @@ namespace Jint.Runtime.Interpreter.Expressions
                     {
                         if (r.IsSuperReference())
                         {
-                            ExceptionHelper.ThrowReferenceError(_engine.Realm, r);
+                            ExceptionHelper.ThrowReferenceError(engine.Realm, r);
                         }
 
-                        var o = TypeConverter.ToObject(_engine.Realm, r.GetBase());
+                        var o = TypeConverter.ToObject(engine.Realm, r.GetBase());
                         var deleteStatus = o.Delete(r.GetReferencedName());
                         if (!deleteStatus && r.IsStrictReference())
                         {
-                            ExceptionHelper.ThrowTypeError(_engine.Realm);
+                            ExceptionHelper.ThrowTypeError(engine.Realm);
                         }
 
-                        _engine._referencePool.Return(r);
+                        engine._referencePool.Return(r);
                         return deleteStatus ? JsBoolean.True : JsBoolean.False;
                     }
 
                     if (r.IsStrictReference())
                     {
-                        ExceptionHelper.ThrowSyntaxError(_engine.Realm);
+                        ExceptionHelper.ThrowSyntaxError(engine.Realm);
                     }
 
                     var bindings = r.GetBase().TryCast<EnvironmentRecord>();
                     var property = r.GetReferencedName();
-                    _engine._referencePool.Return(r);
+                    engine._referencePool.Return(r);
 
                     return bindings.DeleteBinding(property.ToString()) ? JsBoolean.True : JsBoolean.False;
 
                 case UnaryOperator.Void:
-                    _argument.GetValue();
+                    _argument.GetValue(context);
                     return Undefined.Instance;
 
                 case UnaryOperator.TypeOf:
                 {
-                    var value = _argument.Evaluate();
+                    var value = _argument.Evaluate(context).Value;
                     r = value as Reference;
                     if (r != null)
                     {
                         if (r.IsUnresolvableReference())
                         {
-                            _engine._referencePool.Return(r);
+                            engine._referencePool.Return(r);
                             return JsString.UndefinedString;
                         }
                     }
 
                     // TODO: double evaluation problem
-                    var v = _argument.GetValue();
+                    var v = _argument.GetValue(context).Value;
                     if (v.IsUndefined())
                     {
                         return JsString.UndefinedString;
@@ -215,7 +221,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             return JsNumber.Create(double.IsNaN(n) ? double.NaN : n * -1);
         }
 
-        internal static bool TryOperatorOverloading(Engine _engine, JsValue value, string clrName, out JsValue result)
+        internal static bool TryOperatorOverloading(EvaluationContext context, JsValue value, string clrName, out JsValue result)
         {
             var operand = value.ToObject();
 
@@ -243,7 +249,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                 if (method != null)
                 {
-                    result = method.Call(_engine, null, arguments);
+                    result = method.Call(context.Engine, null, arguments);
                     return true;
                 }
             }

+ 23 - 21
Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs

@@ -14,16 +14,16 @@ namespace Jint.Runtime.Interpreter.Expressions
         private JintIdentifierExpression _leftIdentifier;
         private bool _evalOrArguments;
 
-        public JintUpdateExpression(Engine engine, UpdateExpression expression) : base(engine, expression)
+        public JintUpdateExpression(UpdateExpression expression) : base(expression)
         {
             _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
             var expression = (UpdateExpression) _expression;
             _prefix = expression.Prefix;
-            _argument = Build(_engine, expression.Argument);
+            _argument = Build(context.Engine, expression.Argument);
             if (expression.Operator == UnaryOperator.Increment)
             {
                 _change = 1;
@@ -41,34 +41,35 @@ namespace Jint.Runtime.Interpreter.Expressions
             _evalOrArguments = _leftIdentifier?.HasEvalOrArguments == true;
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
             var fastResult = _leftIdentifier != null
-                ? UpdateIdentifier()
+                ? UpdateIdentifier(context)
                 : null;
 
-            return fastResult ?? UpdateNonIdentifier();
+            return NormalCompletion(fastResult ?? UpdateNonIdentifier(context));
         }
 
-        private object UpdateNonIdentifier()
+        private JsValue UpdateNonIdentifier(EvaluationContext context)
         {
-            var reference = _argument.Evaluate() as Reference;
+            var engine = context.Engine;
+            var reference = _argument.Evaluate(context).Value as Reference;
             if (reference is null)
             {
-                ExceptionHelper.ThrowTypeError(_engine.Realm, "Invalid left-hand side expression");
+                ExceptionHelper.ThrowTypeError(engine.Realm, "Invalid left-hand side expression");
             }
 
-            reference.AssertValid(_engine);
+            reference.AssertValid(engine.Realm);
 
-            var value = _engine.GetValue(reference, false);
+            var value = engine.GetValue(reference, false);
             var isInteger = value._type == InternalTypes.Integer;
 
             JsValue newValue = null;
 
             var operatorOverloaded = false;
-            if (_engine.Options.Interop.OperatorOverloadingAllowed)
+            if (context.OperatorOverloadingAllowed)
             {
-                if (JintUnaryExpression.TryOperatorOverloading(_engine, _argument.GetValue(), _change > 0 ? "op_Increment" : "op_Decrement", out var result))
+                if (JintUnaryExpression.TryOperatorOverloading(context, _argument.GetValue(context).Value, _change > 0 ? "op_Increment" : "op_Decrement", out var result))
                 {
                     operatorOverloaded = true;
                     newValue = result;
@@ -82,21 +83,22 @@ namespace Jint.Runtime.Interpreter.Expressions
                     : JsNumber.Create(TypeConverter.ToNumber(value) + _change);
             }
 
-            _engine.PutValue(reference, newValue);
-            _engine._referencePool.Return(reference);
+            engine.PutValue(reference, newValue);
+            engine._referencePool.Return(reference);
 
             return _prefix
                 ? newValue
                 : (isInteger || operatorOverloaded ? value : JsNumber.Create(TypeConverter.ToNumber(value)));
         }
 
-        private JsValue UpdateIdentifier()
+        private JsValue UpdateIdentifier(EvaluationContext context)
         {
             var strict = StrictModeScope.IsStrictModeCode;
             var name = _leftIdentifier._expressionName;
-            var env = _engine.ExecutionContext.LexicalEnvironment;
+            var engine = context.Engine;
+            var env = engine.ExecutionContext.LexicalEnvironment;
             if (JintEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
-                _engine,
+                engine,
                 env,
                 name,
                 strict,
@@ -105,7 +107,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
                 if (strict && _evalOrArguments)
                 {
-                    ExceptionHelper.ThrowSyntaxError(_engine.Realm);
+                    ExceptionHelper.ThrowSyntaxError(engine.Realm);
                 }
 
                 var isInteger = value._type == InternalTypes.Integer;
@@ -113,9 +115,9 @@ namespace Jint.Runtime.Interpreter.Expressions
                 JsValue newValue = null;
 
                 var operatorOverloaded = false;
-                if (_engine.Options.Interop.OperatorOverloadingAllowed)
+                if (context.OperatorOverloadingAllowed)
                 {
-                    if (JintUnaryExpression.TryOperatorOverloading(_engine, _argument.GetValue(), _change > 0 ? "op_Increment" : "op_Decrement", out var result))
+                    if (JintUnaryExpression.TryOperatorOverloading(context, _argument.GetValue(context).Value, _change > 0 ? "op_Increment" : "op_Decrement", out var result))
                     {
                         operatorOverloaded = true;
                         newValue = result;

+ 9 - 9
Jint/Runtime/Interpreter/Expressions/NullishCoalescingExpression.cs

@@ -10,7 +10,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         private readonly JintExpression _right;
         private readonly JsValue _constant;
 
-        public NullishCoalescingExpression(Engine engine, BinaryExpression expression) : base(engine, expression)
+        public NullishCoalescingExpression(Engine engine, BinaryExpression expression) : base(expression)
         {
             _left = Build(engine, expression.Left);
 
@@ -25,26 +25,26 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
         }
 
-        public override JsValue GetValue()
+        public override Completion GetValue(EvaluationContext context)
         {
             // need to notify correct node when taking shortcut
-            _engine._lastSyntaxNode = _expression;
-            return EvaluateConstantOrExpression();
+            context.LastSyntaxNode = _expression;
+            return Completion.Normal(EvaluateConstantOrExpression(context), _expression.Location);
         }
 
-        protected override object EvaluateInternal()
+        protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
-            return EvaluateConstantOrExpression();
+            return NormalCompletion(EvaluateConstantOrExpression(context));
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private JsValue EvaluateConstantOrExpression()
+        private JsValue EvaluateConstantOrExpression(EvaluationContext context)
         {
-            var left = _left.GetValue();
+            var left = _left.GetValue(context).Value;
 
             return !left.IsNullOrUndefined()
                 ? left
-                : _constant ?? _right.GetValue();
+                : _constant ?? _right.GetValue(context).Value;
         }
     }
 }

+ 6 - 6
Jint/Runtime/Interpreter/JintFunctionDefinition.cs

@@ -35,18 +35,18 @@ namespace Jint.Runtime.Interpreter
 
         public FunctionThisMode ThisMode => Strict || _engine._isStrict ? FunctionThisMode.Strict : FunctionThisMode.Global;
 
-        internal Completion Execute()
+        internal Completion Execute(EvaluationContext context)
         {
             if (Function.Expression)
             {
                 _bodyExpression ??= JintExpression.Build(_engine, (Expression) Function.Body);
-                var jsValue = _bodyExpression?.GetValue() ?? Undefined.Instance;
-                return new Completion(CompletionType.Return, jsValue, null, Function.Body.Location);
+                var jsValue = _bodyExpression?.GetValue(context).Value ?? Undefined.Instance;
+                return new Completion(CompletionType.Return, jsValue, Function.Body.Location);
             }
 
             var blockStatement = (BlockStatement) Function.Body;
-            _bodyStatementList ??= new JintStatementList(_engine, blockStatement, blockStatement.Body);
-            return _bodyStatementList.Execute();
+            _bodyStatementList ??= new JintStatementList(blockStatement, blockStatement.Body);
+            return _bodyStatementList.Execute(context);
         }
 
         internal State Initialize(FunctionInstance functionInstance)
@@ -54,7 +54,7 @@ namespace Jint.Runtime.Interpreter
             return _state ??= DoInitialize(functionInstance);
         }
 
-        internal class State
+        internal sealed class State
         {
             public bool HasRestParameter;
             public int Length;

+ 26 - 17
Jint/Runtime/Interpreter/JintStatementList.cs

@@ -6,7 +6,7 @@ using Jint.Runtime.Interpreter.Statements;
 
 namespace Jint.Runtime.Interpreter
 {
-    internal class JintStatementList
+    internal sealed class JintStatementList
     {
         private class Pair
         {
@@ -14,21 +14,29 @@ namespace Jint.Runtime.Interpreter
             internal Completion? Value;
         }
 
-        private readonly Engine _engine;
         private readonly Statement _statement;
         private readonly NodeList<Statement> _statements;
 
         private Pair[] _jintStatements;
         private bool _initialized;
 
-        public JintStatementList(Engine engine, Statement statement, NodeList<Statement> statements)
+        public JintStatementList(BlockStatement blockStatement)
+            : this(blockStatement, blockStatement.Body)
+        {
+        }
+
+        public JintStatementList(Program program)
+            : this(null, program.Body)
+        {
+        }
+
+        public JintStatementList(Statement statement, in NodeList<Statement> statements)
         {
-            _engine = engine;
             _statement = statement;
             _statements = statements;
         }
 
-        private void Initialize()
+        private void Initialize(EvaluationContext context)
         {
             var jintStatements = new Pair[_statements.Count];
             for (var i = 0; i < jintStatements.Length; i++)
@@ -36,30 +44,31 @@ namespace Jint.Runtime.Interpreter
                 var esprimaStatement = _statements[i];
                 jintStatements[i] = new Pair
                 {
-                    Statement = JintStatement.Build(_engine, esprimaStatement),
+                    Statement = JintStatement.Build(esprimaStatement),
                     // When in debug mode, don't do FastResolve: Stepping requires each statement to be actually executed.
-                    Value = _engine._isDebugMode ? null : JintStatement.FastResolve(esprimaStatement)
+                    Value = context.DebugMode ? null : JintStatement.FastResolve(esprimaStatement)
                 };
             }
             _jintStatements = jintStatements;
         }
 
-        public Completion Execute()
+        public Completion Execute(EvaluationContext context)
         {
             if (!_initialized)
             {
-                Initialize();
+                Initialize(context);
                 _initialized = true;
             }
 
+            var engine = context.Engine;
             if (_statement != null)
             {
-                _engine._lastSyntaxNode = _statement;
-                _engine.RunBeforeExecuteStatementChecks(_statement);
+                context.LastSyntaxNode = _statement;
+                engine.RunBeforeExecuteStatementChecks(_statement);
             }
 
             JintStatement s = null;
-            var c = new Completion(CompletionType.Normal, null, null, _engine._lastSyntaxNode?.Location ?? default);
+            var c = new Completion(CompletionType.Normal, null, null, context.LastSyntaxNode?.Location ?? default);
             Completion sl = c;
 
             // The value of a StatementList is the value of the last value-producing item in the StatementList
@@ -69,14 +78,14 @@ namespace Jint.Runtime.Interpreter
                 foreach (var pair in _jintStatements)
                 {
                     s = pair.Statement;
-                    c = pair.Value ?? s.Execute();
+                    c = pair.Value ?? s.Execute(context);
 
                     if (c.Type != CompletionType.Normal)
                     {
                         return new Completion(
                             c.Type,
                             c.Value ?? sl.Value,
-                            c.Identifier,
+                            c.Target,
                             c.Location);
                     }
                     sl = c;
@@ -91,7 +100,7 @@ namespace Jint.Runtime.Interpreter
             }
             catch (TypeErrorException e)
             {
-                var error = _engine.Realm.Intrinsics.TypeError.Construct(new JsValue[]
+                var error = engine.Realm.Intrinsics.TypeError.Construct(new JsValue[]
                 {
                     e.Message
                 });
@@ -99,13 +108,13 @@ namespace Jint.Runtime.Interpreter
             }
             catch (RangeErrorException e)
             {
-                var error = _engine.Realm.Intrinsics.RangeError.Construct(new JsValue[]
+                var error = engine.Realm.Intrinsics.RangeError.Construct(new JsValue[]
                 {
                     e.Message
                 });
                 c = new Completion(CompletionType.Throw, error, null, s.Location);
             }
-            return new Completion(c.Type, lastValue ?? JsValue.Undefined, c.Identifier, c.Location);
+            return new Completion(c.Type, lastValue ?? JsValue.Undefined, c.Target, c.Location);
         }
 
         /// <summary>

+ 13 - 12
Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs

@@ -9,34 +9,35 @@ namespace Jint.Runtime.Interpreter.Statements
         private JintStatementList _statementList;
         private List<Declaration> _lexicalDeclarations;
 
-        public JintBlockStatement(Engine engine, BlockStatement blockStatement) : base(engine, blockStatement)
+        public JintBlockStatement(BlockStatement blockStatement) : base(blockStatement)
         {
-            _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
-            _statementList = new JintStatementList(_engine, _statement, _statement.Body);
+            _statementList = new JintStatementList(_statement, _statement.Body);
             _lexicalDeclarations = HoistingScope.GetLexicalDeclarations(_statement);
         }
 
-        // http://www.ecma-international.org/ecma-262/6.0/#sec-blockdeclarationinstantiation
-        protected override Completion ExecuteInternal()
+        protected override bool SupportsResume => true;
+
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
             EnvironmentRecord oldEnv = null;
+            var engine = context.Engine;
             if (_lexicalDeclarations != null)
             {
-                oldEnv = _engine.ExecutionContext.LexicalEnvironment;
-                var blockEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
-                JintStatementList.BlockDeclarationInstantiation(_engine, blockEnv, _lexicalDeclarations);
-                _engine.UpdateLexicalEnvironment(blockEnv);
+                oldEnv = engine.ExecutionContext.LexicalEnvironment;
+                var blockEnv = JintEnvironment.NewDeclarativeEnvironment(engine, engine.ExecutionContext.LexicalEnvironment);
+                JintStatementList.BlockDeclarationInstantiation(engine, blockEnv, _lexicalDeclarations);
+                engine.UpdateLexicalEnvironment(blockEnv);
             }
 
-            var blockValue = _statementList.Execute();
+            var blockValue = _statementList.Execute(context);
 
             if (oldEnv is not null)
             {
-                _engine.UpdateLexicalEnvironment(oldEnv);
+                engine.UpdateLexicalEnvironment(oldEnv);
             }
 
             return blockValue;

+ 2 - 2
Jint/Runtime/Interpreter/Statements/JintBreakStatement.cs

@@ -9,12 +9,12 @@ namespace Jint.Runtime.Interpreter.Statements
     {
         private readonly string _label;
 
-        public JintBreakStatement(Engine engine, BreakStatement statement) : base(engine, statement)
+        public JintBreakStatement(BreakStatement statement) : base(statement)
         {
             _label = statement.Label?.Name;
         }
 
-        protected override Completion ExecuteInternal()
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
             return new Completion(CompletionType.Break, null, _label, Location);
         }

+ 6 - 5
Jint/Runtime/Interpreter/Statements/JintClassDeclarationStatement.cs

@@ -9,15 +9,16 @@ namespace Jint.Runtime.Interpreter.Statements
     {
         private readonly ClassDefinition _classDefinition;
 
-        public JintClassDeclarationStatement(Engine engine, ClassDeclaration classDeclaration) : base(engine, classDeclaration)
+        public JintClassDeclarationStatement(ClassDeclaration classDeclaration) : base(classDeclaration)
         {
             _classDefinition = new ClassDefinition(className: classDeclaration.Id, classDeclaration.SuperClass, classDeclaration.Body);
         }
 
-        protected override Completion ExecuteInternal()
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
-            var env = _engine.ExecutionContext.LexicalEnvironment;
-            var F = _classDefinition.BuildConstructor(_engine, env);
+            var engine = context.Engine;
+            var env = engine.ExecutionContext.LexicalEnvironment;
+            var F = _classDefinition.BuildConstructor(context, env);
 
             var classBinding = _classDefinition._className;
             if (classBinding != null)
@@ -26,7 +27,7 @@ namespace Jint.Runtime.Interpreter.Statements
                 env.InitializeBinding(classBinding, F);
             }
 
-            return new Completion(CompletionType.Normal, null, null, Location);
+            return Completion.Empty();
         }
     }
 }

+ 3 - 3
Jint/Runtime/Interpreter/Statements/JintContinueStatement.cs

@@ -9,14 +9,14 @@ namespace Jint.Runtime.Interpreter.Statements
     {
         private readonly string _labelName;
 
-        public JintContinueStatement(Engine engine, ContinueStatement statement) : base(engine, statement)
+        public JintContinueStatement(ContinueStatement statement) : base(statement)
         {
             _labelName = _statement.Label?.Name;
         }
 
-        protected override Completion ExecuteInternal()
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
-            return new Completion(CompletionType.Continue, null, _labelName, Location);
+            return new Completion(CompletionType.Continue, _labelName, Location);
         }
     }
 }

+ 5 - 4
Jint/Runtime/Interpreter/Statements/JintDebuggerStatement.cs

@@ -5,13 +5,14 @@ namespace Jint.Runtime.Interpreter.Statements
 {
     internal sealed class JintDebuggerStatement : JintStatement<DebuggerStatement>
     {
-        public JintDebuggerStatement(Engine engine, DebuggerStatement statement) : base(engine, statement)
+        public JintDebuggerStatement(DebuggerStatement statement) : base(statement)
         {
         }
 
-        protected override Completion ExecuteInternal()
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
-            switch (_engine.Options.Debugger.StatementHandling)
+            var engine = context.Engine;
+            switch (engine.Options.Debugger.StatementHandling)
             {
                 case DebuggerStatementHandling.Clr:
                     if (!System.Diagnostics.Debugger.IsAttached)
@@ -22,7 +23,7 @@ namespace Jint.Runtime.Interpreter.Statements
                     System.Diagnostics.Debugger.Break();
                     break;
                 case DebuggerStatementHandling.Script:
-                    _engine.DebugHandler?.OnBreak(_statement);
+                    engine.DebugHandler?.OnBreak(_statement);
                     break;
                 case DebuggerStatementHandling.Ignore:
                     break;

+ 17 - 13
Jint/Runtime/Interpreter/Statements/JintDoWhileStatement.cs

@@ -9,33 +9,37 @@ namespace Jint.Runtime.Interpreter.Statements
     /// </summary>
     internal sealed class JintDoWhileStatement : JintStatement<DoWhileStatement>
     {
-        private readonly JintStatement _body;
-        private readonly string _labelSetName;
-        private readonly JintExpression _test;
+        private JintStatement _body;
+        private string _labelSetName;
+        private JintExpression _test;
 
-        public JintDoWhileStatement(Engine engine, DoWhileStatement statement) : base(engine, statement)
+        public JintDoWhileStatement(DoWhileStatement statement) : base(statement)
         {
-            _body = Build(_engine, statement.Body);
-            _test = JintExpression.Build(engine, statement.Test);
-            _labelSetName = statement.LabelSet?.Name;
         }
 
-        protected override Completion ExecuteInternal()
+        protected override void Initialize(EvaluationContext context)
+        {
+            _body = Build(_statement.Body);
+            _test = JintExpression.Build(context.Engine, _statement.Test);
+            _labelSetName = _statement.LabelSet?.Name;
+        }
+
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
             JsValue v = Undefined.Instance;
             bool iterating;
 
             do
             {
-                var completion = _body.Execute();
+                var completion = _body.Execute(context);
                 if (!ReferenceEquals(completion.Value, null))
                 {
                     v = completion.Value;
                 }
 
-                if (completion.Type != CompletionType.Continue || completion.Identifier != _labelSetName)
+                if (completion.Type != CompletionType.Continue || completion.Target != _labelSetName)
                 {
-                    if (completion.Type == CompletionType.Break && (completion.Identifier == null || completion.Identifier == _labelSetName))
+                    if (completion.Type == CompletionType.Break && (completion.Target == null || completion.Target == _labelSetName))
                     {
                         return new Completion(CompletionType.Normal, v, null, Location);
                     }
@@ -46,10 +50,10 @@ namespace Jint.Runtime.Interpreter.Statements
                     }
                 }
 
-                iterating = TypeConverter.ToBoolean(_test.GetValue());
+                iterating = TypeConverter.ToBoolean(_test.GetValue(context).Value);
             } while (iterating);
 
-            return new Completion(CompletionType.Normal, v, null, Location);
+            return NormalCompletion(v);
         }
     }
 }

+ 2 - 2
Jint/Runtime/Interpreter/Statements/JintEmptyStatement.cs

@@ -4,11 +4,11 @@ namespace Jint.Runtime.Interpreter.Statements
 {
     internal sealed class JintEmptyStatement : JintStatement<EmptyStatement>
     {
-        public JintEmptyStatement(Engine engine, EmptyStatement statement) : base(engine, statement)
+        public JintEmptyStatement(EmptyStatement statement) : base(statement)
         {
         }
 
-        protected override Completion ExecuteInternal()
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
             return new Completion(CompletionType.Normal, null, null, Location);
         }

+ 17 - 6
Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs

@@ -1,21 +1,32 @@
 using Esprima.Ast;
 using Jint.Runtime.Interpreter.Expressions;
+using Jint.Runtime.References;
 
 namespace Jint.Runtime.Interpreter.Statements
 {
     internal sealed class JintExpressionStatement : JintStatement<ExpressionStatement>
     {
-        private readonly JintExpression _expression;
+        private JintExpression _expression;
 
-        public JintExpressionStatement(Engine engine, ExpressionStatement statement) : base(engine, statement)
+        public JintExpressionStatement(ExpressionStatement statement) : base(statement)
         {
-            _expression = JintExpression.Build(engine, statement.Expression);
         }
 
-        protected override Completion ExecuteInternal()
+        protected override void Initialize(EvaluationContext context)
         {
-            var value = _expression.GetValue();
-            return new Completion(CompletionType.Normal, value, null, Location);
+            _expression = JintExpression.Build(context.Engine, _statement.Expression);
+        }
+
+        protected override Completion ExecuteInternal(EvaluationContext context)
+        {
+            var result = _expression.Evaluate(context);
+
+            if (result.Type != ExpressionCompletionType.Reference)
+            {
+                return new Completion(result);
+            }
+
+            return new Completion(CompletionType.Normal, context.Engine.GetValue((Reference) result.Value, true), null, Location);
         }
     }
 }

+ 72 - 56
Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs

@@ -28,31 +28,26 @@ namespace Jint.Runtime.Interpreter.Statements
         private bool _destructuring;
         private LhsKind _lhsKind;
 
-        public JintForInForOfStatement(
-            Engine engine,
-            ForInStatement statement) : base(engine, statement)
+        public JintForInForOfStatement(ForInStatement statement) : base(statement)
         {
-            _initialized = false;
             _leftNode = statement.Left;
             _rightExpression = statement.Right;
             _forBody = statement.Body;
             _iterationKind = IterationKind.Enumerate;
         }
 
-        public JintForInForOfStatement(
-            Engine engine,
-            ForOfStatement statement) : base(engine, statement)
+        public JintForInForOfStatement(ForOfStatement statement) : base(statement)
         {
-            _initialized = false;
             _leftNode = statement.Left;
             _rightExpression = statement.Right;
             _forBody = statement.Body;
             _iterationKind = IterationKind.Iterate;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
             _lhsKind = LhsKind.Assignment;
+            var engine = context.Engine;
             if (_leftNode is VariableDeclaration variableDeclaration)
             {
                 _lhsKind = variableDeclaration.Kind == VariableDeclarationKind.Var
@@ -75,7 +70,7 @@ namespace Jint.Runtime.Interpreter.Statements
                 else
                 {
                     var identifier = (Identifier) id;
-                    _expr = new JintIdentifierExpression(_engine, identifier);
+                    _expr = new JintIdentifierExpression(identifier);
                 }
             }
             else if (_leftNode is BindingPattern bindingPattern)
@@ -85,34 +80,35 @@ namespace Jint.Runtime.Interpreter.Statements
             }
             else if (_leftNode is MemberExpression memberExpression)
             {
-                _expr = new JintMemberExpression(_engine, memberExpression);
+                _expr = new JintMemberExpression(memberExpression);
             }
             else
             {
-                _expr = new JintIdentifierExpression(_engine, (Identifier) _leftNode);
+                _expr = new JintIdentifierExpression((Identifier) _leftNode);
             }
 
-            _body = Build(_engine, _forBody);
-            _right = JintExpression.Build(_engine, _rightExpression);
+            _body = Build(_forBody);
+            _right = JintExpression.Build(engine, _rightExpression);
         }
 
-        protected override Completion ExecuteInternal()
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
-            if (!HeadEvaluation(out var keyResult))
+            if (!HeadEvaluation(context, out var keyResult))
             {
                 return new Completion(CompletionType.Normal, JsValue.Undefined, null, Location);
             }
 
-            return BodyEvaluation(_expr, _body, keyResult, IterationKind.Enumerate, _lhsKind);
+            return BodyEvaluation(context, _expr, _body, keyResult, IterationKind.Enumerate, _lhsKind);
         }
 
         /// <summary>
         /// https://tc39.es/ecma262/#sec-runtime-semantics-forin-div-ofheadevaluation-tdznames-expr-iterationkind
         /// </summary>
-        private bool HeadEvaluation(out IIterator result)
+        private bool HeadEvaluation(EvaluationContext context, out IIterator result)
         {
-            var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
-            var tdz = JintEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
+            var engine = context.Engine;
+            var oldEnv = engine.ExecutionContext.LexicalEnvironment;
+            var tdz = JintEnvironment.NewDeclarativeEnvironment(engine, oldEnv);
             if (_tdzNames != null)
             {
                 var TDZEnvRec = tdz;
@@ -122,11 +118,10 @@ namespace Jint.Runtime.Interpreter.Statements
                 }
             }
 
-            _engine.UpdateLexicalEnvironment(tdz);
-            var exprRef = _right.Evaluate();
-            _engine.UpdateLexicalEnvironment(oldEnv);
+            engine.UpdateLexicalEnvironment(tdz);
+            var exprValue = _right.GetValue(context).Value;
+            engine.UpdateLexicalEnvironment(oldEnv);
 
-            var exprValue = _engine.GetValue(exprRef, true);
             if (_iterationKind == IterationKind.Enumerate)
             {
                 if (exprValue.IsNullOrUndefined())
@@ -135,12 +130,12 @@ namespace Jint.Runtime.Interpreter.Statements
                     return false;
                 }
 
-                var obj = TypeConverter.ToObject(_engine.Realm, exprValue);
-                result = EnumeratorObjectProperties(obj);
+                var obj = TypeConverter.ToObject(engine.Realm, exprValue);
+                result = new ObjectKeyVisitor(engine, obj);
             }
             else
             {
-                result = exprValue as IIterator ?? exprValue.GetIterator(_engine.Realm);
+                result = exprValue as IIterator ?? exprValue.GetIterator(engine.Realm);
             }
 
             return true;
@@ -150,6 +145,7 @@ namespace Jint.Runtime.Interpreter.Statements
         /// https://tc39.es/ecma262/#sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset
         /// </summary>
         private Completion BodyEvaluation(
+            EvaluationContext context,
             JintExpression lhs,
             JintStatement stmt,
             IIterator iteratorRecord,
@@ -157,7 +153,8 @@ namespace Jint.Runtime.Interpreter.Statements
             LhsKind lhsKind,
             IteratorKind iteratorKind = IteratorKind.Sync)
         {
-            var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
+            var engine = context.Engine;
+            var oldEnv = engine.ExecutionContext.LexicalEnvironment;
             var v = Undefined.Instance;
             var destructuring = _destructuring;
             string lhsName = null;
@@ -179,53 +176,58 @@ namespace Jint.Runtime.Interpreter.Statements
                     if (iteratorKind == IteratorKind.Async)
                     {
                         // nextResult = await nextResult;
+                        ExceptionHelper.ThrowNotImplementedException("await");
                     }
 
                     var nextValue = nextResult.Get(CommonProperties.Value);
                     close = true;
 
-                    Reference lhsRef = null;
+                    var lhsRef = new ExpressionResult();
                     if (lhsKind != LhsKind.LexicalBinding)
                     {
                         if (!destructuring)
                         {
-                            lhsRef = (Reference) lhs.Evaluate();
+                            lhsRef = lhs.Evaluate(context);
                         }
                     }
                     else
                     {
-                        iterationEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
+                        iterationEnv = JintEnvironment.NewDeclarativeEnvironment(engine, oldEnv);
                         if (_tdzNames != null)
                         {
                             BindingInstantiation(iterationEnv);
                         }
-                        _engine.UpdateLexicalEnvironment(iterationEnv);
+                        engine.UpdateLexicalEnvironment(iterationEnv);
 
                         if (!destructuring)
                         {
-                            lhsName ??= ((Identifier) ((VariableDeclaration) _leftNode).Declarations[0].Id).Name;
-                            lhsRef = _engine.ResolveBinding(lhsName);
+                            var identifier = (Identifier) ((VariableDeclaration) _leftNode).Declarations[0].Id;
+                            lhsName ??= identifier.Name;
+                            lhsRef = new ExpressionResult(ExpressionCompletionType.Normal, engine.ResolveBinding(lhsName), identifier.Location);
                         }
                     }
 
+                    var status = new Completion();
                     if (!destructuring)
                     {
-                        // If lhsRef is an abrupt completion, then
-                        // Let status be lhsRef.
-
-                        if (lhsKind == LhsKind.LexicalBinding)
+                        if (lhsRef.IsAbrupt())
+                        {
+                            close = true;
+                            status = new Completion(lhsRef);
+                        }
+                        else if (lhsKind == LhsKind.LexicalBinding)
                         {
-                            lhsRef.InitializeReferencedBinding(nextValue);
+                            ((Reference) lhsRef.Value).InitializeReferencedBinding(nextValue);
                         }
                         else
                         {
-                            _engine.PutValue(lhsRef, nextValue);
+                            engine.PutValue((Reference) lhsRef.Value, nextValue);
                         }
                     }
                     else
                     {
-                        BindingPatternAssignmentExpression.ProcessPatterns(
-                            _engine,
+                        status = BindingPatternAssignmentExpression.ProcessPatterns(
+                            context,
                             _assignmentPattern,
                             nextValue,
                             iterationEnv,
@@ -245,25 +247,44 @@ namespace Jint.Runtime.Interpreter.Statements
                         }
                     }
 
-                    var result = stmt.Execute();
-                    _engine.UpdateLexicalEnvironment(oldEnv);
+                    if (status.IsAbrupt())
+                    {
+                        engine.UpdateLexicalEnvironment(oldEnv);
+                        if (_iterationKind == IterationKind.AsyncIterate)
+                        {
+                            iteratorRecord.Close(status.Type);
+                            return status;
+                        }
+
+                        if (iterationKind == IterationKind.Enumerate)
+                        {
+                            return status;
+                        }
+
+                        iteratorRecord.Close(status.Type);
+                        return status;
+                    }
+
+                    var result = stmt.Execute(context);
+                    engine.UpdateLexicalEnvironment(oldEnv);
 
                     if (!ReferenceEquals(result.Value, null))
                     {
                         v = result.Value;
                     }
 
-                    if (result.Type == CompletionType.Break && (result.Identifier == null || result.Identifier == _statement?.LabelSet?.Name))
+                    if (result.Type == CompletionType.Break && (result.Target == null || result.Target == _statement?.LabelSet?.Name))
                     {
                         completionType = CompletionType.Normal;
                         return new Completion(CompletionType.Normal, v, null, Location);
                     }
 
-                    if (result.Type != CompletionType.Continue || (result.Identifier != null && result.Identifier != _statement?.LabelSet?.Name))
+                    if (result.Type != CompletionType.Continue || (result.Target != null && result.Target != _statement?.LabelSet?.Name))
                     {
                         completionType = result.Type;
-                        if (result.Type != CompletionType.Normal)
+                        if (result.IsAbrupt())
                         {
+                            close = true;
                             return result;
                         }
                     }
@@ -291,7 +312,7 @@ namespace Jint.Runtime.Interpreter.Statements
                         }
                     }
                 }
-                _engine.UpdateLexicalEnvironment(oldEnv);
+                engine.UpdateLexicalEnvironment(oldEnv);
             }
         }
 
@@ -328,11 +349,6 @@ namespace Jint.Runtime.Interpreter.Statements
             Async
         }
 
-        private IIterator EnumeratorObjectProperties(ObjectInstance obj)
-        {
-            return new ObjectKeyVisitor(_engine, obj);
-        }
-
         private enum IterationKind
         {
             Enumerate,
@@ -343,11 +359,11 @@ namespace Jint.Runtime.Interpreter.Statements
         private sealed class ObjectKeyVisitor : IteratorInstance
         {
             public ObjectKeyVisitor(Engine engine, ObjectInstance obj)
-                : base(engine, CreateEnumerator(engine, obj))
+                : base(engine, CreateEnumerator(obj))
             {
             }
 
-            private static IEnumerable<JsValue> CreateEnumerator(Engine engine, ObjectInstance obj)
+            private static IEnumerable<JsValue> CreateEnumerator(ObjectInstance obj)
             {
                 var visited = new HashSet<JsValue>();
                 foreach (var key in obj.GetOwnPropertyKeys(Types.String))
@@ -368,7 +384,7 @@ namespace Jint.Runtime.Interpreter.Statements
                     yield break;
                 }
 
-                foreach (var protoKey in CreateEnumerator(engine, obj.Prototype))
+                foreach (var protoKey in CreateEnumerator(obj.Prototype))
                 {
                     if (!visited.Contains(protoKey))
                     {

+ 37 - 36
Jint/Runtime/Interpreter/Statements/JintForStatement.cs

@@ -13,23 +13,23 @@ namespace Jint.Runtime.Interpreter.Statements
     {
         private JintVariableDeclaration _initStatement;
         private JintExpression _initExpression;
-        
+
         private JintExpression _test;
         private JintExpression _increment;
-        
+
         private JintStatement _body;
         private List<string> _boundNames;
 
         private bool _shouldCreatePerIterationEnvironment;
 
-        public JintForStatement(Engine engine, ForStatement statement) : base(engine, statement)
+        public JintForStatement(ForStatement statement) : base(statement)
         {
-            _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
-            _body = Build(_engine, _statement.Body);
+            var engine = context.Engine;
+            _body = Build(_statement.Body);
 
             if (_statement.Init != null)
             {
@@ -41,34 +41,35 @@ namespace Jint.Runtime.Interpreter.Statements
                         _boundNames = new List<string>();
                         d.GetBoundNames(_boundNames);
                     }
-                    _initStatement = new JintVariableDeclaration(_engine, d);
+                    _initStatement = new JintVariableDeclaration(d);
                     _shouldCreatePerIterationEnvironment = d.Kind == VariableDeclarationKind.Let;
                 }
                 else
                 {
-                    _initExpression = JintExpression.Build(_engine, (Expression) _statement.Init);
+                    _initExpression = JintExpression.Build(engine, (Expression) _statement.Init);
                 }
             }
 
             if (_statement.Test != null)
             {
-                _test = JintExpression.Build(_engine, _statement.Test);
+                _test = JintExpression.Build(engine, _statement.Test);
             }
 
             if (_statement.Update != null)
             {
-                _increment = JintExpression.Build(_engine, _statement.Update);
+                _increment = JintExpression.Build(engine, _statement.Update);
             }
         }
 
-        protected override Completion ExecuteInternal()
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
             EnvironmentRecord oldEnv = null;
             EnvironmentRecord loopEnv = null;
+            var engine = context.Engine;
             if (_boundNames != null)
             {
-                oldEnv = _engine.ExecutionContext.LexicalEnvironment;
-                loopEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
+                oldEnv = engine.ExecutionContext.LexicalEnvironment;
+                loopEnv = JintEnvironment.NewDeclarativeEnvironment(engine, oldEnv);
                 var loopEnvRec = loopEnv;
                 var kind = _initStatement._statement.Kind;
                 for (var i = 0; i < _boundNames.Count; i++)
@@ -84,27 +85,27 @@ namespace Jint.Runtime.Interpreter.Statements
                     }
                 }
 
-                _engine.UpdateLexicalEnvironment(loopEnv);
+                engine.UpdateLexicalEnvironment(loopEnv);
             }
 
             try
             {
                 if (_initExpression != null)
                 {
-                    _initExpression?.GetValue();
+                    _initExpression?.GetValue(context);
                 }
                 else
                 {
-                    _initStatement?.Execute();
+                    _initStatement?.Execute(context);
                 }
 
-                return ForBodyEvaluation();
+                return ForBodyEvaluation(context);
             }
             finally
             {
                 if (oldEnv is not null)
                 {
-                    _engine.UpdateLexicalEnvironment(oldEnv);
+                    engine.UpdateLexicalEnvironment(oldEnv);
                 }
             }
         }
@@ -112,37 +113,37 @@ namespace Jint.Runtime.Interpreter.Statements
         /// <summary>
         /// https://tc39.es/ecma262/#sec-forbodyevaluation
         /// </summary>
-        private Completion ForBodyEvaluation()
+        private Completion ForBodyEvaluation(EvaluationContext context)
         {
             var v = Undefined.Instance;
 
             if (_shouldCreatePerIterationEnvironment)
             {
-                CreatePerIterationEnvironment();
+                CreatePerIterationEnvironment(context);
             }
 
             while (true)
             {
                 if (_test != null)
                 {
-                    if (!TypeConverter.ToBoolean(_test.GetValue()))
+                    if (!TypeConverter.ToBoolean(_test.GetValue(context).Value))
                     {
-                        return new Completion(CompletionType.Normal, v, null, Location);
+                        return NormalCompletion(v);
                     }
                 }
 
-                var result = _body.Execute();
+                var result = _body.Execute(context);
                 if (!ReferenceEquals(result.Value, null))
                 {
                     v = result.Value;
                 }
 
-                if (result.Type == CompletionType.Break && (result.Identifier == null || result.Identifier == _statement?.LabelSet?.Name))
+                if (result.Type == CompletionType.Break && (result.Target == null || result.Target == _statement?.LabelSet?.Name))
                 {
-                    return new Completion(CompletionType.Normal, result.Value, null, Location);
+                    return NormalCompletion(result.Value);
                 }
 
-                if (result.Type != CompletionType.Continue || (result.Identifier != null && result.Identifier != _statement?.LabelSet?.Name))
+                if (result.Type != CompletionType.Continue || (result.Target != null && result.Target != _statement?.LabelSet?.Name))
                 {
                     if (result.Type != CompletionType.Normal)
                     {
@@ -152,34 +153,34 @@ namespace Jint.Runtime.Interpreter.Statements
 
                 if (_shouldCreatePerIterationEnvironment)
                 {
-                    CreatePerIterationEnvironment();
+                    CreatePerIterationEnvironment(context);
                 }
 
-                _increment?.GetValue();
+                _increment?.GetValue(context);
             }
         }
 
-        private void CreatePerIterationEnvironment()
+        private void CreatePerIterationEnvironment(EvaluationContext context)
         {
             if (_boundNames == null || _boundNames.Count == 0)
             {
                 return;
             }
-            
-            var lastIterationEnv = _engine.ExecutionContext.LexicalEnvironment;
+
+            var engine = context.Engine;
+            var lastIterationEnv = engine.ExecutionContext.LexicalEnvironment;
             var lastIterationEnvRec = lastIterationEnv;
             var outer = lastIterationEnv._outerEnv;
-            var thisIterationEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, outer);
-            var thisIterationEnvRec = (DeclarativeEnvironmentRecord) thisIterationEnv;
-            
+            var thisIterationEnv = JintEnvironment.NewDeclarativeEnvironment(engine, outer);
+
             for (var j = 0; j < _boundNames.Count; j++)
             {
                 var bn = _boundNames[j];
                 var lastValue = lastIterationEnvRec.GetBindingValue(bn, true);
-                thisIterationEnvRec.CreateMutableBindingAndInitialize(bn, false, lastValue);
+                thisIterationEnv.CreateMutableBindingAndInitialize(bn, false, lastValue);
             }
 
-            _engine.UpdateLexicalEnvironment(thisIterationEnv);
+            engine.UpdateLexicalEnvironment(thisIterationEnv);
         }
     }
 }

+ 4 - 3
Jint/Runtime/Interpreter/Statements/JintFunctionDeclarationStatement.cs

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

+ 16 - 12
Jint/Runtime/Interpreter/Statements/JintIfStatement.cs

@@ -5,31 +5,35 @@ namespace Jint.Runtime.Interpreter.Statements
 {
     internal sealed class JintIfStatement : JintStatement<IfStatement>
     {
-        private readonly JintStatement _statementConsequent;
-        private readonly JintExpression _test;
-        private readonly JintStatement _alternate;
+        private JintStatement _statementConsequent;
+        private JintExpression _test;
+        private JintStatement _alternate;
 
-        public JintIfStatement(Engine engine, IfStatement statement) : base(engine, statement)
+        public JintIfStatement(IfStatement statement) : base(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()
+        protected override void Initialize(EvaluationContext context)
+        {
+            _statementConsequent = Build(_statement.Consequent);
+            _test = JintExpression.Build(context.Engine, _statement.Test);
+            _alternate = _statement.Alternate != null ? Build(_statement.Alternate) : null;
+        }
+
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
             Completion result;
-            if (TypeConverter.ToBoolean(_test.GetValue()))
+            if (TypeConverter.ToBoolean(_test.GetValue(context).Value))
             {
-                result = _statementConsequent.Execute();
+                result = _statementConsequent.Execute(context);
             }
             else if (_alternate != null)
             {
-                result = _alternate.Execute();
+                result = _alternate.Execute(context);
             }
             else
             {
-                return new Completion(CompletionType.Normal, null, null, Location);
+                return new Completion(CompletionType.Normal, null, Location);
             }
 
             return result;

+ 12 - 8
Jint/Runtime/Interpreter/Statements/JintLabeledStatement.cs

@@ -4,22 +4,26 @@ namespace Jint.Runtime.Interpreter.Statements
 {
     internal sealed class JintLabeledStatement : JintStatement<LabeledStatement>
     {
-        private readonly JintStatement _body;
-        private readonly string _labelName;
+        private JintStatement _body;
+        private string _labelName;
 
-        public JintLabeledStatement(Engine engine, LabeledStatement statement) : base(engine, statement)
+        public JintLabeledStatement(LabeledStatement statement) : base(statement)
         {
-            _body = Build(engine, statement.Body);
-            _labelName = statement.Label.Name;
         }
 
-        protected override Completion ExecuteInternal()
+        protected override void Initialize(EvaluationContext context)
+        {
+            _body = Build(_statement.Body);
+            _labelName = _statement.Label.Name;
+        }
+
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
             // 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 result = _body.Execute(context);
+            if (result.Type == CompletionType.Break && result.Target == _labelName)
             {
                 var value = result.Value;
                 return new Completion(CompletionType.Normal, value, null, Location);

+ 9 - 5
Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs

@@ -9,18 +9,22 @@ namespace Jint.Runtime.Interpreter.Statements
     /// </summary>
     internal sealed class JintReturnStatement : JintStatement<ReturnStatement>
     {
-        private readonly JintExpression _argument;
+        private JintExpression _argument;
 
-        public JintReturnStatement(Engine engine, ReturnStatement statement) : base(engine, statement)
+        public JintReturnStatement(ReturnStatement statement) : base(statement)
+        {
+        }
+
+        protected override void Initialize(EvaluationContext context)
         {
             _argument = _statement.Argument != null
-                ? JintExpression.Build(engine, _statement.Argument)
+                ? JintExpression.Build(context.Engine, _statement.Argument)
                 : null;
         }
 
-        protected override Completion ExecuteInternal()
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
-            var jsValue = _argument?.GetValue() ?? Undefined.Instance;
+            var jsValue = _argument?.GetValue(context).Value ?? Undefined.Instance;
             return new Completion(CompletionType.Return, jsValue, null, Location);
         }
     }

+ 4 - 4
Jint/Runtime/Interpreter/Statements/JintScript.cs

@@ -6,14 +6,14 @@ namespace Jint.Runtime.Interpreter.Statements
     {
         private readonly JintStatementList _list;
 
-        public JintScript(Engine engine, Script script) : base(engine, script)
+        public JintScript(Script script) : base(script)
         {
-            _list = new JintStatementList(_engine, null, _statement.Body);
+            _list = new JintStatementList(script);
         }
 
-        protected override Completion ExecuteInternal()
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
-            return _list.Execute();
+            return _list.Execute(context);
         }
     }
 }

+ 56 - 38
Jint/Runtime/Interpreter/Statements/JintStatement.cs

@@ -1,6 +1,8 @@
-using System.Runtime.CompilerServices;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
 using Esprima;
 using Esprima.Ast;
+using Jint.Native;
 using Jint.Runtime.Interpreter.Expressions;
 
 namespace Jint.Runtime.Interpreter.Statements
@@ -9,7 +11,7 @@ namespace Jint.Runtime.Interpreter.Statements
     {
         internal readonly T _statement;
 
-        protected JintStatement(Engine engine, T statement) : base(engine, statement)
+        protected JintStatement(T statement) : base(statement)
         {
             _statement = statement;
         }
@@ -17,73 +19,76 @@ namespace Jint.Runtime.Interpreter.Statements
 
     internal abstract class JintStatement
     {
-        protected readonly Engine _engine;
         private readonly Statement _statement;
+        private bool _initialized;
 
-        // require sub-classes to set to false explicitly to skip virtual call
-        protected bool _initialized = true;
-
-        protected JintStatement(Engine engine, Statement statement)
+        protected JintStatement(Statement statement)
         {
-            _engine = engine;
             _statement = statement;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public Completion Execute()
+        public Completion Execute(EvaluationContext context)
         {
             if (_statement.Type != Nodes.BlockStatement)
             {
-                _engine._lastSyntaxNode = _statement;
-                _engine.RunBeforeExecuteStatementChecks(_statement);
+                context.LastSyntaxNode = _statement;
+                context.Engine.RunBeforeExecuteStatementChecks(_statement);
             }
 
             if (!_initialized)
             {
-                Initialize();
+                Initialize(context);
                 _initialized = true;
             }
 
-            return ExecuteInternal();
+            if (context.ResumedCompletion.IsAbrupt() && !SupportsResume)
+            {
+                return NormalCompletion(JsValue.Undefined);
+            }
+
+            return ExecuteInternal(context);
         }
 
-        protected abstract Completion ExecuteInternal();
+        protected virtual bool SupportsResume => false;
+
+        protected abstract Completion ExecuteInternal(EvaluationContext context);
 
         public Location Location => _statement.Location;
 
         /// <summary>
         /// Opportunity to build one-time structures and caching based on lexical context.
         /// </summary>
-        protected virtual void Initialize()
+        /// <param name="context"></param>
+        protected virtual void Initialize(EvaluationContext context)
         {
         }
 
-        protected internal static JintStatement Build(Engine engine, Statement statement)
+        protected internal static JintStatement Build(Statement statement)
         {
             JintStatement result = statement.Type switch
             {
-                Nodes.BlockStatement => new JintBlockStatement(engine, (BlockStatement) statement),
-                Nodes.ReturnStatement => new JintReturnStatement(engine, (ReturnStatement) statement),
-                Nodes.VariableDeclaration => new JintVariableDeclaration(engine, (VariableDeclaration) statement),
-                Nodes.BreakStatement => new JintBreakStatement(engine, (BreakStatement) statement),
-                Nodes.ContinueStatement => new JintContinueStatement(engine, (ContinueStatement) statement),
-                Nodes.DoWhileStatement => new JintDoWhileStatement(engine, (DoWhileStatement) statement),
-                Nodes.EmptyStatement => new JintEmptyStatement(engine, (EmptyStatement) statement),
-                Nodes.ExpressionStatement => new JintExpressionStatement(engine, (ExpressionStatement) statement),
-                Nodes.ForStatement => new JintForStatement(engine, (ForStatement) statement),
-                Nodes.ForInStatement => new JintForInForOfStatement(engine, (ForInStatement) statement),
-                Nodes.ForOfStatement => new JintForInForOfStatement(engine, (ForOfStatement) statement),
-                Nodes.IfStatement => new JintIfStatement(engine, (IfStatement) statement),
-                Nodes.LabeledStatement => new JintLabeledStatement(engine, (LabeledStatement) statement),
-                Nodes.SwitchStatement => new JintSwitchStatement(engine, (SwitchStatement) statement),
-                Nodes.FunctionDeclaration => new JintFunctionDeclarationStatement(engine, (FunctionDeclaration) statement),
-                Nodes.ThrowStatement => new JintThrowStatement(engine, (ThrowStatement) statement),
-                Nodes.TryStatement => new JintTryStatement(engine, (TryStatement) statement),
-                Nodes.WhileStatement => new JintWhileStatement(engine, (WhileStatement) statement),
-                Nodes.WithStatement => new JintWithStatement(engine, (WithStatement) statement),
-                Nodes.DebuggerStatement => new JintDebuggerStatement(engine, (DebuggerStatement) statement),
-                Nodes.Program when statement is Script s => new JintScript(engine, s),
-                Nodes.ClassDeclaration => new JintClassDeclarationStatement(engine, (ClassDeclaration) statement),
+                Nodes.BlockStatement => new JintBlockStatement((BlockStatement) statement),
+                Nodes.ReturnStatement => new JintReturnStatement((ReturnStatement) statement),
+                Nodes.VariableDeclaration => new JintVariableDeclaration((VariableDeclaration) statement),
+                Nodes.BreakStatement => new JintBreakStatement((BreakStatement) statement),
+                Nodes.ContinueStatement => new JintContinueStatement((ContinueStatement) statement),
+                Nodes.DoWhileStatement => new JintDoWhileStatement((DoWhileStatement) statement),
+                Nodes.EmptyStatement => new JintEmptyStatement((EmptyStatement) statement),
+                Nodes.ExpressionStatement => new JintExpressionStatement((ExpressionStatement) statement),
+                Nodes.ForStatement => new JintForStatement((ForStatement) statement),
+                Nodes.ForInStatement => new JintForInForOfStatement((ForInStatement) statement),
+                Nodes.ForOfStatement => new JintForInForOfStatement((ForOfStatement) statement),
+                Nodes.IfStatement => new JintIfStatement((IfStatement) statement),
+                Nodes.LabeledStatement => new JintLabeledStatement((LabeledStatement) statement),
+                Nodes.SwitchStatement => new JintSwitchStatement((SwitchStatement) statement),
+                Nodes.FunctionDeclaration => new JintFunctionDeclarationStatement((FunctionDeclaration) statement),
+                Nodes.ThrowStatement => new JintThrowStatement((ThrowStatement) statement),
+                Nodes.TryStatement => new JintTryStatement((TryStatement) statement),
+                Nodes.WhileStatement => new JintWhileStatement((WhileStatement) statement),
+                Nodes.WithStatement => new JintWithStatement((WithStatement) statement),
+                Nodes.DebuggerStatement => new JintDebuggerStatement((DebuggerStatement) statement),
+                Nodes.ClassDeclaration => new JintClassDeclarationStatement((ClassDeclaration) statement),
                 _ => null
             };
 
@@ -108,5 +113,18 @@ namespace Jint.Runtime.Interpreter.Statements
 
             return null;
         }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-normalcompletion
+        /// </summary>
+        /// <remarks>
+        /// We use custom type that is translated to Completion later on.
+        /// </remarks>
+        [DebuggerStepThrough]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        protected Completion NormalCompletion(JsValue value)
+        {
+            return new Completion(CompletionType.Normal, value, _statement.Location);
+        }
     }
 }

+ 22 - 22
Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs

@@ -9,36 +9,36 @@ namespace Jint.Runtime.Interpreter.Statements
 {
     internal sealed class JintSwitchBlock
     {
-        private readonly Engine _engine;
         private readonly NodeList<SwitchCase> _switchBlock;
         private JintSwitchCase[] _jintSwitchBlock;
         private bool _initialized;
 
-        public JintSwitchBlock(Engine engine, NodeList<SwitchCase> switchBlock)
+        public JintSwitchBlock(NodeList<SwitchCase> switchBlock)
         {
-            _engine = engine;
             _switchBlock = switchBlock;
         }
 
-        private void Initialize()
+        private void Initialize(EvaluationContext context)
         {
+            var engine = context.Engine;
             _jintSwitchBlock = new JintSwitchCase[_switchBlock.Count];
             for (var i = 0; i < _jintSwitchBlock.Length; i++)
             {
-                _jintSwitchBlock[i] = new JintSwitchCase(_engine, _switchBlock[i]);
+                _jintSwitchBlock[i] = new JintSwitchCase(engine, _switchBlock[i]);
             }
         }
 
-        public Completion Execute(JsValue input)
+        public Completion Execute(EvaluationContext context, JsValue input)
         {
             if (!_initialized)
             {
-                Initialize();
+                Initialize(context);
                 _initialized = true;
             }
 
+            var engine = context.Engine;
             JsValue v = Undefined.Instance;
-            Location l = _engine._lastSyntaxNode.Location;
+            Location l = context.LastSyntaxNode.Location;
             JintSwitchCase defaultCase = null;
             bool hit = false;
 
@@ -49,10 +49,10 @@ namespace Jint.Runtime.Interpreter.Statements
                 EnvironmentRecord oldEnv = null;
                 if (clause.LexicalDeclarations != null)
                 {
-                    oldEnv = _engine.ExecutionContext.LexicalEnvironment;
-                    var blockEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
-                    JintStatementList.BlockDeclarationInstantiation(_engine, blockEnv, clause.LexicalDeclarations);
-                    _engine.UpdateLexicalEnvironment(blockEnv);
+                    oldEnv = engine.ExecutionContext.LexicalEnvironment;
+                    var blockEnv = JintEnvironment.NewDeclarativeEnvironment(engine, oldEnv);
+                    JintStatementList.BlockDeclarationInstantiation(engine, blockEnv, clause.LexicalDeclarations);
+                    engine.UpdateLexicalEnvironment(blockEnv);
                 }
 
                 if (clause.Test == null)
@@ -61,7 +61,7 @@ namespace Jint.Runtime.Interpreter.Statements
                 }
                 else
                 {
-                    var clauseSelector = clause.Test.GetValue();
+                    var clauseSelector = clause.Test.GetValue(context).Value;
                     if (JintBinaryExpression.StrictlyEqual(clauseSelector, input))
                     {
                         hit = true;
@@ -70,11 +70,11 @@ namespace Jint.Runtime.Interpreter.Statements
 
                 if (hit && clause.Consequent != null)
                 {
-                    var r = clause.Consequent.Execute();
+                    var r = clause.Consequent.Execute(context);
 
                     if (oldEnv is not null)
                     {
-                        _engine.UpdateLexicalEnvironment(oldEnv);
+                        engine.UpdateLexicalEnvironment(oldEnv);
                     }
 
                     if (r.Type != CompletionType.Normal)
@@ -93,17 +93,17 @@ namespace Jint.Runtime.Interpreter.Statements
                 EnvironmentRecord oldEnv = null;
                 if (defaultCase.LexicalDeclarations != null)
                 {
-                    oldEnv = _engine.ExecutionContext.LexicalEnvironment;
-                    var blockEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
-                    JintStatementList.BlockDeclarationInstantiation(_engine, blockEnv, defaultCase.LexicalDeclarations);
-                    _engine.UpdateLexicalEnvironment(blockEnv);
+                    oldEnv = engine.ExecutionContext.LexicalEnvironment;
+                    var blockEnv = JintEnvironment.NewDeclarativeEnvironment(engine, oldEnv);
+                    JintStatementList.BlockDeclarationInstantiation(engine, blockEnv, defaultCase.LexicalDeclarations);
+                    engine.UpdateLexicalEnvironment(blockEnv);
                 }
 
-                var r = defaultCase.Consequent.Execute();
+                var r = defaultCase.Consequent.Execute(context);
 
                 if (oldEnv is not null)
                 {
-                    _engine.UpdateLexicalEnvironment(oldEnv);
+                    engine.UpdateLexicalEnvironment(oldEnv);
                 }
                 if (r.Type != CompletionType.Normal)
                 {
@@ -125,7 +125,7 @@ namespace Jint.Runtime.Interpreter.Statements
 
             public JintSwitchCase(Engine engine, SwitchCase switchCase)
             {
-                Consequent = new JintStatementList(engine, null, switchCase.Consequent);
+                Consequent = new JintStatementList(null, switchCase.Consequent);
                 LexicalDeclarations = HoistingScope.GetLexicalDeclarations(switchCase);
 
                 if (switchCase.Test != null)

+ 10 - 10
Jint/Runtime/Interpreter/Statements/JintSwitchStatement.cs

@@ -11,24 +11,24 @@ namespace Jint.Runtime.Interpreter.Statements
         private JintSwitchBlock _switchBlock;
         private JintExpression _discriminant;
 
-        public JintSwitchStatement(Engine engine, SwitchStatement statement) : base(engine, statement)
+        public JintSwitchStatement(SwitchStatement statement) : base(statement)
         {
-            _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
-            _switchBlock = new JintSwitchBlock(_engine, _statement.Cases);
-            _discriminant = JintExpression.Build(_engine, _statement.Discriminant);
+            var engine = context.Engine;
+            _switchBlock = new JintSwitchBlock(_statement.Cases);
+            _discriminant = JintExpression.Build(engine, _statement.Discriminant);
         }
 
-        protected override Completion ExecuteInternal()
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
-            var jsValue = _discriminant.GetValue();
-            var r = _switchBlock.Execute(jsValue);
-            if (r.Type == CompletionType.Break && r.Identifier == _statement.LabelSet?.Name)
+            var value = _discriminant.GetValue(context).Value;
+            var r = _switchBlock.Execute(context, value);
+            if (r.Type == CompletionType.Break && r.Target == _statement.LabelSet?.Name)
             {
-                return new Completion(CompletionType.Normal, r.Value, null, Location);
+                return NormalCompletion(r.Value);
             }
 
             return r;

+ 5 - 6
Jint/Runtime/Interpreter/Statements/JintThrowStatement.cs

@@ -10,19 +10,18 @@ namespace Jint.Runtime.Interpreter.Statements
     {
         private JintExpression _argument;
 
-        public JintThrowStatement(Engine engine, ThrowStatement statement) : base(engine, statement)
+        public JintThrowStatement(ThrowStatement statement) : base(statement)
         {
-            _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
-            _argument = JintExpression.Build(_engine, _statement.Argument);
+            _argument = JintExpression.Build(context.Engine, _statement.Argument);
         }
 
-        protected override Completion ExecuteInternal()
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
-            var jsValue = _argument.GetValue();
+            var jsValue = _argument.GetValue(context).Value;
             return new Completion(CompletionType.Throw, jsValue, null, _statement.Location);
         }
     }

+ 29 - 20
Jint/Runtime/Interpreter/Statements/JintTryStatement.cs

@@ -6,34 +6,43 @@ using Jint.Runtime.Environments;
 namespace Jint.Runtime.Interpreter.Statements
 {
     /// <summary>
-    /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.14
+    /// https://tc39.es/ecma262/#sec-try-statement
     /// </summary>
     internal sealed class JintTryStatement : JintStatement<TryStatement>
     {
-        private readonly JintStatement _block;
+        private JintStatement _block;
         private JintStatement _catch;
-        private readonly JintStatement _finalizer;
+        private JintStatement _finalizer;
 
-        public JintTryStatement(Engine engine, TryStatement statement) : base(engine, statement)
+        public JintTryStatement(TryStatement statement) : base(statement)
         {
-            _block = Build(engine, statement.Block);
-            if (statement.Finalizer != null)
+
+        }
+
+        protected override void Initialize(EvaluationContext context)
+        {
+            _block = Build(_statement.Block);
+            if (_statement.Finalizer != null)
             {
-                _finalizer = Build(engine, _statement.Finalizer);
+                _finalizer = Build(_statement.Finalizer);
             }
         }
 
-        protected override Completion ExecuteInternal()
+        protected override bool SupportsResume => true;
+
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
-            int callStackSizeBeforeExecution = _engine.CallStack.Count;
+            var engine = context.Engine;
+            int callStackSizeBeforeExecution = engine.CallStack.Count;
+
+            var b = _block.Execute(context);
 
-            var b = _block.Execute();
             if (b.Type == CompletionType.Throw)
             {
                 // initialize lazily
                 if (_statement.Handler is not null && _catch is null)
                 {
-                    _catch = Build(_engine, _statement.Handler.Body);
+                    _catch = Build(_statement.Handler.Body);
                 }
 
                 // execute catch
@@ -44,16 +53,16 @@ namespace Jint.Runtime.Interpreter.Statements
                     // from a call, regardless of whether it throws (i.e. CallStack.Pop() in finally clause
                     // in Engine.Call/Engine.Construct - however, that method currently breaks stack traces
                     // in error messages.
-                    while (callStackSizeBeforeExecution < _engine.CallStack.Count)
+                    while (callStackSizeBeforeExecution < engine.CallStack.Count)
                     {
-                        _engine.CallStack.Pop();
+                        engine.CallStack.Pop();
                     }
 
                     // https://tc39.es/ecma262/#sec-runtime-semantics-catchclauseevaluation
 
                     var thrownValue = b.Value;
-                    var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
-                    var catchEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, oldEnv, catchEnvironment: true);
+                    var oldEnv = engine.ExecutionContext.LexicalEnvironment;
+                    var catchEnv = JintEnvironment.NewDeclarativeEnvironment(engine, oldEnv, catchEnvironment: true);
 
                     var boundNames = new List<string>();
                     _statement.Handler.Param.GetBoundNames(boundNames);
@@ -63,20 +72,20 @@ namespace Jint.Runtime.Interpreter.Statements
                         catchEnv.CreateMutableBinding(argName, false);
                     }
 
-                    _engine.UpdateLexicalEnvironment(catchEnv);
+                    engine.UpdateLexicalEnvironment(catchEnv);
 
                     var catchParam = _statement.Handler?.Param;
-                    catchParam.BindingInitialization(_engine, thrownValue, catchEnv);
+                    catchParam.BindingInitialization(context, thrownValue, catchEnv);
 
-                    b = _catch.Execute();
+                    b = _catch.Execute(context);
 
-                    _engine.UpdateLexicalEnvironment(oldEnv);
+                    engine.UpdateLexicalEnvironment(oldEnv);
                 }
             }
 
             if (_finalizer != null)
             {
-                var f = _finalizer.Execute();
+                var f = _finalizer.Execute(context);
                 if (f.Type == CompletionType.Normal)
                 {
                     return b;

+ 24 - 25
Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs

@@ -8,7 +8,7 @@ namespace Jint.Runtime.Interpreter.Statements
 {
     internal sealed class JintVariableDeclaration : JintStatement<VariableDeclaration>
     {
-        private static readonly Completion VoidCompletion = new Completion(CompletionType.Normal, null, null, default);
+        private static readonly Completion VoidCompletion = new(CompletionType.Normal, null!, default);
 
         private ResolvedDeclaration[] _declarations;
 
@@ -21,13 +21,13 @@ namespace Jint.Runtime.Interpreter.Statements
             internal bool EvalOrArguments;
         }
 
-        public JintVariableDeclaration(Engine engine, VariableDeclaration statement) : base(engine, statement)
+        public JintVariableDeclaration(VariableDeclaration statement) : base(statement)
         {
-            _initialized = false;
         }
 
-        protected override void Initialize()
+        protected override void Initialize(EvaluationContext context)
         {
+            var engine = context.Engine;
             _declarations = new ResolvedDeclaration[_statement.Declarations.Count];
             for (var i = 0; i < _declarations.Length; i++)
             {
@@ -43,12 +43,12 @@ namespace Jint.Runtime.Interpreter.Statements
                 }
                 else
                 {
-                    left = JintExpression.Build(_engine, declaration.Id);
+                    left = JintExpression.Build(engine, declaration.Id);
                 }
 
                 if (declaration.Init != null)
                 {
-                    init = JintExpression.Build(_engine, declaration.Init);
+                    init = JintExpression.Build(engine, declaration.Init);
                 }
 
                 var leftIdentifier = left as JintIdentifierExpression;
@@ -63,23 +63,19 @@ namespace Jint.Runtime.Interpreter.Statements
             }
         }
 
-        protected override Completion ExecuteInternal()
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
-            if (!_initialized)
-            {
-                _initialized = true;
-                Initialize();
-            }
-
+            var engine = context.Engine;
             foreach (var declaration in _declarations)
             {
                 if (_statement.Kind != VariableDeclarationKind.Var && declaration.Left != null)
                 {
-                    var lhs = (Reference) declaration.Left.Evaluate();
+                    var lhs = (Reference) declaration.Left.Evaluate(context).Value;
                     var value = JsValue.Undefined;
                     if (declaration.Init != null)
                     {
-                        value = declaration.Init.GetValue().Clone();
+                        var completion = declaration.Init.GetValue(context);
+                        value = completion.Value.Clone();
                         if (declaration.Init._expression.IsFunctionDefinition())
                         {
                             ((FunctionInstance) value).SetFunctionName(lhs.GetReferencedName());
@@ -87,43 +83,46 @@ namespace Jint.Runtime.Interpreter.Statements
                     }
 
                     lhs.InitializeReferencedBinding(value);
-                    _engine._referencePool.Return(lhs);
+                    engine._referencePool.Return(lhs);
                 }
                 else if (declaration.Init != null)
                 {
                     if (declaration.LeftPattern != null)
                     {
                         var environment = _statement.Kind != VariableDeclarationKind.Var
-                            ? _engine.ExecutionContext.LexicalEnvironment
+                            ? engine.ExecutionContext.LexicalEnvironment
                             : null;
 
+                        var completion = declaration.Init.GetValue(context);
+
                         BindingPatternAssignmentExpression.ProcessPatterns(
-                            _engine,
+                            context,
                             declaration.LeftPattern,
-                            declaration.Init.GetValue(),
+                            completion.Value,
                             environment,
                             checkObjectPatternPropertyReference: _statement.Kind != VariableDeclarationKind.Var);
                     }
                     else if (declaration.LeftIdentifierExpression == null
                              || JintAssignmentExpression.SimpleAssignmentExpression.AssignToIdentifier(
-                                 _engine,
+                                 context,
                                  declaration.LeftIdentifierExpression,
                                  declaration.Init,
                                  declaration.EvalOrArguments) is null)
                     {
                         // slow path
-                        var lhs = (Reference) declaration.Left.Evaluate();
-                        lhs.AssertValid(_engine);
+                        var lhs = (Reference) declaration.Left.Evaluate(context).Value;
+                        lhs.AssertValid(engine.Realm);
 
-                        var value = declaration.Init.GetValue().Clone();
+                        var completion = declaration.Init.GetValue(context);
+                        var value = completion.Value.Clone();
 
                         if (declaration.Init._expression.IsFunctionDefinition())
                         {
                             ((FunctionInstance) value).SetFunctionName(lhs.GetReferencedName());
                         }
 
-                        _engine.PutValue(lhs, value);
-                        _engine._referencePool.Return(lhs);
+                        engine.PutValue(lhs, value);
+                        engine._referencePool.Return(lhs);
                     }
                 }
             }

+ 16 - 12
Jint/Runtime/Interpreter/Statements/JintWhileStatement.cs

@@ -9,38 +9,42 @@ namespace Jint.Runtime.Interpreter.Statements
     /// </summary>
     internal sealed class JintWhileStatement : JintStatement<WhileStatement>
     {
-        private readonly string _labelSetName;
-        private readonly JintStatement _body;
-        private readonly JintExpression _test;
+        private string _labelSetName;
+        private JintStatement _body;
+        private JintExpression _test;
 
-        public JintWhileStatement(Engine engine, WhileStatement statement) : base(engine, statement)
+        public JintWhileStatement(WhileStatement statement) : base(statement)
         {
-            _labelSetName = _statement?.LabelSet?.Name;
-            _body = Build(engine, statement.Body);
-            _test = JintExpression.Build(engine, statement.Test);
         }
 
-        protected override Completion ExecuteInternal()
+        protected override void Initialize(EvaluationContext context)
+        {
+            _labelSetName = _statement.LabelSet?.Name;
+            _body = Build(_statement.Body);
+            _test = JintExpression.Build(context.Engine, _statement.Test);
+        }
+
+        protected override Completion ExecuteInternal(EvaluationContext context)
         {
             var v = Undefined.Instance;
             while (true)
             {
-                var jsValue = _test.GetValue();
+                var jsValue = _test.GetValue(context).Value;
                 if (!TypeConverter.ToBoolean(jsValue))
                 {
                     return new Completion(CompletionType.Normal, v, null, Location);
                 }
 
-                var completion = _body.Execute();
+                var completion = _body.Execute(context);
 
                 if (!ReferenceEquals(completion.Value, null))
                 {
                     v = completion.Value;
                 }
 
-                if (completion.Type != CompletionType.Continue || completion.Identifier != _labelSetName)
+                if (completion.Type != CompletionType.Continue || completion.Target != _labelSetName)
                 {
-                    if (completion.Type == CompletionType.Break && (completion.Identifier == null || completion.Identifier == _labelSetName))
+                    if (completion.Type == CompletionType.Break && (completion.Target == null || completion.Target == _labelSetName))
                     {
                         return new Completion(CompletionType.Normal, v, null, Location);
                     }

+ 19 - 14
Jint/Runtime/Interpreter/Statements/JintWithStatement.cs

@@ -9,35 +9,40 @@ namespace Jint.Runtime.Interpreter.Statements
     /// </summary>
     internal sealed class JintWithStatement : JintStatement<WithStatement>
     {
-        private readonly JintStatement _body;
-        private readonly JintExpression _object;
+        private JintStatement _body;
+        private JintExpression _object;
 
-        public JintWithStatement(Engine engine, WithStatement statement) : base(engine, statement)
+        public JintWithStatement(WithStatement statement) : base(statement)
         {
-            _body = Build(engine, statement.Body);
-            _object = JintExpression.Build(engine, _statement.Object);
         }
 
-        protected override Completion ExecuteInternal()
+        protected override void Initialize(EvaluationContext context)
         {
-            var jsValue = _object.GetValue();
-            var obj = TypeConverter.ToObject(_engine.Realm, jsValue);
-            var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
-            var newEnv = JintEnvironment.NewObjectEnvironment(_engine, obj, oldEnv, provideThis: true, withEnvironment: true);
-            _engine.UpdateLexicalEnvironment(newEnv);
+            _body = Build(_statement.Body);
+            _object = JintExpression.Build(context.Engine, _statement.Object);
+        }
+
+        protected override Completion ExecuteInternal(EvaluationContext context)
+        {
+            var jsValue = _object.GetValue(context).Value;
+            var engine = context.Engine;
+            var obj = TypeConverter.ToObject(engine.Realm, jsValue);
+            var oldEnv = engine.ExecutionContext.LexicalEnvironment;
+            var newEnv = JintEnvironment.NewObjectEnvironment(engine, obj, oldEnv, provideThis: true, withEnvironment: true);
+            engine.UpdateLexicalEnvironment(newEnv);
 
             Completion c;
             try
             {
-                c = _body.Execute();
+                c = _body.Execute(context);
             }
             catch (JavaScriptException e)
             {
-                c = new Completion(CompletionType.Throw, e.Error, null, _statement.Location);
+                c = new Completion(CompletionType.Throw, e.Error, _statement.Location);
             }
             finally
             {
-                _engine.UpdateLexicalEnvironment(oldEnv);
+                engine.UpdateLexicalEnvironment(oldEnv);
             }
 
             return c;

+ 2 - 0
Jint/Runtime/KnownKeys.cs

@@ -5,5 +5,7 @@ namespace Jint.Runtime
         internal static readonly Key Arguments = "arguments";
         internal static readonly Key Eval = "eval";
         internal static readonly Key Length = "length";
+        internal static readonly Key Done = "done";
+        internal static readonly Key Value = "value";
     }
 }

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

@@ -76,13 +76,13 @@ namespace Jint.Runtime.References
             return this;
         }
 
-        internal void AssertValid(Engine engine)
+        internal void AssertValid(Realm realm)
         {
             if (_strict
                 && (_baseValue._type & InternalTypes.ObjectEnvironmentRecord) != 0
                 && (_property == CommonProperties.Eval || _property == CommonProperties.Arguments))
             {
-                ExceptionHelper.ThrowSyntaxError(engine.Realm);
+                ExceptionHelper.ThrowSyntaxError(realm);
             }
         }
 

+ 0 - 1
Jint/Runtime/TypeConverter.cs

@@ -818,7 +818,6 @@ namespace Jint.Runtime
         }
 
         internal static IEnumerable<Tuple<MethodDescriptor, JsValue[]>> FindBestMatch(
-            Engine engine,
             MethodDescriptor[] methods,
             Func<MethodDescriptor, JsValue[]> argumentProvider)
         {