소스 검색

Introduce object pooling (#479)

* #451 pool Reference instances

* #451 pool Completion instances

* #451 use Roslyn's tried ObjectPool implementation

* #451 use faster ArgumentsInstance initialization

* saner defaults for slow benchmarks

* #451 remove Interlocked.CompareExchange

* #451 pool ArgumentsInstances
Marko Lahma 7 년 전
부모
커밋
707244db2c

+ 12 - 0
Jint.Benchmark/ArrayStressBenchmark.cs

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

+ 13 - 1
Jint.Benchmark/DromaeoBenchmark.cs

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

+ 13 - 0
Jint.Benchmark/StopwatchBenchmark.cs

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

+ 13 - 1
Jint.Benchmark/SunSpiderBenchmark.cs

@@ -3,13 +3,25 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Configs;
+using BenchmarkDotNet.Diagnosers;
+using BenchmarkDotNet.Jobs;
 using Jint;
 
 namespace Esprima.Benchmark
 {
-    [MemoryDiagnoser]
+    [Config(typeof(Config))]
     public class SunSpiderBenchmark
     {
+        private class Config : ManualConfig
+        {
+            public Config()
+            {
+                Add(Job.ShortRun.WithLaunchCount(1));
+                Add(MemoryDiagnoser.Default);
+            }
+        }
+        
         private static readonly Dictionary<string, string> files = new Dictionary<string, string>
         {
             {"3d-cube", null},

+ 60 - 39
Jint/Engine.cs

@@ -1,9 +1,9 @@
 using System;
 using System.Collections.Generic;
+using System.Runtime.CompilerServices;
 using Esprima;
 using Esprima.Ast;
 using Jint.Native;
-using Jint.Native.Argument;
 using Jint.Native.Array;
 using Jint.Native.Boolean;
 using Jint.Native.Date;
@@ -17,6 +17,7 @@ using Jint.Native.Object;
 using Jint.Native.RegExp;
 using Jint.Native.String;
 using Jint.Native.Symbol;
+using Jint.Pooling;
 using Jint.Runtime;
 using Jint.Runtime.CallStack;
 using Jint.Runtime.Debugger;
@@ -49,7 +50,7 @@ namespace Jint
         // cache of types used when resolving CLR type names
         internal Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();
 
-        internal static Dictionary<Type, Func<Engine, object, JsValue>> TypeMappers = new Dictionary<Type, Func<Engine, object, JsValue>>()
+        internal static Dictionary<Type, Func<Engine, object, JsValue>> TypeMappers = new Dictionary<Type, Func<Engine, object, JsValue>>
         {
             { typeof(bool), (Engine engine, object v) => (bool) v ? JsBoolean.True : JsBoolean.False },
             { typeof(byte), (Engine engine, object v) => JsNumber.Create((byte)v) },
@@ -151,10 +152,11 @@ namespace Jint
 
             Options = new Options();
 
-            if (options != null)
-            {
-                options(Options);
-            }
+            options?.Invoke(Options);
+
+            ReferencePool = new ReferencePool();
+            CompletionPool = new CompletionPool();
+            ArgumentsInstancePool = new ArgumentsInstancePool(this);
 
             Eval = new EvalFunctionInstance(this, System.Array.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
             Global.FastAddProperty("eval", Eval, true, false, true);
@@ -205,6 +207,10 @@ namespace Jint
 
         internal Options Options { get; private set; }
 
+        internal ReferencePool ReferencePool { get; }
+        internal CompletionPool CompletionPool { get; }
+        internal ArgumentsInstancePool ArgumentsInstancePool { get; }
+
         #region Debugger
         public delegate StepMode DebugStepDelegate(object sender, DebugInformation e);
         public delegate StepMode BreakDelegate(object sender, DebugInformation e);
@@ -215,20 +221,12 @@ namespace Jint
 
         internal StepMode? InvokeStepEvent(DebugInformation info)
         {
-            if (Step != null)
-            {
-                return Step(this, info);
-            }
-            return null;
+            return Step?.Invoke(this, info);
         }
 
         internal StepMode? InvokeBreakEvent(DebugInformation info)
         {
-            if (Break != null)
-            {
-                return Break(this, info);
-            }
-            return null;
+            return Break?.Invoke(this, info);
         }
         #endregion
 
@@ -277,7 +275,7 @@ namespace Jint
             return this;
         }
 
-        public Engine SetValue(string name, Object obj)
+        public Engine SetValue(string name, object obj)
         {
             return SetValue(name, JsValue.FromObject(this, obj));
         }
@@ -334,11 +332,13 @@ namespace Jint
                 var result = _statements.ExecuteProgram(program);
                 if (result.Type == Completion.Throw)
                 {
-                    throw new JavaScriptException(result.GetValueOrDefault())
-                        .SetCallstack(this, result.Location);
+                    var ex = new JavaScriptException(result.GetValueOrDefault()).SetCallstack(this, result.Location);
+                    CompletionPool.Return(result);
+                    throw ex;
                 }
 
                 _completionValue = result.GetValueOrDefault();
+                CompletionPool.Return(result);
             }
 
             return this;
@@ -503,12 +503,19 @@ namespace Jint
             }
         }
 
+
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1
         /// </summary>
         /// <param name="value"></param>
         /// <returns></returns>
         public JsValue GetValue(object value)
+        {
+            return GetValue(value, false);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal JsValue GetValue(object value, bool returnReferenceToPool)
         {
             if (value is JsValue jsValue)
             {
@@ -520,7 +527,7 @@ namespace Jint
             {
                 if (value is Completion completion)
                 {
-                    return GetValue(completion.Value);
+                    return GetValue(completion.Value, returnReferenceToPool);
                 }
             }
 
@@ -544,15 +551,21 @@ namespace Jint
                     return baseValue;
                 }
 
+                var referencedName = reference.GetReferencedName();
+                if (returnReferenceToPool)
+                {
+                    ReferencePool.Return(reference);
+                }
                 if (reference.HasPrimitiveBase() == false)
                 {
                     var o = TypeConverter.ToObject(this, baseValue);
-                    return o.Get(reference.GetReferencedName());
+                    var v = o.Get(referencedName);
+                    return v;
                 }
                 else
                 {
                     var o = TypeConverter.ToObject(this, baseValue);
-                    var desc = o.GetProperty(reference.GetReferencedName());
+                    var desc = o.GetProperty(referencedName);
                     if (desc == PropertyDescriptor.Undefined)
                     {
                         return JsValue.Undefined;
@@ -580,7 +593,14 @@ namespace Jint
                 throw new ArgumentException();
             }
 
-            return record.GetBindingValue(reference.GetReferencedName(), reference.IsStrict());
+            var bindingValue = record.GetBindingValue(reference.GetReferencedName(), reference.IsStrict());
+
+            if (returnReferenceToPool)
+            {
+                ReferencePool.Return(reference);
+            }
+
+            return bindingValue;
         }
 
         /// <summary>
@@ -614,7 +634,7 @@ namespace Jint
             else
             {
                 var baseValue = reference.GetBase();
-                var record = baseValue.As<EnvironmentRecord>();
+                var record = baseValue as EnvironmentRecord;
 
                 if (record == null)
                 {
@@ -757,18 +777,20 @@ namespace Jint
         /// <param name="propertyName">The name of the property to return.</param>
         public JsValue GetValue(JsValue scope, string propertyName)
         {
-            if (System.String.IsNullOrEmpty(propertyName))
+            if (string.IsNullOrEmpty(propertyName))
             {
                 throw new ArgumentException("propertyName");
             }
 
-            var reference = new Reference(scope, propertyName, Options._IsStrict);
+            var reference = ReferencePool.Rent(scope, propertyName, Options._IsStrict);
+            var jsValue = GetValue(reference);
+            ReferencePool.Return(reference);
 
-            return GetValue(reference);
+            return jsValue;
         }
 
         //  http://www.ecma-international.org/ecma-262/5.1/#sec-10.5
-        public void DeclarationBindingInstantiation(
+        internal bool DeclarationBindingInstantiation(
             DeclarationBindingType declarationBindingType,
             IList<FunctionDeclaration> functionDeclarations,
             IList<VariableDeclaration> variableDeclarations,
@@ -791,7 +813,7 @@ namespace Jint
                     var argAlreadyDeclared = env.HasBinding(argName);
                     if (!argAlreadyDeclared)
                     {
-                        env.CreateMutableBinding(argName);
+                        env.CreateMutableBinding(argName, v);
                     }
 
                     env.SetMutableBinding(argName, v, strict);
@@ -837,11 +859,11 @@ namespace Jint
                 env.SetMutableBinding(fn, fo, strict);
             }
 
-            var argumentsAlreadyDeclared = env.HasBinding("arguments");
-
-            if (declarationBindingType == DeclarationBindingType.FunctionCode && !argumentsAlreadyDeclared)
+            bool canReleaseArgumentsInstance = false;
+            if (declarationBindingType == DeclarationBindingType.FunctionCode && !env.HasBinding("arguments"))
             {
-                var argsObj = ArgumentsInstance.CreateArgumentsObject(this, functionInstance, functionInstance.FormalParameters, arguments, env, strict);
+                var argsObj = ArgumentsInstancePool.Rent(functionInstance, functionInstance.FormalParameters, arguments, env, strict);
+                canReleaseArgumentsInstance = true;
 
                 if (strict)
                 {
@@ -852,13 +874,11 @@ namespace Jint
                         throw new ArgumentException();
                     }
 
-                    declEnv.CreateImmutableBinding("arguments");
-                    declEnv.InitializeImmutableBinding("arguments", argsObj);
+                    declEnv.CreateImmutableBinding("arguments", argsObj);
                 }
                 else
                 {
-                    env.CreateMutableBinding("arguments");
-                    env.SetMutableBinding("arguments", argsObj, false);
+                    env.CreateMutableBinding("arguments", argsObj);
                 }
             }
 
@@ -873,11 +893,12 @@ namespace Jint
                     var varAlreadyDeclared = env.HasBinding(dn);
                     if (!varAlreadyDeclared)
                     {
-                        env.CreateMutableBinding(dn, configurableBindings);
-                        env.SetMutableBinding(dn, Undefined.Instance, strict);
+                        env.CreateMutableBinding(dn, Undefined.Instance);
                     }
                 }
             }
+
+            return canReleaseArgumentsInstance;
         }
     }
 }

+ 52 - 54
Jint/Native/Argument/ArgumentsInstance.cs

@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.Threading;
 using Jint.Native.Function;
 using Jint.Native.Object;
@@ -16,14 +15,30 @@ namespace Jint.Native.Argument
     public class ArgumentsInstance : ObjectInstance
     {
         // cache key container for array iteration for less allocations
-        private static readonly ThreadLocal<List<string>> _mappedNamed = new ThreadLocal<List<string>>(() => new List<string>());
+        private static readonly ThreadLocal<HashSet<string>> _mappedNamed = new ThreadLocal<HashSet<string>>(() => new HashSet<string>());
+
+        private FunctionInstance _func;
+        private string[] _names;
+        private JsValue[] _args;
+        private EnvironmentRecord _env;
+        private bool _strict;
 
-        private readonly Action<ArgumentsInstance> _initializer;
         private bool _initialized;
 
-        private ArgumentsInstance(Engine engine, Action<ArgumentsInstance> initializer) : base(engine)
+        internal ArgumentsInstance(Engine engine) : base(engine)
+        {
+        }
+
+        internal void Prepare(FunctionInstance func, string[] names, JsValue[] args, EnvironmentRecord env, bool strict)
         {
-            _initializer = initializer;
+            Clear();
+
+            _func = func;
+            _names = names;
+            _args = args;
+            _env = env;
+            _strict = strict;
+
             _initialized = false;
         }
 
@@ -38,66 +53,49 @@ namespace Jint.Native.Argument
 
             _initialized = true;
 
-            _initializer(this);
-        }
-
-        public static ArgumentsInstance CreateArgumentsObject(Engine engine, FunctionInstance func, string[] names, JsValue[] args, EnvironmentRecord env, bool strict)
-        {
-            void Initializer(ArgumentsInstance self)
+            var self = this;
+            var len = _args.Length;
+            self.SetOwnProperty("length", new NonEnumerablePropertyDescriptor(len));
+            if (_args.Length > 0)
             {
-                var len = args.Length;
-                self.SetOwnProperty("length", new NonEnumerablePropertyDescriptor(len));
-                if (args.Length > 0)
+                var map = Engine.Object.Construct(Arguments.Empty);
+                var mappedNamed = _mappedNamed.Value;
+                mappedNamed.Clear();
+                for (var indx = 0; indx < len; indx++)
                 {
-                    var map = engine.Object.Construct(Arguments.Empty);
-                    var mappedNamed = _mappedNamed.Value;
-                    mappedNamed.Clear();
-                    for (var indx = 0; indx < len; indx++)
+                    var indxStr = TypeConverter.ToString(indx);
+                    var val = _args[indx];
+                    self.SetOwnProperty(indxStr, new ConfigurableEnumerableWritablePropertyDescriptor(val));
+                    if (indx < _names.Length)
                     {
-                        var indxStr = TypeConverter.ToString(indx);
-                        var val = args[indx];
-                        self.SetOwnProperty(indxStr, new ConfigurableEnumerableWritablePropertyDescriptor(val));
-                        if (indx < names.Length)
+                        var name = _names[indx];
+                        if (!_strict && !mappedNamed.Contains(name))
                         {
-                            var name = names[indx];
-                            if (!strict && !mappedNamed.Contains(name))
-                            {
-                                mappedNamed.Add(name);
-                                map.SetOwnProperty(indxStr, new ClrAccessDescriptor(env, engine, name));
-                            }
+                            mappedNamed.Add(name);
+                            map.SetOwnProperty(indxStr, new ClrAccessDescriptor(_env, Engine, name));
                         }
                     }
-
-                    // step 12
-                    if (mappedNamed.Count > 0)
-                    {
-                        self.ParameterMap = map;
-                    }
                 }
 
-                // step 13
-                if (!strict)
-                {
-                    self.SetOwnProperty("callee", new NonEnumerablePropertyDescriptor(func));
-                }
-                // step 14
-                else
+                // step 12
+                if (mappedNamed.Count > 0)
                 {
-                    var thrower = engine.Function.ThrowTypeError;
-                    self.DefineOwnProperty("caller", new PropertyDescriptor(get: thrower, set: thrower, enumerable: false, configurable: false), false);
-                    self.DefineOwnProperty("callee", new PropertyDescriptor(get: thrower, set: thrower, enumerable: false, configurable: false), false);
+                    self.ParameterMap = map;
                 }
             }
 
-            var obj = new ArgumentsInstance(engine, Initializer);
-
-            // These properties are pre-initialized as their don't trigger
-            // the EnsureInitialized() event and are cheap
-            obj.Prototype = engine.Object.PrototypeObject;
-            obj.Extensible = true;
-            obj.Strict = strict;
-
-            return obj;
+            // step 13
+            if (!_strict)
+            {
+                self.SetOwnProperty("callee", new NonEnumerablePropertyDescriptor(_func));
+            }
+            // step 14
+            else
+            {
+                var thrower = Engine.Function.ThrowTypeError;
+                self.DefineOwnProperty("caller", new PropertyDescriptor(get: thrower, set: thrower, enumerable: false, configurable: false), false);
+                self.DefineOwnProperty("callee", new PropertyDescriptor(get: thrower, set: thrower, enumerable: false, configurable: false), false);
+            }
         }
 
         public ObjectInstance ParameterMap { get; set; }

+ 18 - 8
Jint/Native/Array/ArrayPrototype.cs

@@ -111,7 +111,7 @@ namespace Jint.Native.Array
             var o = ArrayOperations.For(Engine, thisObj);
             var len = o.GetLength();
 
-            var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
+            var callable = GetCallable(callbackfn);
 
             if (len == 0 && arguments.Length < 2)
             {
@@ -170,7 +170,7 @@ namespace Jint.Native.Array
             var o = ArrayOperations.For(Engine, thisObj);
             var len = o.GetLength();
 
-            var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
+            var callable = GetCallable(callbackfn);
 
             var a = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
 
@@ -203,7 +203,7 @@ namespace Jint.Native.Array
             var o = ArrayOperations.For(Engine, thisObj);
             var len = o.GetLength();
 
-            var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
+            var callable = GetCallable(callbackfn);
 
             var a = Engine.Array.Construct(new JsValue[] {len}, len);
             var args = new JsValue[3];
@@ -230,7 +230,7 @@ namespace Jint.Native.Array
             var o = ArrayOperations.For(Engine, thisObj);
             var len = o.GetLength();
 
-            var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
+            var callable = GetCallable(callbackfn);
 
             var args = new JsValue[3];
             for (uint k = 0; k < len; k++)
@@ -255,7 +255,7 @@ namespace Jint.Native.Array
             var o = ArrayOperations.For(Engine, thisObj);
             var len = o.GetLength();
 
-            var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
+            var callable = GetCallable(callbackfn);
 
             var args = new JsValue[3];
             for (uint k = 0; k < len; k++)
@@ -284,7 +284,7 @@ namespace Jint.Native.Array
             var o = ArrayOperations.For(Engine, thisObj);
             var len = o.GetLength();
 
-            var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
+            var callable = GetCallable(callbackfn);
 
             var args = new JsValue[3];
             for (uint k = 0; k < len; k++)
@@ -833,7 +833,7 @@ namespace Jint.Native.Array
             var lenValue = o.Get("length");
             var len = TypeConverter.ToUint32(lenValue);
 
-            var callable = callbackfn.TryCast<ICallable>(x => { throw new JavaScriptException(Engine.TypeError, "Argument must be callable"); });
+            var callable = GetCallable(callbackfn);
 
             if (len == 0 && arguments.Length < 2)
             {
@@ -928,6 +928,16 @@ namespace Jint.Native.Array
             o.Target.Put("length", len, true);
             return element;
         }
+        
+        private ICallable GetCallable(JsValue source)
+        {
+            if (source is ICallable callable)
+            {
+                return callable;
+            }
+
+            throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
+        }
 
         /// <summary>
         /// Adapter to use optimized array operations when possible.
@@ -998,7 +1008,7 @@ namespace Jint.Native.Array
                     }
 
                     // if getter is not undefined it must be ICallable
-                    var callable = getter.TryCast<ICallable>();
+                    var callable = (ICallable) getter;
                     var value = callable.Call(_instance, Arguments.Empty);
                     return TypeConverter.ToUint32(value);
                 }

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

@@ -1,4 +1,5 @@
 using Esprima;
+using Jint.Native.Argument;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Environments;
@@ -55,18 +56,34 @@ namespace Jint.Native.Function
                                 Engine.EnterExecutionContext(strictVarEnv, strictVarEnv, Engine.ExecutionContext.ThisBinding);
                             }
 
-                            Engine.DeclarationBindingInstantiation(DeclarationBindingType.EvalCode, program.HoistingScope.FunctionDeclarations, program.HoistingScope.VariableDeclarations, this, arguments);
+                            bool argumentInstanceRented = Engine.DeclarationBindingInstantiation(
+                                DeclarationBindingType.EvalCode,
+                                program.HoistingScope.FunctionDeclarations,
+                                program.HoistingScope.VariableDeclarations,
+                                this, 
+                                arguments);
 
                             var result = _engine.ExecuteStatement(program);
+                            var value = result.GetValueOrDefault();
+
+                            // we can safely release arguments if they don't escape the scope
+                            if (argumentInstanceRented
+                                && Engine.ExecutionContext.LexicalEnvironment?.Record is DeclarativeEnvironmentRecord der
+                                && !(result.Value is ArgumentsInstance))
+                            {
+                                der.ReleaseArguments();
+                            }
 
                             if (result.Type == Completion.Throw)
                             {
-                                throw new JavaScriptException(result.GetValueOrDefault())
-                                    .SetCallstack(_engine, result.Location);
+                                var ex = new JavaScriptException(value).SetCallstack(_engine, result.Location);
+                                _engine.CompletionPool.Return(result);
+                                throw ex;
                             }
                             else
                             {
-                                return result.GetValueOrDefault();
+                                _engine.CompletionPool.Return(result);
+                                return value;
                             }
                         }
                         finally

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

@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using Esprima.Ast;
+using Jint.Native.Argument;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
@@ -166,31 +167,42 @@ namespace Jint.Native.Function
 
                 Engine.EnterExecutionContext(localEnv, localEnv, thisBinding);
 
+                Completion result = null;
                 try
                 {
-                    Engine.DeclarationBindingInstantiation(
+                    var argumentInstanceRented = Engine.DeclarationBindingInstantiation(
                         DeclarationBindingType.FunctionCode,
                         _functionDeclaration.HoistingScope.FunctionDeclarations,
                         _functionDeclaration.HoistingScope.VariableDeclarations,
                         this,
                         arguments);
 
-                    var result = Engine.ExecuteStatement(_functionDeclaration.Body);
+                    result = Engine.ExecuteStatement(_functionDeclaration.Body);
+                    
+                    var value = result.GetValueOrDefault();
+                    
+                    // we can safely release arguments if they don't escape the scope
+                    if (argumentInstanceRented
+                        && Engine.ExecutionContext.LexicalEnvironment?.Record is DeclarativeEnvironmentRecord der
+                        && !(result.Value is ArgumentsInstance))
+                    {
+                        der.ReleaseArguments();
+                    }
 
                     if (result.Type == Completion.Throw)
                     {
-                        JavaScriptException ex = new JavaScriptException(result.GetValueOrDefault())
-                            .SetCallstack(Engine, result.Location);
+                        var ex = new JavaScriptException(value).SetCallstack(Engine, result.Location);
                         throw ex;
                     }
 
                     if (result.Type == Completion.Return)
                     {
-                        return result.GetValueOrDefault();
+                        return value;
                     }
                 }
                 finally
                 {
+                    Engine.CompletionPool.Return(result);
                     Engine.LeaveExecutionContext();
                 }
 

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

@@ -959,5 +959,11 @@ namespace Jint.Native.Object
 
             return false;
         }
+
+        internal void Clear()
+        {
+            _intrinsicProperties?.Clear();
+            _properties?.Clear();
+        }
     }
 }

+ 57 - 0
Jint/Pooling/ArgumentsInstancePool.cs

@@ -0,0 +1,57 @@
+using Jint.Native;
+using Jint.Native.Argument;
+using Jint.Native.Function;
+using Jint.Runtime.Environments;
+using Jint.Runtime.References;
+
+namespace Jint.Pooling
+{
+    /// <summary>
+    /// Cache reusable <see cref="Reference" /> instances as we allocate them a lot.
+    /// </summary>
+    internal sealed class ArgumentsInstancePool
+    {
+        private const int PoolSize = 10;
+        private readonly Engine _engine;
+        private readonly ObjectPool<ArgumentsInstance> _pool;
+
+        public ArgumentsInstancePool(Engine engine)
+        {
+            _engine = engine;
+            _pool = new ObjectPool<ArgumentsInstance>(Factory, PoolSize);
+        }
+
+        private ArgumentsInstance Factory()
+        {
+            return new ArgumentsInstance(_engine);
+        }
+
+        public ArgumentsInstance Rent(
+            FunctionInstance func, 
+            string[] names, 
+            JsValue[] args, 
+            EnvironmentRecord env, 
+            bool strict)
+        {
+            var obj = _pool.Allocate();
+            obj.Prepare(func, names, args, env, strict);
+
+            // These properties are pre-initialized as their don't trigger
+            // the EnsureInitialized() event and are cheap
+            obj.Prototype = _engine.Object.PrototypeObject;
+            obj.Extensible = true;
+            obj.Strict = strict;
+
+            return obj;
+        }
+
+        public void Return(ArgumentsInstance instance)
+        {
+            if (instance == null)
+            {
+                return;
+            }
+            _pool.Free(instance);;
+        }
+    }
+}

+ 41 - 0
Jint/Pooling/CompletionPool.cs

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

+ 277 - 0
Jint/Pooling/ObjectPool.cs

@@ -0,0 +1,277 @@
+// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
+ 
+// define TRACE_LEAKS to get additional diagnostics that can lead to the leak sources. note: it will
+// make everything about 2-3x slower
+// 
+// #define TRACE_LEAKS
+ 
+// define DETECT_LEAKS to detect possible leaks
+// #if DEBUG
+// #define DETECT_LEAKS  //for now always enable DETECT_LEAKS in debug.
+// #endif
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+
+#if DETECT_LEAKS
+using System.Runtime.CompilerServices;
+ 
+#endif
+namespace Jint.Pooling
+{
+    /// <summary>
+    /// Generic implementation of object pooling pattern with predefined pool size limit. The main
+    /// purpose is that limited number of frequently used objects can be kept in the pool for
+    /// further recycling.
+    /// 
+    /// Notes: 
+    /// 1) it is not the goal to keep all returned objects. Pool is not meant for storage. If there
+    ///    is no space in the pool, extra returned objects will be dropped.
+    /// 
+    /// 2) it is implied that if object was obtained from a pool, the caller will return it back in
+    ///    a relatively short time. Keeping checked out objects for long durations is ok, but 
+    ///    reduces usefulness of pooling. Just new up your own.
+    /// 
+    /// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice. 
+    /// 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
+    {
+        [DebuggerDisplay("{Value,nq}")]
+        private struct Element
+        {
+            internal T Value;
+        }
+ 
+        /// <remarks>
+        /// Not using System.Func{T} because this file is linked into the (debugger) Formatter,
+        /// which does not have that type (since it compiles against .NET 2.0).
+        /// </remarks>
+        internal delegate T Factory();
+ 
+        // Storage for the pool objects. The first item is stored in a dedicated field because we
+        // expect to be able to satisfy most requests from it.
+        private T _firstItem;
+        private readonly Element[] _items;
+ 
+        // factory is stored for the lifetime of the pool. We will call this only when pool needs to
+        // expand. compared to "new T()", Func gives more flexibility to implementers and faster
+        // than "new T()".
+        private readonly Factory _factory;
+ 
+#if DETECT_LEAKS
+        private static readonly ConditionalWeakTable<T, LeakTracker> leakTrackers = new ConditionalWeakTable<T, LeakTracker>();
+ 
+        private class LeakTracker : IDisposable
+        {
+            private volatile bool disposed;
+ 
+#if TRACE_LEAKS
+            internal volatile object Trace = null;
+#endif
+
+            public void Dispose()
+            {
+                disposed = true;
+                GC.SuppressFinalize(this);
+            }
+ 
+            private string GetTrace()
+            {
+#if TRACE_LEAKS
+                return Trace == null ? "" : Trace.ToString();
+#else
+                return "Leak tracing information is disabled. Define TRACE_LEAKS on ObjectPool`1.cs to get more info \n";
+#endif
+            }
+ 
+            ~LeakTracker()
+            {
+                if (!this.disposed && !Environment.HasShutdownStarted)
+                {
+                    var trace = GetTrace();
+ 
+                    // If you are seeing this message it means that object has been allocated from the pool 
+                    // and has not been returned back. This is not critical, but turns pool into rather 
+                    // inefficient kind of "new".
+                    Debug.WriteLine($"TRACEOBJECTPOOLLEAKS_BEGIN\nPool detected potential leaking of {typeof(T)}. \n Location of the leak: \n {GetTrace()} TRACEOBJECTPOOLLEAKS_END");
+                }
+            }
+        }
+#endif      
+ 
+        internal ObjectPool(Factory factory)
+            : this(factory, Environment.ProcessorCount * 2)
+        { }
+ 
+        internal ObjectPool(Factory factory, int size)
+        {
+            Debug.Assert(size >= 1);
+            _factory = factory;
+            _items = new Element[size - 1];
+        }
+ 
+        private T CreateInstance()
+        {
+            var inst = _factory();
+            return inst;
+        }
+ 
+        /// <summary>
+        /// Produces an instance.
+        /// </summary>
+        /// <remarks>
+        /// Search strategy is a simple linear probing which is chosen for it cache-friendliness.
+        /// Note that Free will try to store recycled objects close to the start thus statistically 
+        /// reducing how far we will typically search.
+        /// </remarks>
+        internal T Allocate()
+        {
+            // PERF: Examine the first element. If that fails, AllocateSlow will look at the remaining elements.
+            // Note that the initial read is optimistically not synchronized. That is intentional. 
+            // We will interlock only when we have a candidate. in a worst case we may miss some
+            // recently returned objects. Not a big deal.
+            T inst = _firstItem;
+            if (inst != null)
+            {
+                _firstItem = null;
+                return inst;
+            }
+
+            inst = AllocateSlow();
+
+#if DETECT_LEAKS
+            var tracker = new LeakTracker();
+            leakTrackers.Add(inst, tracker);
+ 
+#if TRACE_LEAKS
+            var frame = CaptureStackTrace();
+            tracker.Trace = frame;
+#endif
+#endif
+            return inst;
+        }
+
+        private T AllocateSlow()
+        {
+            var items = _items;
+ 
+            for (int i = 0; i < items.Length; i++)
+            {
+                T inst = items[i].Value;
+                if (inst != null)
+                {
+                    items[i].Value = null;
+                    return inst;
+                }
+            }
+ 
+            return CreateInstance();
+        }
+ 
+        /// <summary>
+        /// Returns objects to the pool.
+        /// </summary>
+        /// <remarks>
+        /// Search strategy is a simple linear probing which is chosen for it cache-friendliness.
+        /// Note that Free will try to store recycled objects close to the start thus statistically 
+        /// reducing how far we will typically search in Allocate.
+        /// </remarks>
+        internal void Free(T obj)
+        {
+            Validate(obj);
+            ForgetTrackedObject(obj);
+ 
+            if (_firstItem == null)
+            {
+                // Intentionally not using interlocked here. 
+                // In a worst case scenario two objects may be stored into same slot.
+                // It is very unlikely to happen and will only mean that one of the objects will get collected.
+                _firstItem = obj;
+            }
+            else
+            {
+                FreeSlow(obj);
+            }
+        }
+ 
+        private void FreeSlow(T obj)
+        {
+            var items = _items;
+            for (int i = 0; i < items.Length; i++)
+            {
+                if (items[i].Value == null)
+                {
+                    // Intentionally not using interlocked here. 
+                    // In a worst case scenario two objects may be stored into same slot.
+                    // It is very unlikely to happen and will only mean that one of the objects will get collected.
+                    items[i].Value = obj;
+                    break;
+                }
+            }
+        }
+ 
+        /// <summary>
+        /// Removes an object from leak tracking.  
+        /// 
+        /// This is called when an object is returned to the pool.  It may also be explicitly 
+        /// called if an object allocated from the pool is intentionally not being returned
+        /// to the pool.  This can be of use with pooled arrays if the consumer wants to 
+        /// return a larger array to the pool than was originally allocated.
+        /// </summary>
+        [Conditional("DEBUG")]
+        internal void ForgetTrackedObject(T old, T replacement = null)
+        {
+#if DETECT_LEAKS
+            LeakTracker tracker;
+            if (leakTrackers.TryGetValue(old, out tracker))
+            {
+                tracker.Dispose();
+                leakTrackers.Remove(old);
+            }
+            else
+            {
+                var trace = CaptureStackTrace();
+                Debug.WriteLine($"TRACEOBJECTPOOLLEAKS_BEGIN\nObject of type {typeof(T)} was freed, but was not from pool. \n Callstack: \n {trace} TRACEOBJECTPOOLLEAKS_END");
+            }
+ 
+            if (replacement != null)
+            {
+                tracker = new LeakTracker();
+                leakTrackers.Add(replacement, tracker);
+            }
+#endif
+        }
+ 
+#if DETECT_LEAKS
+        private static Lazy<Type> _stackTraceType = new Lazy<Type>(() => Type.GetType("System.Diagnostics.StackTrace"));
+ 
+        private static object CaptureStackTrace()
+        {
+            return Activator.CreateInstance(_stackTraceType.Value);
+        }
+#endif
+ 
+        [Conditional("DEBUG")]
+        private void Validate(object obj)
+        {
+            Debug.Assert(obj != null, "freeing null?");
+ 
+            Debug.Assert(_firstItem != obj, "freeing twice?");
+ 
+            var items = _items;
+            for (int i = 0; i < items.Length; i++)
+            {
+                var value = items[i].Value;
+                if (value == null)
+                {
+                    return;
+                }
+ 
+                Debug.Assert(value != obj, "freeing twice?");
+            }
+        }
+    }
+}

+ 38 - 0
Jint/Pooling/ReferencePool.cs

@@ -0,0 +1,38 @@
+using Jint.Native;
+using Jint.Runtime.References;
+
+namespace Jint.Pooling
+{
+    /// <summary>
+    /// Cache reusable <see cref="Reference" /> instances as we allocate them a lot.
+    /// </summary>
+    internal sealed class ReferencePool
+    {
+        private const int PoolSize = 10;
+        private readonly ObjectPool<Reference> _pool;
+
+        public ReferencePool()
+        {
+            _pool = new ObjectPool<Reference>(Factory, PoolSize);
+        }
+
+        private static Reference Factory()
+        {
+            return new Reference(JsValue.Undefined, string.Empty, false);
+        }
+
+        public Reference Rent(JsValue baseValue, string name, bool strict)
+        {
+            return _pool.Allocate().Reassign(baseValue, name, strict);
+        }
+
+        public void Return(Reference reference)
+        {
+            if (reference == null)
+            {
+                return;
+            }
+            _pool.Free(reference);;
+        }
+    }
+}

+ 34 - 10
Jint/Runtime/Completion.cs

@@ -1,4 +1,5 @@
-using Esprima;
+using System;
+using Esprima;
 using Jint.Native;
 
 namespace Jint.Runtime
@@ -6,7 +7,7 @@ namespace Jint.Runtime
     /// <summary>
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.9
     /// </summary>
-    public class Completion
+    public sealed class Completion
     {
         public const string Normal = "normal";
         public const string Break = "break";
@@ -14,19 +15,22 @@ namespace Jint.Runtime
         public const string Return = "return";
         public const string Throw = "throw";
 
-        public static readonly Completion Empty = new Completion(Normal, null, null);
-        public static readonly Completion EmptyUndefined = new Completion(Normal, Undefined.Instance, null);
+        internal static readonly Completion Empty = new Completion(Normal, null, null).Freeze();
+        internal static readonly Completion EmptyUndefined = new Completion(Normal, Undefined.Instance, null).Freeze();
 
-        public Completion(string type, JsValue value, string identifier)
+        private bool _frozen;
+
+        public Completion(string type, JsValue value, string identifier, Location location = null)
         {
             Type = type;
             Value = value;
             Identifier = identifier;
+            Location = location;
         }
 
-        public string Type { get; }
-        public JsValue Value { get; }
-        public string Identifier { get; }
+        public string Type { get; private set; }
+        public JsValue Value { get; private set; }
+        public string Identifier { get; private set; }
 
         public JsValue GetValueOrDefault()
         {
@@ -38,6 +42,26 @@ namespace Jint.Runtime
             return Type != Normal;
         }
 
-        public Location Location { get; set; }
+        public Location Location { get; private set; }
+
+        private Completion Freeze()
+        {
+            _frozen = true;
+            return this;
+        }
+
+        public Completion Reassign(string type, JsValue value, string identifier, Location location = null)
+        {
+            if (_frozen)
+            {
+                throw new InvalidOperationException("object is frozen");
+            }
+            Type = type;
+            Value = value;
+            Identifier = identifier;
+            Location = location;
+
+            return this;
+        }
     }
-}
+}

+ 16 - 16
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -1,5 +1,6 @@
 using System;
 using Jint.Native;
+using Jint.Native.Argument;
 
 namespace Jint.Runtime.Environments
 {
@@ -31,11 +32,11 @@ namespace Jint.Runtime.Environments
             return _bindings?.ContainsKey(name) == true;
         }
 
-        public override void CreateMutableBinding(string name, bool canBeDeleted = false)
+        public override void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false)
         {
             var binding = new Binding
             {
-                Value = Undefined,
+                Value = value,
                 CanBeDeleted =  canBeDeleted,
                 Mutable = true
             };
@@ -132,14 +133,15 @@ namespace Jint.Runtime.Environments
         }
 
         /// <summary>
-        /// Creates a new but uninitialised immutable binding in an environment record.
+        /// Creates a new immutable binding in an environment record.
         /// </summary>
         /// <param name="name">The identifier of the binding.</param>
-        public void CreateImmutableBinding(string name)
+        /// <param name="value">The value  of the binding.</param>
+        public void CreateImmutableBinding(string name, JsValue value)
         {
             var binding = new Binding
             {
-                Value = Undefined,
+                Value = value,
                 Mutable = false,
                 CanBeDeleted = false
             };
@@ -158,17 +160,6 @@ namespace Jint.Runtime.Environments
             }
         }
 
-        /// <summary>
-        /// Sets the value of an already existing but uninitialised immutable binding in an environment record.
-        /// </summary>
-        /// <param name="name">The identifier of the binding.</param>
-        /// <param name="value">The value of the binding.</param>
-        public void InitializeImmutableBinding(string name, JsValue value)
-        {
-            var binding = name == BindingNameArguments ? _argumentsBinding : _bindings[name];
-            binding.Value = value;
-        }
-
         /// <summary>
         /// Returns an array of all the defined binding names
         /// </summary>
@@ -177,5 +168,14 @@ namespace Jint.Runtime.Environments
         {
             return _bindings?.GetKeys() ?? Array.Empty<string>();
         }
+
+        internal void ReleaseArguments()
+        {
+            if (_argumentsBinding?.Value is ArgumentsInstance instance)
+            {
+                _engine.ArgumentsInstancePool.Return(instance);
+                _argumentsBinding = null;
+            }
+        }
     }
 }

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

@@ -24,8 +24,9 @@ namespace Jint.Runtime.Environments
         /// Creates a new mutable binding in an environment record.
         /// </summary>
         /// <param name="name">The identifier of the binding.</param>
+        /// <param name="value">The value of the binding.</param>
         /// <param name="canBeDeleted"><c>true</c> if the binding may be subsequently deleted.</param>
-        public abstract void CreateMutableBinding(string name, bool canBeDeleted = false);
+        public abstract void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false);
 
         /// <summary>
         /// Sets the value of an already existing mutable binding in an environment record. 

+ 9 - 15
Jint/Runtime/Environments/LexicalEnvironment.cs

@@ -11,24 +11,20 @@ namespace Jint.Runtime.Environments
     /// </summary>
     public sealed class LexicalEnvironment
     {
+        private readonly Engine _engine;
         private readonly EnvironmentRecord _record;
         private readonly LexicalEnvironment _outer;
 
-        public LexicalEnvironment(EnvironmentRecord record, LexicalEnvironment outer)
+        public LexicalEnvironment(Engine engine, EnvironmentRecord record, LexicalEnvironment outer)
         {
+            _engine = engine;
             _record = record;
             _outer = outer;
         }
 
-        public EnvironmentRecord Record
-        {
-            get { return _record; }
-        }
+        public EnvironmentRecord Record => _record;
 
-        public LexicalEnvironment Outer
-        {
-            get { return _outer; }
-        }
+        public LexicalEnvironment Outer => _outer;
 
         public static Reference GetIdentifierReference(LexicalEnvironment lex, string name, bool strict)
         {
@@ -41,12 +37,12 @@ namespace Jint.Runtime.Environments
 
                 if (lex.Record.HasBinding(name))
                 {
-                    return new Reference(lex.Record, name, strict);
+                    return lex._engine.ReferencePool.Rent(lex.Record, name, strict);
                 }
 
                 if (lex.Outer == null)
                 {
-                    return new Reference(Undefined.Instance, name, strict);
+                    return lex._engine.ReferencePool.Rent(Undefined.Instance, name, strict);
                 }
 
                 lex = lex.Outer;
@@ -55,14 +51,12 @@ namespace Jint.Runtime.Environments
 
         public static LexicalEnvironment NewDeclarativeEnvironment(Engine engine, LexicalEnvironment outer = null)
         {
-            return new LexicalEnvironment(new DeclarativeEnvironmentRecord(engine), outer);
+            return new LexicalEnvironment(engine, new DeclarativeEnvironmentRecord(engine), outer);
         }
 
         public static LexicalEnvironment NewObjectEnvironment(Engine engine, ObjectInstance objectInstance, LexicalEnvironment outer, bool provideThis)
         {
-            return new LexicalEnvironment(new ObjectEnvironmentRecord(engine, objectInstance, provideThis), outer);
+            return new LexicalEnvironment(engine, new ObjectEnvironmentRecord(engine, objectInstance, provideThis), outer);
         }
     }
-
-    
 }

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

@@ -32,13 +32,11 @@ namespace Jint.Runtime.Environments
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1.2.2
         /// </summary>
-        /// <param name="name"></param>
-        /// <param name="configurable"></param>
-        public override void CreateMutableBinding(string name, bool configurable = true)
+        public override void CreateMutableBinding(string name, JsValue value, bool configurable = true)
         {
             var propertyDescriptor = configurable
-                ? (IPropertyDescriptor) new ConfigurableEnumerableWritablePropertyDescriptor(Undefined)
-                : new NonConfigurablePropertyDescriptor(Undefined);
+                ? (IPropertyDescriptor) new ConfigurableEnumerableWritablePropertyDescriptor(value)
+                : new NonConfigurablePropertyDescriptor(value);
 
             _bindingObject.SetOwnProperty(name, propertyDescriptor);
         }

+ 49 - 35
Jint/Runtime/ExpressionIntepreter.cs

@@ -31,22 +31,22 @@ namespace Jint.Runtime
         public JsValue EvaluateConditionalExpression(ConditionalExpression conditionalExpression)
         {
             var lref = _engine.EvaluateExpression(conditionalExpression.Test);
-            if (TypeConverter.ToBoolean(_engine.GetValue(lref)))
+            if (TypeConverter.ToBoolean(_engine.GetValue(lref, true)))
             {
                 var trueRef = _engine.EvaluateExpression(conditionalExpression.Consequent);
-                return _engine.GetValue(trueRef);
+                return _engine.GetValue(trueRef, true);
             }
             else
             {
                 var falseRef = _engine.EvaluateExpression(conditionalExpression.Alternate);
-                return _engine.GetValue(falseRef);
+                return _engine.GetValue(falseRef, true);
             }
         }
 
         public JsValue EvaluateAssignmentExpression(AssignmentExpression assignmentExpression)
         {
             var lref = EvaluateExpression(assignmentExpression.Left.As<Expression>()) as Reference;
-            JsValue rval = _engine.GetValue(EvaluateExpression(assignmentExpression.Right));
+            JsValue rval = _engine.GetValue(EvaluateExpression(assignmentExpression.Right), true);
 
             if (lref == null)
             {
@@ -62,6 +62,7 @@ namespace Jint.Runtime
                 }
 
                 _engine.PutValue(lref, rval);
+                _engine.ReferencePool.Return(lref);
                 return rval;
             }
 
@@ -148,6 +149,7 @@ namespace Jint.Runtime
 
             _engine.PutValue(lref, lval);
 
+            _engine.ReferencePool.Return(lref);
             return lval;
         }
 
@@ -210,8 +212,7 @@ namespace Jint.Runtime
             }
             else
             {
-                var leftExpression = EvaluateExpression(expression.Left);
-                left = _engine.GetValue(leftExpression);
+                left = _engine.GetValue(EvaluateExpression(expression.Left), true);
             }
 
             JsValue right;
@@ -221,8 +222,7 @@ namespace Jint.Runtime
             }
             else
             {
-                var rightExpression = EvaluateExpression(expression.Right);
-                right = _engine.GetValue(rightExpression);
+                right = _engine.GetValue(EvaluateExpression(expression.Right), true);
             }
 
             JsValue value;
@@ -373,7 +373,7 @@ namespace Jint.Runtime
 
         public JsValue EvaluateLogicalExpression(BinaryExpression binaryExpression)
         {
-            var left = _engine.GetValue(EvaluateExpression(binaryExpression.Left));
+            var left = _engine.GetValue(EvaluateExpression(binaryExpression.Left), true);
 
             switch (binaryExpression.Operator)
             {
@@ -384,7 +384,7 @@ namespace Jint.Runtime
                         return left;
                     }
 
-                    return _engine.GetValue(EvaluateExpression(binaryExpression.Right));
+                    return _engine.GetValue(EvaluateExpression(binaryExpression.Right), true);
 
                 case BinaryOperator.LogicalOr:
                     if (TypeConverter.ToBoolean(left))
@@ -392,7 +392,7 @@ namespace Jint.Runtime
                         return left;
                     }
 
-                    return _engine.GetValue(EvaluateExpression(binaryExpression.Right));
+                    return _engine.GetValue(EvaluateExpression(binaryExpression.Right), true);
 
                 default:
                     throw new NotImplementedException();
@@ -669,7 +669,7 @@ namespace Jint.Runtime
                     case PropertyKind.Init:
                     case PropertyKind.Data:
                         var exprValue = _engine.EvaluateExpression(property.Value.As<Expression>());
-                        var propValue = _engine.GetValue(exprValue);
+                        var propValue = _engine.GetValue(exprValue, true);
                         propDesc = new ConfigurableEnumerableWritablePropertyDescriptor(propValue);
                         break;
 
@@ -777,13 +777,17 @@ namespace Jint.Runtime
             else
             {
                 var propertyNameReference = EvaluateExpression(memberExpression.Property);
-                var propertyNameValue = _engine.GetValue(propertyNameReference);
+                var propertyNameValue = _engine.GetValue(propertyNameReference, true);
                 propertyNameString = TypeConverter.ToString(propertyNameValue);
             }
 
             TypeConverter.CheckObjectCoercible(_engine, baseValue, memberExpression, baseReference);
 
-            return new Reference(baseValue, propertyNameString, StrictModeScope.IsStrictModeCode);
+            if (baseReference is Reference r)
+            {
+                _engine.ReferencePool.Return(r);
+            }
+            return _engine.ReferencePool.Rent(baseValue, propertyNameString, StrictModeScope.IsStrictModeCode);
         }
 
         public JsValue EvaluateFunctionExpression(IFunction functionExpression)
@@ -791,11 +795,6 @@ namespace Jint.Runtime
             var funcEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
             var envRec = (DeclarativeEnvironmentRecord)funcEnv.Record;
 
-            if (functionExpression.Id != null && !String.IsNullOrEmpty(functionExpression.Id.Name))
-            {
-                envRec.CreateMutableBinding(functionExpression.Id.Name);
-            }
-
             var closure = new ScriptFunctionInstance(
                 _engine,
                 functionExpression,
@@ -803,9 +802,9 @@ namespace Jint.Runtime
                 functionExpression.Strict
                 );
 
-            if (functionExpression.Id != null && !String.IsNullOrEmpty(functionExpression.Id.Name))
+            if (!string.IsNullOrEmpty(functionExpression.Id?.Name))
             {
-                envRec.InitializeImmutableBinding(functionExpression.Id.Name, closure);
+                envRec.CreateMutableBinding(functionExpression.Id.Name, closure);
             }
 
             return closure;
@@ -909,7 +908,9 @@ namespace Jint.Runtime
             // is it a direct call to eval ? http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.1.1
             if (r != null && r.GetReferencedName() == "eval" && callable is EvalFunctionInstance)
             {
-                return ((EvalFunctionInstance) callable).Call(thisObject, arguments, true);
+                var value = ((EvalFunctionInstance) callable).Call(thisObject, arguments, true);
+                _engine.ReferencePool.Return(r);
+                return value;
             }
 
             var result = callable.Call(thisObject, arguments);
@@ -924,6 +925,7 @@ namespace Jint.Runtime
                 _engine.CallStack.Pop();
             }
 
+            _engine.ReferencePool.Return(r);
             return result;
         }
 
@@ -932,7 +934,7 @@ namespace Jint.Runtime
             var result = Undefined.Instance;
             foreach (var expression in sequenceExpression.Expressions)
             {
-                result = _engine.GetValue(_engine.EvaluateExpression(expression.As<Expression>()));
+                result = _engine.GetValue(_engine.EvaluateExpression(expression.As<Expression>()), true);
             }
 
             return result;
@@ -959,6 +961,7 @@ namespace Jint.Runtime
                     var newValue = oldValue + 1;
                     _engine.PutValue(r, newValue);
 
+                    _engine.ReferencePool.Return(r);
                     return updateExpression.Prefix ? newValue : oldValue;
 
                 case UnaryOperator.Decrement:
@@ -973,7 +976,9 @@ namespace Jint.Runtime
 
                     oldValue = TypeConverter.ToNumber(_engine.GetValue(value));
                     newValue = oldValue - 1;
+
                     _engine.PutValue(r, newValue);
+                    _engine.ReferencePool.Return(r);
 
                     return updateExpression.Prefix ? newValue : oldValue;
                 default:
@@ -992,7 +997,7 @@ namespace Jint.Runtime
             var arguments = BuildArguments(newExpression.Arguments, out bool _);
 
             // todo: optimize by defining a common abstract class or interface
-            var callee = _engine.GetValue(EvaluateExpression(newExpression.Callee)).TryCast<IConstructor>();
+            var callee = _engine.GetValue(EvaluateExpression(newExpression.Callee), true).TryCast<IConstructor>();
 
             if (callee == null)
             {
@@ -1015,7 +1020,7 @@ namespace Jint.Runtime
                 var expr = elements[n];
                 if (expr != null)
                 {
-                    var value = _engine.GetValue(EvaluateExpression(expr.As<Expression>()));
+                    var value = _engine.GetValue(EvaluateExpression(expr.As<Expression>()), true);
                     a.SetIndexValue((uint) n, value, throwOnError: false);
                 }
             }
@@ -1026,25 +1031,24 @@ namespace Jint.Runtime
         public JsValue EvaluateUnaryExpression(UnaryExpression unaryExpression)
         {
             var value = _engine.EvaluateExpression(unaryExpression.Argument);
-            Reference r;
 
             switch (unaryExpression.Operator)
             {
                 case UnaryOperator.Plus:
-                    return TypeConverter.ToNumber(_engine.GetValue(value));
+                    return TypeConverter.ToNumber(_engine.GetValue(value, true));
 
                 case UnaryOperator.Minus:
-                    var n = TypeConverter.ToNumber(_engine.GetValue(value));
+                    var n = TypeConverter.ToNumber(_engine.GetValue(value, true));
                     return double.IsNaN(n) ? double.NaN : n*-1;
 
                 case UnaryOperator.BitwiseNot:
-                    return ~TypeConverter.ToInt32(_engine.GetValue(value));
+                    return ~TypeConverter.ToInt32(_engine.GetValue(value, true));
 
                 case UnaryOperator.LogicalNot:
-                    return !TypeConverter.ToBoolean(_engine.GetValue(value));
+                    return !TypeConverter.ToBoolean(_engine.GetValue(value, true));
 
                 case UnaryOperator.Delete:
-                    r = value as Reference;
+                    var r = value as Reference;
                     if (r == null)
                     {
                         return true;
@@ -1056,19 +1060,26 @@ namespace Jint.Runtime
                             throw new JavaScriptException(_engine.SyntaxError);
                         }
 
+                        _engine.ReferencePool.Return(r);
                         return true;
                     }
                     if (r.IsPropertyReference())
                     {
                         var o = TypeConverter.ToObject(_engine, r.GetBase());
-                        return o.Delete(r.GetReferencedName(), r.IsStrict());
+                        var jsValue = o.Delete(r.GetReferencedName(), r.IsStrict());
+                        _engine.ReferencePool.Return(r);
+                        return jsValue;
                     }
                     if (r.IsStrict())
                     {
                         throw new JavaScriptException(_engine.SyntaxError);
                     }
+
                     var bindings = r.GetBase().TryCast<EnvironmentRecord>();
-                    return bindings.DeleteBinding(r.GetReferencedName());
+                    var referencedName = r.GetReferencedName();
+                    _engine.ReferencePool.Return(r);
+
+                    return bindings.DeleteBinding(referencedName);
 
                 case UnaryOperator.Void:
                     _engine.GetValue(value);
@@ -1080,10 +1091,13 @@ namespace Jint.Runtime
                     {
                         if (r.IsUnresolvableReference())
                         {
+                            _engine.ReferencePool.Return(r);
                             return "undefined";
                         }
                     }
-                    var v = _engine.GetValue(value);
+
+                    var v = _engine.GetValue(value, true);
+
                     if (ReferenceEquals(v, Undefined.Instance))
                     {
                         return "undefined";
@@ -1124,7 +1138,7 @@ namespace Jint.Runtime
             for (var i = 0; i < count; i++)
             {
                 var argument = (Expression) expressionArguments[i];
-                arguments[i] = _engine.GetValue(EvaluateExpression(argument));
+                arguments[i] = _engine.GetValue(EvaluateExpression(argument), true);
                 allLiteral &= argument is Literal;
             }
 

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

@@ -1,4 +1,5 @@
-using Jint.Native;
+using System;
+using Jint.Native;
 using Jint.Runtime.Environments;
 
 namespace Jint.Runtime.References
@@ -7,17 +8,16 @@ namespace Jint.Runtime.References
     /// Represents the Reference Specification Type
     /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7
     /// </summary>
-    public class Reference
+    public sealed class Reference
     {
-        private readonly JsValue _baseValue;
-        private readonly string _name;
-        private readonly bool _strict;
+        private JsValue _baseValue;
+        private string _name;
+        private bool _strict;
 
         public Reference(JsValue baseValue, string name, bool strict)
         {
             _baseValue = baseValue;
             _name = name;
-            _strict = strict;
         }
 
         public JsValue GetBase()
@@ -50,5 +50,14 @@ namespace Jint.Runtime.References
             // http://www.ecma-international.org/ecma-262/5.1/#sec-8.7
             return HasPrimitiveBase() || (_baseValue.IsObject() && !(_baseValue is EnvironmentRecord));
         }
+
+        internal Reference Reassign(JsValue baseValue, string name, bool strict)
+        {
+            _baseValue = baseValue;
+            _name = name;
+            _strict = strict;
+
+            return this;
+        }
     }
 }

+ 77 - 57
Jint/Runtime/StatementInterpreter.cs

@@ -31,15 +31,13 @@ namespace Jint.Runtime
         public Completion ExecuteExpressionStatement(ExpressionStatement expressionStatement)
         {
             var exprRef = _engine.EvaluateExpression(expressionStatement.Expression);
-            return new Completion(Completion.Normal, _engine.GetValue(exprRef), null);
+            return _engine.CompletionPool.Rent(Completion.Normal, _engine.GetValue(exprRef, true), null);
         }
 
         public Completion ExecuteIfStatement(IfStatement ifStatement)
         {
-            var exprRef = _engine.EvaluateExpression(ifStatement.Test);
             Completion result;
-
-            if (TypeConverter.ToBoolean(_engine.GetValue(exprRef)))
+            if (TypeConverter.ToBoolean(_engine.GetValue(_engine.EvaluateExpression(ifStatement.Test), true)))
             {
                 result = ExecuteStatement(ifStatement.Consequent);
             }
@@ -63,7 +61,9 @@ namespace Jint.Runtime
             var result = ExecuteStatement(labeledStatement.Body);
             if (result.Type == Completion.Break && result.Identifier == labeledStatement.Label.Name)
             {
-                return new Completion(Completion.Normal, result.Value, null);
+                var value = result.Value;
+                _engine.CompletionPool.Return(result);
+                return _engine.CompletionPool.Rent(Completion.Normal, value, null);
             }
 
             return result;
@@ -90,7 +90,8 @@ namespace Jint.Runtime
                 {
                     if (stmt.Type == Completion.Break && (stmt.Identifier == null || stmt.Identifier == doWhileStatement?.LabelSet?.Name))
                     {
-                        return new Completion(Completion.Normal, v, null);
+                        _engine.CompletionPool.Return(stmt);
+                        return _engine.CompletionPool.Rent(Completion.Normal, v, null);
                     }
 
                     if (stmt.Type != Completion.Normal)
@@ -98,12 +99,14 @@ namespace Jint.Runtime
                         return stmt;
                     }
                 }
+
+                _engine.CompletionPool.Return(stmt);
                 var exprRef = _engine.EvaluateExpression(doWhileStatement.Test);
-                iterating = TypeConverter.ToBoolean(_engine.GetValue(exprRef));
+                iterating = TypeConverter.ToBoolean(_engine.GetValue(exprRef, true));
 
             } while (iterating);
 
-            return new Completion(Completion.Normal, v, null);
+            return _engine.CompletionPool.Rent(Completion.Normal, v, null);
         }
 
         /// <summary>
@@ -116,11 +119,10 @@ namespace Jint.Runtime
             JsValue v = Undefined.Instance;
             while (true)
             {
-                var exprRef = _engine.EvaluateExpression(whileStatement.Test);
-
-                if (!TypeConverter.ToBoolean(_engine.GetValue(exprRef)))
+                var jsValue = _engine.GetValue(_engine.EvaluateExpression(whileStatement.Test), true);
+                if (!TypeConverter.ToBoolean(jsValue))
                 {
-                    return new Completion(Completion.Normal, v, null);
+                    return _engine.CompletionPool.Rent(Completion.Normal, v, null);
                 }
 
                 var stmt = ExecuteStatement(whileStatement.Body);
@@ -134,7 +136,8 @@ namespace Jint.Runtime
                 {
                     if (stmt.Type == Completion.Break && (stmt.Identifier == null || stmt.Identifier == whileStatement?.LabelSet?.Name))
                     {
-                        return new Completion(Completion.Normal, v, null);
+                        _engine.CompletionPool.Return(stmt);
+                        return _engine.CompletionPool.Rent(Completion.Normal, v, null);
                     }
 
                     if (stmt.Type != Completion.Normal)
@@ -142,6 +145,8 @@ namespace Jint.Runtime
                         return stmt;
                     }
                 }
+
+                _engine.CompletionPool.Return(stmt);
             }
         }
 
@@ -157,11 +162,13 @@ namespace Jint.Runtime
             {
                 if (forStatement.Init.Type == Nodes.VariableDeclaration)
                 {
-                    ExecuteStatement(forStatement.Init.As<Statement>());
+                    var c = ExecuteStatement(forStatement.Init.As<Statement>());
+                    _engine.CompletionPool.Return(c);
+
                 }
                 else
                 {
-                    _engine.GetValue(_engine.EvaluateExpression(forStatement.Init.As<Expression>()));
+                    _engine.GetValue(_engine.EvaluateExpression(forStatement.Init.As<Expression>()), true);
                 }
             }
 
@@ -171,9 +178,9 @@ namespace Jint.Runtime
                 if (forStatement.Test != null)
                 {
                     var testExprRef = _engine.EvaluateExpression(forStatement.Test);
-                    if (!TypeConverter.ToBoolean(_engine.GetValue(testExprRef)))
+                    if (!TypeConverter.ToBoolean(_engine.GetValue(testExprRef, true)))
                     {
-                        return new Completion(Completion.Normal, v, null);
+                        return _engine.CompletionPool.Rent(Completion.Normal, v, null);
                     }
                 }
 
@@ -184,7 +191,8 @@ namespace Jint.Runtime
                 }
                 if (stmt.Type == Completion.Break && (stmt.Identifier == null || stmt.Identifier == forStatement?.LabelSet?.Name))
                 {
-                    return new Completion(Completion.Normal, v, null);
+                    _engine.CompletionPool.Return(stmt);
+                    return _engine.CompletionPool.Rent(Completion.Normal, v, null);
                 }
                 if (stmt.Type != Completion.Continue || ((stmt.Identifier != null) && stmt.Identifier != forStatement?.LabelSet?.Name))
                 {
@@ -195,9 +203,9 @@ namespace Jint.Runtime
                 }
                 if (forStatement.Update != null)
                 {
-                    var incExprRef = _engine.EvaluateExpression(forStatement.Update);
-                    _engine.GetValue(incExprRef);
+                    _engine.GetValue(_engine.EvaluateExpression(forStatement.Update), true);
                 }
+                _engine.CompletionPool.Return(stmt);
             }
         }
 
@@ -213,8 +221,7 @@ namespace Jint.Runtime
                                         : forInStatement.Left.As<Identifier>();
 
             var varRef = _engine.EvaluateExpression(identifier) as Reference;
-            var exprRef = _engine.EvaluateExpression(forInStatement.Right);
-            var experValue = _engine.GetValue(exprRef);
+            var experValue = _engine.GetValue(_engine.EvaluateExpression(forInStatement.Right), true);
             if (ReferenceEquals(experValue, Undefined.Instance) || ReferenceEquals(experValue,  Null.Instance))
             {
                 return Completion.Empty;
@@ -264,7 +271,8 @@ namespace Jint.Runtime
                     }
                     if (stmt.Type == Completion.Break)
                     {
-                        return new Completion(Completion.Normal, v, null);
+                        _engine.CompletionPool.Return(stmt);
+                        return _engine.CompletionPool.Rent(Completion.Normal, v, null);
                     }
                     if (stmt.Type != Completion.Continue)
                     {
@@ -273,12 +281,13 @@ namespace Jint.Runtime
                             return stmt;
                         }
                     }
+                    _engine.CompletionPool.Return(stmt);
                 }
 
                 cursor = cursor.Prototype;
             }
 
-            return new Completion(Completion.Normal, v, null);
+            return _engine.CompletionPool.Rent(Completion.Normal, v, null);
         }
 
         /// <summary>
@@ -288,7 +297,10 @@ namespace Jint.Runtime
         /// <returns></returns>
         public Completion ExecuteContinueStatement(ContinueStatement continueStatement)
         {
-            return new Completion(Completion.Continue, null, continueStatement.Label != null ? continueStatement.Label.Name : null);
+            return _engine.CompletionPool.Rent(
+                Completion.Continue,
+                null,
+                continueStatement.Label != null ? continueStatement.Label.Name : null);
         }
 
         /// <summary>
@@ -298,7 +310,10 @@ namespace Jint.Runtime
         /// <returns></returns>
         public Completion ExecuteBreakStatement(BreakStatement breakStatement)
         {
-            return new Completion(Completion.Break, null, breakStatement.Label != null ? breakStatement.Label.Name : null);
+            return _engine.CompletionPool.Rent(
+                Completion.Break,
+                null,
+                breakStatement.Label != null ? breakStatement.Label.Name : null);
         }
 
         /// <summary>
@@ -310,11 +325,11 @@ namespace Jint.Runtime
         {
             if (statement.Argument == null)
             {
-                return new Completion(Completion.Return, Undefined.Instance, null);
+                return _engine.CompletionPool.Rent(Completion.Return, Undefined.Instance, null);
             }
 
-            var exprRef = _engine.EvaluateExpression(statement.Argument);
-            return new Completion(Completion.Return, _engine.GetValue(exprRef), null);
+            var jsValue = _engine.GetValue(_engine.EvaluateExpression(statement.Argument), true);
+            return _engine.CompletionPool.Rent(Completion.Return, jsValue, null);
         }
 
         /// <summary>
@@ -324,8 +339,8 @@ namespace Jint.Runtime
         /// <returns></returns>
         public Completion ExecuteWithStatement(WithStatement withStatement)
         {
-            var val = _engine.EvaluateExpression(withStatement.Object);
-            var obj = TypeConverter.ToObject(_engine, _engine.GetValue(val));
+            var jsValue = _engine.GetValue(_engine.EvaluateExpression(withStatement.Object), true);
+            var obj = TypeConverter.ToObject(_engine, jsValue);
             var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
             var newEnv = LexicalEnvironment.NewObjectEnvironment(_engine, obj, oldEnv, true);
             _engine.ExecutionContext.LexicalEnvironment = newEnv;
@@ -337,8 +352,7 @@ namespace Jint.Runtime
             }
             catch (JavaScriptException e)
             {
-                c = new Completion(Completion.Throw, e.Error, null);
-                c.Location = withStatement.Location;
+                c = _engine.CompletionPool.Rent(Completion.Throw, e.Error, null, withStatement.Location);
             }
             finally
             {
@@ -355,11 +369,11 @@ namespace Jint.Runtime
         /// <returns></returns>
         public Completion ExecuteSwitchStatement(SwitchStatement switchStatement)
         {
-            var exprRef = _engine.EvaluateExpression(switchStatement.Discriminant);
-            var r = ExecuteSwitchBlock(switchStatement.Cases, _engine.GetValue(exprRef));
+            var jsValue = _engine.GetValue(_engine.EvaluateExpression(switchStatement.Discriminant), true);
+            var r = ExecuteSwitchBlock(switchStatement.Cases, jsValue);
             if (r.Type == Completion.Break && r.Identifier == switchStatement.LabelSet?.Name)
             {
-                return new Completion(Completion.Normal, r.Value, null);
+                return _engine.CompletionPool.Rent(Completion.Normal, r.Value, null);
             }
             return r;
         }
@@ -377,7 +391,7 @@ namespace Jint.Runtime
                 }
                 else
                 {
-                    var clauseSelector = _engine.GetValue(_engine.EvaluateExpression(clause.Test));
+                    var clauseSelector = _engine.GetValue(_engine.EvaluateExpression(clause.Test), true);
                     if (ExpressionInterpreter.StrictlyEqual(clauseSelector, input))
                     {
                         hit = true;
@@ -409,7 +423,7 @@ namespace Jint.Runtime
                 v = r.Value != null ? r.Value : Undefined.Instance;
             }
 
-            return new Completion(Completion.Normal, v, null);
+            return _engine.CompletionPool.Rent(Completion.Normal, v, null);
         }
 
         public Completion ExecuteStatementList(List<StatementListItem> statementList)
@@ -426,10 +440,20 @@ namespace Jint.Runtime
                     c = ExecuteStatement(s);
                     if (c.Type != Completion.Normal)
                     {
-                        return new Completion(c.Type, c.Value != null ? c.Value : sl.Value, c.Identifier)
-                        {
-                            Location = c.Location
-                        };
+                        var executeStatementList = _engine.CompletionPool.Rent(
+                            c.Type,
+                            c.Value != null ? c.Value : sl.Value,
+                            c.Identifier,
+                            c.Location);
+
+                        _engine.CompletionPool.Return(sl);
+                        _engine.CompletionPool.Return(c);
+                        return executeStatementList;
+                    }
+
+                    if (sl != c)
+                    {
+                        _engine.CompletionPool.Return(sl);
                     }
 
                     sl = c;
@@ -437,12 +461,13 @@ namespace Jint.Runtime
             }
             catch (JavaScriptException v)
             {
-                c = new Completion(Completion.Throw, v.Error, null);
-                c.Location = v.Location ?? s.Location;
-                return c;
+                var completion = _engine.CompletionPool.Rent(Completion.Throw, v.Error, null, v.Location ?? s.Location);
+                return completion;
             }
 
-            return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
+            var rent = _engine.CompletionPool.Rent(c.Type, c.GetValueOrDefault(), c.Identifier);
+            _engine.CompletionPool.Return(c);
+            return rent;
         }
 
         /// <summary>
@@ -452,10 +477,8 @@ namespace Jint.Runtime
         /// <returns></returns>
         public Completion ExecuteThrowStatement(ThrowStatement throwStatement)
         {
-            var exprRef = _engine.EvaluateExpression(throwStatement.Argument);
-            Completion c = new Completion(Completion.Throw, _engine.GetValue(exprRef), null);
-            c.Location = throwStatement.Location;
-            return c;
+            var jsValue = _engine.GetValue(_engine.EvaluateExpression(throwStatement.Argument), true);
+            return _engine.CompletionPool.Rent(Completion.Throw, jsValue, null, throwStatement.Location);
         }
 
         /// <summary>
@@ -475,8 +498,7 @@ namespace Jint.Runtime
                     var c = _engine.GetValue(b);
                     var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
                     var catchEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
-                    catchEnv.Record.CreateMutableBinding(catchClause.Param.As<Identifier>().Name);
-                    catchEnv.Record.SetMutableBinding(catchClause.Param.As<Identifier>().Name, c, false);
+                    catchEnv.Record.CreateMutableBinding(catchClause.Param.As<Identifier>().Name, c);
                     _engine.ExecutionContext.LexicalEnvironment = catchEnv;
                     b = ExecuteStatement(catchClause.Body);
                     _engine.ExecutionContext.LexicalEnvironment = oldEnv;
@@ -508,9 +530,7 @@ namespace Jint.Runtime
             {
                 if (declaration.Init != null)
                 {
-                    var lhs = _engine.EvaluateExpression(declaration.Id.As<Identifier>()) as Reference;
-
-                    if (lhs == null)
+                    if (!(_engine.EvaluateExpression(declaration.Id.As<Identifier>()) is Reference lhs))
                     {
                         throw new ArgumentException();
                     }
@@ -521,9 +541,9 @@ namespace Jint.Runtime
                         throw new JavaScriptException(_engine.SyntaxError);
                     }
 
-                    lhs.GetReferencedName();
-                    var value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init));
+                    var value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init), true);
                     _engine.PutValue(lhs, value);
+                    _engine.ReferencePool.Return(lhs);
                 }
             }