Selaa lähdekoodia

Optimize Array and Completion (#501)

Marko Lahma 7 vuotta sitten
vanhempi
commit
7a6dc642c4

+ 29 - 17
Jint/Engine.cs

@@ -43,8 +43,14 @@ namespace Jint
         private JsValue _completionValue = JsValue.Undefined;
         private int _statementsCount;
         private long _timeoutTicks;
-        private INode _lastSyntaxNode = null;
-
+        private INode _lastSyntaxNode;
+
+        // cached access
+        private readonly bool _isDebugMode;
+        private readonly bool _isStrict;
+        private readonly int _maxStatements;
+        private readonly IReferenceResolver _referenceResolver;
+        
         public ITypeConverter ClrTypeConverter;
 
         // cache of types used when resolving CLR type names
@@ -153,9 +159,14 @@ namespace Jint
             Options = new Options();
 
             options?.Invoke(Options);
+            
+            // gather some options as fields for faster checks
+            _isDebugMode = Options.IsDebugMode;
+            _isStrict = Options.IsStrict;
+            _maxStatements = Options.MaxStatementCount;
+            _referenceResolver = Options.ReferenceResolver;
 
             ReferencePool = new ReferencePool();
-            CompletionPool = new CompletionPool();
             ArgumentsInstancePool = new ArgumentsInstancePool(this);
             JsValueArrayPool = new JsValueArrayPool();
 
@@ -208,8 +219,12 @@ namespace Jint
 
         internal Options Options { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; }
 
-        internal ReferencePool ReferencePool { get; }
-        internal CompletionPool CompletionPool { get; }
+        internal ReferencePool ReferencePool
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get;
+        }
+
         internal ArgumentsInstancePool ArgumentsInstancePool { get; }
         internal JsValueArrayPool JsValueArrayPool { get; }
 
@@ -327,20 +342,18 @@ namespace Jint
             ResetLastStatement();
             ResetCallStack();
 
-            using (new StrictModeScope(Options._IsStrict || program.Strict))
+            using (new StrictModeScope(_isStrict || program.Strict))
             {
                 DeclarationBindingInstantiation(DeclarationBindingType.GlobalCode, program.HoistingScope.FunctionDeclarations, program.HoistingScope.VariableDeclarations, null, null);
 
                 var result = _statements.ExecuteProgram(program);
-                if (result.Type == Completion.Throw)
+                if (result.Type == CompletionType.Throw)
                 {
                     var ex = new JavaScriptException(result.GetValueOrDefault()).SetCallstack(this, result.Location);
-                    CompletionPool.Return(result);
                     throw ex;
                 }
 
                 _completionValue = result.GetValueOrDefault();
-                CompletionPool.Return(result);
             }
 
             return this;
@@ -361,8 +374,7 @@ namespace Jint
 
         public Completion ExecuteStatement(Statement statement)
         {
-            var maxStatements = Options._MaxStatements;
-            if (maxStatements > 0 && _statementsCount++ > maxStatements)
+            if (_maxStatements > 0 && _statementsCount++ > _maxStatements)
             {
                 throw new StatementsCountOverflowException();
             }
@@ -374,7 +386,7 @@ namespace Jint
 
             _lastSyntaxNode = statement;
 
-            if (Options._IsDebugMode)
+            if (_isDebugMode)
             {
                 DebugHandler.OnStep(statement);
             }
@@ -536,8 +548,8 @@ namespace Jint
 
             if (reference.IsUnresolvableReference())
             {
-                if (Options._ReferenceResolver != null &&
-                    Options._ReferenceResolver.TryUnresolvableReference(this, reference, out JsValue val))
+                if (_referenceResolver != null &&
+                    _referenceResolver.TryUnresolvableReference(this, reference, out JsValue val))
                 {
                     return val;
                 }
@@ -548,8 +560,8 @@ namespace Jint
 
             if (reference.IsPropertyReference())
             {
-                if (Options._ReferenceResolver != null &&
-                    Options._ReferenceResolver.TryPropertyReference(this, reference, ref baseValue))
+                if (_referenceResolver != null &&
+                    _referenceResolver.TryPropertyReference(this, reference, ref baseValue))
                 {
                     return baseValue;
                 }
@@ -789,7 +801,7 @@ namespace Jint
                 throw new ArgumentException("propertyName");
             }
 
-            var reference = ReferencePool.Rent(scope, propertyName, Options._IsStrict);
+            var reference = ReferencePool.Rent(scope, propertyName, _isStrict);
             var jsValue = GetValue(reference);
             ReferencePool.Return(reference);
 

+ 8 - 0
Jint/Native/Array/ArrayConstructor.cs

@@ -128,5 +128,13 @@ namespace Jint.Native.Array
             return instance;
         }
 
+        internal ArrayInstance ConstructFast(uint length)
+        {
+            var instance = new ArrayInstance(Engine, length);
+            instance.Prototype = PrototypeObject;
+            instance.Extensible = true;
+            instance.SetOwnProperty("length", new PropertyDescriptor(length, PropertyFlag.OnlyWritable));
+            return instance;
+        }
     }
 }

+ 143 - 9
Jint/Native/Array/ArrayInstance.cs

@@ -1,6 +1,5 @@
 using System.Collections;
 using System.Collections.Generic;
-using System.Linq;
 using System.Runtime.CompilerServices;
 
 using Jint.Native.Object;
@@ -55,7 +54,7 @@ namespace Jint.Native.Array
                 length = items.Length;
             }            
             
-            SetOwnProperty(PropertyNameLength, new PropertyDescriptor(length, PropertyFlag.OnlyWritable));
+            _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
         }
 
         public ArrayInstance(Engine engine, Dictionary<uint, PropertyDescriptor> items) : base(engine)
@@ -63,7 +62,7 @@ namespace Jint.Native.Array
             _engine = engine;
             _sparse = items;
             var length = items?.Count ?? 0;
-            SetOwnProperty(PropertyNameLength, new PropertyDescriptor(length, PropertyFlag.OnlyWritable));
+            _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
         }
 
         public override string Class => "Array";
@@ -300,6 +299,7 @@ namespace Jint.Native.Array
             return base.DefineOwnProperty(propertyName, desc, throwOnError);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public uint GetLength()
         {
             return (uint) ((JsNumber) _length.Value)._value;
@@ -479,17 +479,26 @@ namespace Jint.Native.Array
             return (uint) result;
         }
 
-        internal void SetIndexValue(uint index, JsValue value, bool throwOnError)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void SetIndexValue(uint index, JsValue value, bool updateLength)
         {
-            var length = GetLength();
-            if (index >= length)
+            if (updateLength)
             {
-                _length.Value = index + 1;
+                var length = GetLength();
+                if (index >= length)
+                {
+                    SetLength(index + 1);
+                }
             }
-
             WriteArrayValue(index, new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void SetLength(uint length)
+        {
+            _length.Value = length;
+        }
+
         internal uint GetSmallestIndex()
         {
             if (_dense != null)
@@ -550,6 +559,7 @@ namespace Jint.Native.Array
             return false;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private bool TryGetDescriptor(uint index, out PropertyDescriptor descriptor)
         {
             if (_dense != null)
@@ -628,7 +638,8 @@ namespace Jint.Native.Array
 
         public IEnumerator<JsValue> GetEnumerator()
         {
-            for (uint i = 0; i < GetLength(); i++)
+            var length = GetLength();
+            for (uint i = 0; i < length; i++)
             {
                 if (TryGetValue(i, out JsValue outValue))
                 {
@@ -641,5 +652,128 @@ namespace Jint.Native.Array
         {
             return GetEnumerator();
         }
+
+        internal uint Push(JsValue[] arguments)
+        {
+            var initialLength = GetLength();
+            var newLength = initialLength + arguments.Length;
+            
+            // if we see that we are bringing more than normal growth algorithm handles, ensure capacity eagerly
+            if (_dense != null
+                && initialLength != 0
+                && arguments.Length > initialLength * 2
+                && newLength <= MaxDenseArrayLength)
+            {
+                EnsureCapacity((uint) newLength);
+            }
+
+            double n = initialLength;
+            for (var i = 0; i < arguments.Length; i++)
+            {
+                var desc = new PropertyDescriptor(arguments[i], PropertyFlag.ConfigurableEnumerableWritable);
+                if (_dense != null && n < _dense.Length)
+                {
+                    _dense[(int) n] = desc;
+                }
+                else if (n < uint.MaxValue)
+                {
+                    WriteArrayValue((uint) n, desc);
+                }
+                else
+                {
+                    DefineOwnProperty(TypeConverter.ToString(n), desc, true);
+                }
+                n++;
+            }
+
+            // check if we can set length fast without breaking ECMA specification
+            if (n < uint.MaxValue && CanPut(PropertyNameLength))
+            {
+                _length.Value = (uint) n;
+            }
+            else
+            {
+                Put(PropertyNameLength, newLength, true);
+            }
+
+            return (uint) n;
+        }
+
+        internal ArrayInstance Map(JsValue[] arguments)
+        {
+            var callbackfn = arguments.At(0);
+            var thisArg = arguments.At(1);
+
+            var len = GetLength();
+
+            var callable = GetCallable(callbackfn);
+            var a = Engine.Array.ConstructFast(len);
+            var args = Engine.JsValueArrayPool.RentArray(3);
+            for (uint k = 0; k < len; k++)
+            {
+                if (TryGetValue(k, out var kvalue))
+                {
+                    args[0] = kvalue;
+                    args[1] = k;
+                    args[2] = this;
+                    var mappedValue = callable.Call(thisArg, args);
+                    var desc = new PropertyDescriptor(mappedValue, PropertyFlag.ConfigurableEnumerableWritable);
+                    if (a._dense != null && k < a._dense.Length)
+                    {
+                        a._dense[k] = desc;
+                    }
+                    else
+                    {
+                        a.WriteArrayValue(k, desc);
+                    }
+                }
+            }
+
+            Engine.JsValueArrayPool.ReturnArray(args);
+            return a;
+        }
+        
+        /// <inheritdoc />
+        internal override bool FindWithCallback(
+            JsValue[] arguments, 
+            out uint index, 
+            out JsValue value)
+        {
+            var len = GetLength();
+            if (len == 0)
+            {
+                index = 0;
+                value = Undefined;
+                return false;
+            }
+
+            var callbackfn = arguments.At(0);
+            var thisArg = arguments.At(1);
+            var callable = GetCallable(callbackfn);
+
+            var args = Engine.JsValueArrayPool.RentArray(3);
+            for (uint k = 0; k < len; k++)
+            {
+                if (TryGetValue(k, out var kvalue))
+                {
+                    args[0] = kvalue;
+                    args[1] = k;
+                    args[2] = this;
+                    var testResult = callable.Call(thisArg, args);
+                    if (TypeConverter.ToBoolean(testResult))
+                    {
+                        index = k;
+                        value = kvalue;
+                        return true;
+                    }
+                }
+            }
+
+            Engine.JsValueArrayPool.ReturnArray(args);
+
+            index = 0;
+            value = Undefined;
+            return false;
+        }
     }
 }

+ 55 - 68
Jint/Native/Array/ArrayPrototype.cs

@@ -53,6 +53,8 @@ namespace Jint.Native.Array
             FastAddProperty("filter", new ClrFunctionInstance(Engine, Filter, 1), true, false, true);
             FastAddProperty("reduce", new ClrFunctionInstance(Engine, Reduce, 1), true, false, true);
             FastAddProperty("reduceRight", new ClrFunctionInstance(Engine, ReduceRight, 1), true, false, true);
+            FastAddProperty("find", new ClrFunctionInstance(Engine, Find, 1), true, false, true);
+            FastAddProperty("findIndex", new ClrFunctionInstance(Engine, FindIndex, 1), true, false, true);
         }
 
         private JsValue LastIndexOf(JsValue thisObj, JsValue[] arguments)
@@ -171,7 +173,7 @@ namespace Jint.Native.Array
 
             var callable = GetCallable(callbackfn);
 
-            var a = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
+            var a = Engine.Array.ConstructFast(0);
 
             uint to = 0;
             var args = Engine.JsValueArrayPool.RentArray(3);
@@ -185,11 +187,13 @@ namespace Jint.Native.Array
                     var selected = callable.Call(thisArg, args);
                     if (TypeConverter.ToBoolean(selected))
                     {
-                        a.SetIndexValue(to, kvalue, throwOnError: false);
+                        a.SetIndexValue(to, kvalue, updateLength: false);
                         to++;
                     }
                 }
             }
+
+            a.SetLength(to);
             Engine.JsValueArrayPool.ReturnArray(args);
 
             return a;
@@ -197,12 +201,16 @@ namespace Jint.Native.Array
 
         private JsValue Map(JsValue thisObj, JsValue[] arguments)
         {
-            var callbackfn = arguments.At(0);
-            var thisArg = arguments.At(1);
+            if (thisObj is ArrayInstance arrayInstance)
+            {
+                return arrayInstance.Map(arguments);
+            }
 
             var o = ArrayOperations.For(Engine, thisObj);
             var len = o.GetLength();
 
+            var callbackfn = arguments.At(0);
+            var thisArg = arguments.At(1);
             var callable = GetCallable(callbackfn);
 
             var jsValues = Engine.JsValueArrayPool.RentArray(1);
@@ -219,10 +227,11 @@ namespace Jint.Native.Array
                     args[1] = k;
                     args[2] = o.Target;
                     var mappedValue = callable.Call(thisArg, args);
-                    a.SetIndexValue(k, mappedValue, throwOnError: false);
+                    a.SetIndexValue(k, mappedValue, updateLength: false);
                 }
             }
 
+            a.SetLength(len);
             Engine.JsValueArrayPool.ReturnArray(args);
 
             return a;
@@ -256,32 +265,8 @@ namespace Jint.Native.Array
 
         private JsValue Some(JsValue thisObj, JsValue[] arguments)
         {
-            var callbackfn = arguments.At(0);
-            var thisArg = arguments.At(1);
-
-            var o = ArrayOperations.For(Engine, thisObj);
-            var len = o.GetLength();
-
-            var callable = GetCallable(callbackfn);
-
-            var args = Engine.JsValueArrayPool.RentArray(3);
-            for (uint k = 0; k < len; k++)
-            {
-                if (o.TryGetValue(k, out var kvalue))
-                {
-                    args[0] = kvalue;
-                    args[1] = k;
-                    args[2] = o.Target;
-                    var testResult = callable.Call(thisArg, args);
-                    if (TypeConverter.ToBoolean(testResult))
-                    {
-                        return true;
-                    }
-                }
-            }
-            Engine.JsValueArrayPool.ReturnArray(args);
-
-            return false;
+            var target = TypeConverter.ToObject(Engine, thisObj);
+            return target.FindWithCallback(arguments, out _, out _);
         }
 
         private JsValue Every(JsValue thisObj, JsValue[] arguments)
@@ -373,6 +358,23 @@ namespace Jint.Native.Array
 
             return -1;
         }
+        
+        private JsValue Find(JsValue thisObj, JsValue[] arguments)
+        {
+            var target = TypeConverter.ToObject(Engine, thisObj);
+            target.FindWithCallback(arguments, out _, out var value);
+            return value;
+        }
+        
+        private JsValue FindIndex(JsValue thisObj, JsValue[] arguments)
+        {
+            var target = TypeConverter.ToObject(Engine, thisObj);
+            if (target.FindWithCallback(arguments, out var index, out _))
+            {
+                return index;
+            }
+            return -1;
+        }
 
         private JsValue Splice(JsValue thisObj, JsValue[] arguments)
         {
@@ -394,14 +396,15 @@ namespace Jint.Native.Array
             }
 
             var actualDeleteCount = (uint) System.Math.Min(System.Math.Max(TypeConverter.ToInteger(deleteCount), 0), len - actualStart);
-            var a = Engine.Array.Construct(actualDeleteCount);
+            var a = Engine.Array.ConstructFast(actualDeleteCount);
             for (uint k = 0; k < actualDeleteCount; k++)
             {
                 if (o.TryGetValue(actualStart + k, out var fromValue))
                 {
-                    a.SetIndexValue(k, fromValue, throwOnError: false);
+                    a.SetIndexValue(k, fromValue, updateLength: false);
                 }
             }
+            a.SetLength(actualDeleteCount);
 
             var items = System.Array.Empty<JsValue>();
             if (arguments.Length > 2)
@@ -618,7 +621,7 @@ namespace Jint.Native.Array
             {
                 if (o.TryGetValue(k, out var kValue))
                 {
-                    a.SetIndexValue(n, kValue, throwOnError: false);
+                    a.SetIndexValue(n, kValue, updateLength: true);
                 }
 
                 n++;
@@ -787,24 +790,24 @@ namespace Jint.Native.Array
 
             // try to find best capacity
             uint capacity = 0;
-            foreach (var e in items)
+            for (var i = 0; i < items.Count; i++)
             {
-                var eArray = e.TryCast<ArrayInstance>();
+                var eArray = items[i] as ArrayInstance;
                 capacity += eArray?.GetLength() ?? (uint) 1;
             }
 
-            var a = Engine.Array.Construct(Arguments.Empty, capacity);
-            foreach (var e in items)
+            var a = Engine.Array.ConstructFast(capacity);
+            for (var i = 0; i < items.Count; i++)
             {
-                var eArray = e.TryCast<ArrayInstance>();
-                if (!ReferenceEquals(eArray, null))
+                var e = items[i];
+                if (e is ArrayInstance eArray)
                 {
                     var len = eArray.GetLength();
                     for (uint k = 0; k < len; k++)
                     {
                         if (eArray.TryGetValue(k, out var subElement))
                         {
-                            a.SetIndexValue(n, subElement, throwOnError: false);
+                            a.SetIndexValue(n, subElement, updateLength: false);
                         }
 
                         n++;
@@ -812,7 +815,7 @@ namespace Jint.Native.Array
                 }
                 else
                 {
-                    a.SetIndexValue(n, e, throwOnError: false);
+                    a.SetIndexValue(n, e, updateLength: false);
                     n++;
                 }
             }
@@ -892,29 +895,23 @@ namespace Jint.Native.Array
 
         public JsValue Push(JsValue thisObject, JsValue[] arguments)
         {
-            var o = ArrayOperations.For(Engine, thisObject);
-            var lenVal = TypeConverter.ToNumber(o.Target.Get("length"));
+            if (thisObject is ArrayInstance arrayInstance)
+            {
+                return arrayInstance.Push(arguments);
+            }
+
+            var o = TypeConverter.ToObject(Engine, thisObject);
+            var lenVal = TypeConverter.ToNumber(o.Get("length"));
 
             // cast to double as we need to prevent an overflow
             double n = TypeConverter.ToUint32(lenVal);
-            var arrayInstance = o.Target as ArrayInstance;
             for (var i = 0; i < arguments.Length; i++)
             {
-                JsValue e = arguments[i];
-                if (!ReferenceEquals(arrayInstance, null) && n >= 0 && n < uint.MaxValue)
-                {
-                    // try to optimize a bit
-                    arrayInstance.SetIndexValue((uint) n, e, true);
-                }
-                else
-                {
-                    o.Target.Put(TypeConverter.ToString(n), e, true);
-                }
+                o.Put(TypeConverter.ToString(n), arguments[i], true);
                 n++;
             }
 
-            o.Target.Put("length", n, true);
-
+            o.Put("length", n, true);
             return n;
         }
 
@@ -938,16 +935,6 @@ namespace Jint.Native.Array
             return element;
         }
         
-        private ICallable GetCallable(JsValue source)
-        {
-            if (source is ICallable callable)
-            {
-                return callable;
-            }
-
-            throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
-        }
-
         /// <summary>
         /// Adapter to use optimized array operations when possible.
         /// Gaps the difference between ArgumensInstance and ArrayInstance.
@@ -1030,7 +1017,7 @@ namespace Jint.Native.Array
                 {
                     var property = TypeConverter.ToString(index);
                     var kPresent = _instance.HasProperty(property);
-                    value = kPresent ? _instance.Get(property) : JsValue.Undefined;
+                    value = kPresent ? _instance.Get(property) : Undefined;
                     return kPresent;
                 }
 

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

@@ -74,15 +74,13 @@ namespace Jint.Native.Function
                                 der.ReleaseArguments();
                             }
 
-                            if (result.Type == Completion.Throw)
+                            if (result.Type == CompletionType.Throw)
                             {
                                 var ex = new JavaScriptException(value).SetCallstack(_engine, result.Location);
-                                _engine.CompletionPool.Return(result);
                                 throw ex;
                             }
                             else
                             {
-                                _engine.CompletionPool.Return(result);
                                 return value;
                             }
                         }

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

@@ -169,7 +169,6 @@ namespace Jint.Native.Function
 
                 Engine.EnterExecutionContext(localEnv, localEnv, thisBinding);
 
-                Completion result = null;
                 try
                 {
                     var argumentInstanceRented = Engine.DeclarationBindingInstantiation(
@@ -179,7 +178,7 @@ namespace Jint.Native.Function
                         this,
                         arguments);
 
-                    result = Engine.ExecuteStatement(_functionDeclaration.Body);
+                    var result = Engine.ExecuteStatement(_functionDeclaration.Body);
                     
                     var value = result.GetValueOrDefault();
                     
@@ -191,20 +190,19 @@ namespace Jint.Native.Function
                         der.ReleaseArguments();
                     }
 
-                    if (result.Type == Completion.Throw)
+                    if (result.Type == CompletionType.Throw)
                     {
                         var ex = new JavaScriptException(value).SetCallstack(Engine, result.Location);
                         throw ex;
                     }
 
-                    if (result.Type == Completion.Return)
+                    if (result.Type == CompletionType.Return)
                     {
                         return value;
                     }
                 }
                 finally
                 {
-                    Engine.CompletionPool.Return(result);
                     Engine.LeaveExecutionContext();
                 }
 

+ 1 - 1
Jint/Native/JsValue.cs

@@ -174,7 +174,7 @@ namespace Jint.Native
             }
 
             // TODO not implemented
-            return null;
+            return Completion.EmptyUndefined;
         }
 
         [Pure]

+ 7 - 4
Jint/Native/Object/ObjectConstructor.cs

@@ -151,18 +151,20 @@ namespace Jint.Native.Object
                 array = Engine.Array.Construct(ownProperties.Count + length);
                 for (var i = 0; i < length; i++)
                 {
-                    array.SetIndexValue(n, TypeConverter.ToString(i), throwOnError: false);
+                    array.SetIndexValue(n, TypeConverter.ToString(i), updateLength: false);
                     n++;
                 }
             }
 
-            array = array ?? Engine.Array.Construct(ownProperties.Count);
-            foreach (var p in ownProperties)
+            array = array ?? Engine.Array.ConstructFast((uint) ownProperties.Count);
+            for (var i = 0; i < ownProperties.Count; i++)
             {
+                var p = ownProperties[i];
                 array.SetIndexValue(n, p.Key, false);
                 n++;
             }
 
+            array.SetLength(n);
             return array;
         }
 
@@ -408,9 +410,10 @@ namespace Jint.Native.Object
             foreach (var prop in enumerableProperties)
             {
                 var p = prop.Key;
-                array.SetIndexValue(index, p, throwOnError: false);
+                array.SetIndexValue(index, p, updateLength: false);
                 index++;
             }
+            array.SetLength(index);
             return array;
         }
     }

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

@@ -848,6 +848,88 @@ namespace Jint.Native.Object
 
             return this;
         }
+        
+        /// <summary>
+        /// Handles the generic find of (callback[, thisArg])
+        /// </summary>
+        internal virtual bool FindWithCallback(
+            JsValue[] arguments, 
+            out uint index, 
+            out JsValue value)
+        {
+            uint GetLength()
+            {
+                var desc = GetProperty("length");
+                var descValue = desc.Value;
+                if (desc.IsDataDescriptor() && !ReferenceEquals(descValue, null))
+                {
+                    return TypeConverter.ToUint32(descValue);
+                }
+
+                var getter = desc.Get ?? Undefined;
+                if (getter.IsUndefined())
+                {
+                    return 0;
+                }
+
+                // if getter is not undefined it must be ICallable
+                return TypeConverter.ToUint32(((ICallable) getter).Call(this, Arguments.Empty));
+            }
+           
+            bool TryGetValue(uint idx, out JsValue jsValue)
+            {
+                var property = TypeConverter.ToString(idx);
+                var kPresent = HasProperty(property);
+                jsValue = kPresent ? Get(property) : Undefined;
+                return kPresent;
+            }
+
+            var len = GetLength();
+            if (len == 0)
+            {
+                index = 0;
+                value = Undefined;
+                return false;
+            }
+
+            var callbackfn = arguments.At(0);
+            var thisArg = arguments.At(1);
+            var callable = GetCallable(callbackfn);
+
+            var args = Engine.JsValueArrayPool.RentArray(3);
+            for (uint k = 0; k < len; k++)
+            {
+                if (TryGetValue(k, out var kvalue))
+                {
+                    args[0] = kvalue;
+                    args[1] = k;
+                    args[2] = this;
+                    var testResult = callable.Call(thisArg, args);
+                    if (TypeConverter.ToBoolean(testResult))
+                    {
+                        index = k;
+                        value = kvalue;
+                        return true;
+                    }
+                }
+            }
+
+            Engine.JsValueArrayPool.ReturnArray(args);
+
+            index = 0;
+            value = Undefined;
+            return false;
+        }
+
+        protected ICallable GetCallable(JsValue source)
+        {
+            if (source is ICallable callable)
+            {
+                return callable;
+            }
+
+            throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
+        }
 
         public override bool Equals(JsValue obj)
         {

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

@@ -116,9 +116,10 @@ namespace Jint.Native.RegExp
             {
                 var group = r.Groups[(int) k];
                 var value = group.Success ? group.Value : Undefined;
-                a.SetIndexValue(k, value, throwOnError: true);
+                a.SetIndexValue(k, value, updateLength: false);
             }
 
+            a.SetLength((uint) n);
             return a;
         }
 

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

@@ -299,7 +299,7 @@ namespace Jint.Native.String
 
                 if (!match.Success) // No match at all return the string in an array
                 {
-                    a.SetIndexValue(0, s, throwOnError: false);
+                    a.SetIndexValue(0, s, updateLength: true);
                     return a;
                 }
 
@@ -314,7 +314,7 @@ namespace Jint.Native.String
                     }
 
                     // Add the match results to the array.
-                    a.SetIndexValue(index++, s.Substring(lastIndex, match.Index - lastIndex), throwOnError: false);
+                    a.SetIndexValue(index++, s.Substring(lastIndex, match.Index - lastIndex), updateLength: true);
 
                     if (index >= limit)
                     {
@@ -331,7 +331,7 @@ namespace Jint.Native.String
                             item = match.Groups[i].Value;
                         }
 
-                        a.SetIndexValue(index++, item, throwOnError: false);
+                        a.SetIndexValue(index++, item, updateLength: true);
 
                         if (index >= limit)
                         {
@@ -342,7 +342,7 @@ namespace Jint.Native.String
                     match = match.NextMatch();
                     if (!match.Success) // Add the last part of the split
                     {
-                        a.SetIndexValue(index++, s.Substring(lastIndex), throwOnError: false);
+                        a.SetIndexValue(index++, s.Substring(lastIndex), updateLength: true);
                     }
                 }
 
@@ -373,11 +373,13 @@ namespace Jint.Native.String
                     segments.AddRange(s.Split(array, StringSplitOptions.None));
                 }
 
-                var a = Engine.Array.Construct(Arguments.Empty, (uint) segments.Count);
-                for (int i = 0; i < segments.Count && i < limit; i++)
+                var length = (uint) System.Math.Min(segments.Count, limit);
+                var a = Engine.Array.ConstructFast(length);
+                for (int i = 0; i < length; i++)
                 {
-                    a.SetIndexValue((uint) i, segments[i], throwOnError: false);
+                    a.SetIndexValue((uint) i, segments[i], updateLength: false);
                 }
+                a.SetLength(length);
                 return a;
             }
         }
@@ -648,7 +650,7 @@ namespace Jint.Native.String
                         }
 
                         var matchStr = result.Get("0");
-                        a.SetIndexValue(n, matchStr, throwOnError: false);
+                        a.SetIndexValue(n, matchStr, updateLength: false);
                         n++;
                     }
                 }
@@ -656,6 +658,7 @@ namespace Jint.Native.String
                 {
                     return Null;
                 }
+                a.SetLength(n);
                 return a;
             }
 

+ 6 - 20
Jint/Options.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
 using System.Reflection;
-using System.Runtime.CompilerServices;
 using Jint.Native;
 using Jint.Runtime.Interop;
 
@@ -14,7 +13,6 @@ namespace Jint
         private bool _discardGlobal;
         private bool _strict;
         private bool _allowDebuggerStatement;
-        private bool _debugMode;
         private bool _allowClr;
         private readonly List<IObjectConverter> _objectConverters = new List<IObjectConverter>();
         private int _maxStatements;
@@ -63,7 +61,7 @@ namespace Jint
         /// </summary>
         public Options DebugMode(bool debugMode = true)
         {
-            _debugMode = debugMode;
+            IsDebugMode = debugMode;
             return this;
         }
 
@@ -156,19 +154,11 @@ namespace Jint
 
         internal bool _IsGlobalDiscarded => _discardGlobal;
 
-        internal bool _IsStrict
-        {
-            [MethodImpl(MethodImplOptions.AggressiveInlining)]
-            get { return _strict; }
-        }
+        internal bool IsStrict => _strict;
 
         internal bool _IsDebuggerStatementAllowed => _allowDebuggerStatement;
 
-        internal bool _IsDebugMode
-        {
-            [MethodImpl(MethodImplOptions.AggressiveInlining)]
-            get { return _debugMode; }
-        }
+        internal bool IsDebugMode { get; private set; }
 
         internal bool _IsClrAllowed => _allowClr;
 
@@ -178,13 +168,9 @@ namespace Jint
 
         internal List<IObjectConverter> _ObjectConverters => _objectConverters;
 
-        internal int _MaxStatements
-        {
-            [MethodImpl(MethodImplOptions.AggressiveInlining)]
-            get { return _maxStatements; }
-        }
+        internal int MaxStatementCount => _maxStatements;
 
-        internal int _MaxRecursionDepth => _maxRecursionDepth;
+        internal int MaxRecursionDepth => _maxRecursionDepth;
 
         internal TimeSpan _TimeoutInterval => _timeoutInterval;
 
@@ -192,7 +178,7 @@ namespace Jint
 
         internal TimeZoneInfo _LocalTimeZone => _localTimeZone;
 
-        internal IReferenceResolver  _ReferenceResolver => _referenceResolver;
+        internal IReferenceResolver  ReferenceResolver => _referenceResolver;
 
     }
 }

+ 0 - 41
Jint/Pooling/CompletionPool.cs

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

+ 19 - 54
Jint/Runtime/Completion.cs

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

+ 7 - 2
Jint/Runtime/Environments/LexicalEnvironment.cs

@@ -1,4 +1,5 @@
-using Jint.Native;
+using System.Runtime.CompilerServices;
+using Jint.Native;
 using Jint.Native.Object;
 using Jint.Runtime.References;
 
@@ -22,7 +23,11 @@ namespace Jint.Runtime.Environments
             _outer = outer;
         }
 
-        public EnvironmentRecord Record => _record;
+        public EnvironmentRecord Record
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get { return _record; }
+        }
 
         public LexicalEnvironment Outer => _outer;
 

+ 17 - 13
Jint/Runtime/ExpressionIntepreter.cs

@@ -17,10 +17,18 @@ namespace Jint.Runtime
     public class ExpressionInterpreter
     {
         private readonly Engine _engine;
+        private readonly bool _isDebugMode;
+        private readonly int _maxRecursionDepth;
+        private readonly IReferenceResolver _referenceResolver;
 
         public ExpressionInterpreter(Engine engine)
         {
             _engine = engine;
+            
+            // gather some options as fields for faster checks
+            _isDebugMode = engine.Options.IsDebugMode;
+            _maxRecursionDepth = engine.Options.MaxRecursionDepth;
+            _referenceResolver = engine.Options.ReferenceResolver;
         }
 
         private object EvaluateExpression(Expression expression)
@@ -817,11 +825,8 @@ namespace Jint.Runtime
         public JsValue EvaluateCallExpression(CallExpression callExpression)
         {
             var callee = EvaluateExpression(callExpression.Callee);
-            var options = _engine.Options;
-            var maxRecursionDepth = options._MaxRecursionDepth;
-            var debug = options._IsDebugMode;
             
-            if (debug)
+            if (_isDebugMode)
             {
                 _engine.DebugHandler.AddToDebugCallStack(callExpression);
             }
@@ -862,13 +867,13 @@ namespace Jint.Runtime
 
             var r = callee as Reference;
 
-            if (maxRecursionDepth >= 0)
+            if (_maxRecursionDepth >= 0)
             {
                 var stackItem = new CallStackElement(callExpression, func, r != null ? r.GetReferencedName() : "anonymous function");
 
                 var recursionDepth = _engine.CallStack.Push(stackItem);
 
-                if (recursionDepth > maxRecursionDepth)
+                if (recursionDepth > _maxRecursionDepth)
                 {
                     _engine.CallStack.Pop();
                     throw new RecursionDepthOverflowException(_engine.CallStack, stackItem.ToString());
@@ -882,12 +887,10 @@ namespace Jint.Runtime
 
             if (!func.IsObject())
             {
-
-                if (options._ReferenceResolver == null ||
-                    !options._ReferenceResolver.TryGetCallable(_engine, callee, out func))
+                if (_referenceResolver == null || !_referenceResolver.TryGetCallable(_engine, callee, out func))
                 {
                     throw new JavaScriptException(_engine.TypeError,
-                        r == null ? "" : string.Format("Property '{0}' of object is not a function", r.GetReferencedName()));
+                        r == null ? "" : $"Property '{r.GetReferencedName()}' of object is not a function");
                 }
             }
 
@@ -924,12 +927,12 @@ namespace Jint.Runtime
 
             var result = callable.Call(thisObject, arguments);
 
-            if (debug)
+            if (_isDebugMode)
             {
                 _engine.DebugHandler.PopDebugCallStack();
             }
 
-            if (maxRecursionDepth >= 0)
+            if (_maxRecursionDepth >= 0)
             {
                 _engine.CallStack.Pop();
             }
@@ -1044,9 +1047,10 @@ namespace Jint.Runtime
                 if (expr != null)
                 {
                     var value = _engine.GetValue(EvaluateExpression((Expression) expr), true);
-                    a.SetIndexValue((uint) n, value, throwOnError: false);
+                    a.SetIndexValue((uint) n, value, updateLength: false);
                 }
             }
+            a.SetLength((uint) count);
             _engine.JsValueArrayPool.ReturnArray(jsValues);
 
             return a;

+ 46 - 64
Jint/Runtime/StatementInterpreter.cs

@@ -30,7 +30,7 @@ namespace Jint.Runtime
         public Completion ExecuteExpressionStatement(ExpressionStatement expressionStatement)
         {
             var exprRef = _engine.EvaluateExpression(expressionStatement.Expression);
-            return _engine.CompletionPool.Rent(Completion.Normal, _engine.GetValue(exprRef, true), null);
+            return new Completion(CompletionType.Normal, _engine.GetValue(exprRef, true), null);
         }
 
         public Completion ExecuteIfStatement(IfStatement ifStatement)
@@ -58,11 +58,10 @@ namespace Jint.Runtime
             // containing label and could keep a table per program with all the labels
             // labeledStatement.Body.LabelSet = labeledStatement.Label;
             var result = ExecuteStatement(labeledStatement.Body);
-            if (result.Type == Completion.Break && result.Identifier == labeledStatement.Label.Name)
+            if (result.Type == CompletionType.Break && result.Identifier == labeledStatement.Label.Name)
             {
                 var value = result.Value;
-                _engine.CompletionPool.Return(result);
-                return _engine.CompletionPool.Rent(Completion.Normal, value, null);
+                return new Completion(CompletionType.Normal, value, null);
             }
 
             return result;
@@ -85,27 +84,25 @@ namespace Jint.Runtime
                 {
                     v = stmt.Value;
                 }
-                if (stmt.Type != Completion.Continue || stmt.Identifier != doWhileStatement?.LabelSet?.Name)
+                if (stmt.Type != CompletionType.Continue || stmt.Identifier != doWhileStatement?.LabelSet?.Name)
                 {
-                    if (stmt.Type == Completion.Break && (stmt.Identifier == null || stmt.Identifier == doWhileStatement?.LabelSet?.Name))
+                    if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == doWhileStatement?.LabelSet?.Name))
                     {
-                        _engine.CompletionPool.Return(stmt);
-                        return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+                        return new Completion(CompletionType.Normal, v, null);
                     }
 
-                    if (stmt.Type != Completion.Normal)
+                    if (stmt.Type != CompletionType.Normal)
                     {
                         return stmt;
                     }
                 }
 
-                _engine.CompletionPool.Return(stmt);
                 var exprRef = _engine.EvaluateExpression(doWhileStatement.Test);
                 iterating = TypeConverter.ToBoolean(_engine.GetValue(exprRef, true));
 
             } while (iterating);
 
-            return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+            return new Completion(CompletionType.Normal, v, null);
         }
 
         /// <summary>
@@ -121,7 +118,7 @@ namespace Jint.Runtime
                 var jsValue = _engine.GetValue(_engine.EvaluateExpression(whileStatement.Test), true);
                 if (!TypeConverter.ToBoolean(jsValue))
                 {
-                    return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+                    return new Completion(CompletionType.Normal, v, null);
                 }
 
                 var stmt = ExecuteStatement(whileStatement.Body);
@@ -131,21 +128,18 @@ namespace Jint.Runtime
                     v = stmt.Value;
                 }
 
-                if (stmt.Type != Completion.Continue || stmt.Identifier != whileStatement?.LabelSet?.Name)
+                if (stmt.Type != CompletionType.Continue || stmt.Identifier != whileStatement?.LabelSet?.Name)
                 {
-                    if (stmt.Type == Completion.Break && (stmt.Identifier == null || stmt.Identifier == whileStatement?.LabelSet?.Name))
+                    if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == whileStatement?.LabelSet?.Name))
                     {
-                        _engine.CompletionPool.Return(stmt);
-                        return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+                        return new Completion(CompletionType.Normal, v, null);
                     }
 
-                    if (stmt.Type != Completion.Normal)
+                    if (stmt.Type != CompletionType.Normal)
                     {
                         return stmt;
                     }
                 }
-
-                _engine.CompletionPool.Return(stmt);
             }
         }
 
@@ -162,7 +156,6 @@ namespace Jint.Runtime
                 if (init.Type == Nodes.VariableDeclaration)
                 {
                     var c = ExecuteStatement((Statement) init);
-                    _engine.CompletionPool.Return(c);
 
                 }
                 else
@@ -179,7 +172,7 @@ namespace Jint.Runtime
                     var testExprRef = _engine.EvaluateExpression(forStatement.Test);
                     if (!TypeConverter.ToBoolean(_engine.GetValue(testExprRef, true)))
                     {
-                        return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+                        return new Completion(CompletionType.Normal, v, null);
                     }
                 }
 
@@ -188,14 +181,15 @@ namespace Jint.Runtime
                 {
                     v = stmt.Value;
                 }
-                if (stmt.Type == Completion.Break && (stmt.Identifier == null || stmt.Identifier == forStatement?.LabelSet?.Name))
+
+                var stmtType = stmt.Type;
+                if (stmtType == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == forStatement?.LabelSet?.Name))
                 {
-                    _engine.CompletionPool.Return(stmt);
-                    return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+                    return new Completion(CompletionType.Normal, v, null);
                 }
-                if (stmt.Type != Completion.Continue || ((stmt.Identifier != null) && stmt.Identifier != forStatement?.LabelSet?.Name))
+                if (stmtType != CompletionType.Continue || ((stmt.Identifier != null) && stmt.Identifier != forStatement?.LabelSet?.Name))
                 {
-                    if (stmt.Type != Completion.Normal)
+                    if (stmtType != CompletionType.Normal)
                     {
                         return stmt;
                     }
@@ -204,7 +198,6 @@ namespace Jint.Runtime
                 {
                     _engine.GetValue(_engine.EvaluateExpression(forStatement.Update), true);
                 }
-                _engine.CompletionPool.Return(stmt);
             }
         }
 
@@ -268,25 +261,23 @@ namespace Jint.Runtime
                     {
                         v = stmt.Value;
                     }
-                    if (stmt.Type == Completion.Break)
+                    if (stmt.Type == CompletionType.Break)
                     {
-                        _engine.CompletionPool.Return(stmt);
-                        return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+                        return new Completion(CompletionType.Normal, v, null);
                     }
-                    if (stmt.Type != Completion.Continue)
+                    if (stmt.Type != CompletionType.Continue)
                     {
-                        if (stmt.Type != Completion.Normal)
+                        if (stmt.Type != CompletionType.Normal)
                         {
                             return stmt;
                         }
                     }
-                    _engine.CompletionPool.Return(stmt);
                 }
 
                 cursor = cursor.Prototype;
             }
 
-            return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+            return new Completion(CompletionType.Normal, v, null);
         }
 
         /// <summary>
@@ -296,10 +287,10 @@ namespace Jint.Runtime
         /// <returns></returns>
         public Completion ExecuteContinueStatement(ContinueStatement continueStatement)
         {
-            return _engine.CompletionPool.Rent(
-                Completion.Continue,
+            return new Completion(
+                CompletionType.Continue,
                 null,
-                continueStatement.Label != null ? continueStatement.Label.Name : null);
+                continueStatement.Label?.Name);
         }
 
         /// <summary>
@@ -309,10 +300,10 @@ namespace Jint.Runtime
         /// <returns></returns>
         public Completion ExecuteBreakStatement(BreakStatement breakStatement)
         {
-            return _engine.CompletionPool.Rent(
-                Completion.Break,
+            return new Completion(
+                CompletionType.Break,
                 null,
-                breakStatement.Label != null ? breakStatement.Label.Name : null);
+                breakStatement.Label?.Name);
         }
 
         /// <summary>
@@ -324,11 +315,11 @@ namespace Jint.Runtime
         {
             if (statement.Argument == null)
             {
-                return _engine.CompletionPool.Rent(Completion.Return, Undefined.Instance, null);
+                return new Completion(CompletionType.Return, Undefined.Instance, null);
             }
 
             var jsValue = _engine.GetValue(_engine.EvaluateExpression(statement.Argument), true);
-            return _engine.CompletionPool.Rent(Completion.Return, jsValue, null);
+            return new Completion(CompletionType.Return, jsValue, null);
         }
 
         /// <summary>
@@ -351,7 +342,7 @@ namespace Jint.Runtime
             }
             catch (JavaScriptException e)
             {
-                c = _engine.CompletionPool.Rent(Completion.Throw, e.Error, null, withStatement.Location);
+                c = new Completion(CompletionType.Throw, e.Error, null, withStatement.Location);
             }
             finally
             {
@@ -370,9 +361,9 @@ namespace Jint.Runtime
         {
             var jsValue = _engine.GetValue(_engine.EvaluateExpression(switchStatement.Discriminant), true);
             var r = ExecuteSwitchBlock(switchStatement.Cases, jsValue);
-            if (r.Type == Completion.Break && r.Identifier == switchStatement.LabelSet?.Name)
+            if (r.Type == CompletionType.Break && r.Identifier == switchStatement.LabelSet?.Name)
             {
-                return _engine.CompletionPool.Rent(Completion.Normal, r.Value, null);
+                return new Completion(CompletionType.Normal, r.Value, null);
             }
             return r;
         }
@@ -403,7 +394,7 @@ namespace Jint.Runtime
                 if (hit && clause.Consequent != null)
                 {
                     var r = ExecuteStatementList(clause.Consequent);
-                    if (r.Type != Completion.Normal)
+                    if (r.Type != CompletionType.Normal)
                     {
                         return r;
                     }
@@ -416,7 +407,7 @@ namespace Jint.Runtime
             if (hit == false && defaultCase != null)
             {
                 var r = ExecuteStatementList(defaultCase.Consequent);
-                if (r.Type != Completion.Normal)
+                if (r.Type != CompletionType.Normal)
                 {
                     return r;
                 }
@@ -424,7 +415,7 @@ namespace Jint.Runtime
                 v = r.Value ?? Undefined.Instance;
             }
 
-            return _engine.CompletionPool.Rent(Completion.Normal, v, null);
+            return new Completion(CompletionType.Normal, v, null);
         }
 
         public Completion ExecuteStatementList(List<StatementListItem> statementList)
@@ -440,36 +431,27 @@ namespace Jint.Runtime
                 {
                     s = (Statement) statementList[i];
                     c = ExecuteStatement(s);
-                    if (c.Type != Completion.Normal)
+                    if (c.Type != CompletionType.Normal)
                     {
-                        var executeStatementList = _engine.CompletionPool.Rent(
+                        var executeStatementList = new Completion(
                             c.Type,
                             c.Value ?? sl.Value,
                             c.Identifier,
                             c.Location);
 
-                        _engine.CompletionPool.Return(sl);
-                        _engine.CompletionPool.Return(c);
                         return executeStatementList;
                     }
 
-                    if (sl != c)
-                    {
-                        _engine.CompletionPool.Return(sl);
-                    }
-
                     sl = c;
                 }
             }
             catch (JavaScriptException v)
             {
-                var completion = _engine.CompletionPool.Rent(Completion.Throw, v.Error, null, v.Location ?? s.Location);
+                var completion = new Completion(CompletionType.Throw, v.Error, null, v.Location ?? s.Location);
                 return completion;
             }
 
-            var rent = _engine.CompletionPool.Rent(c.Type, c.GetValueOrDefault(), c.Identifier);
-            _engine.CompletionPool.Return(c);
-            return rent;
+            return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
         }
 
         /// <summary>
@@ -480,7 +462,7 @@ namespace Jint.Runtime
         public Completion ExecuteThrowStatement(ThrowStatement throwStatement)
         {
             var jsValue = _engine.GetValue(_engine.EvaluateExpression(throwStatement.Argument), true);
-            return _engine.CompletionPool.Rent(Completion.Throw, jsValue, null, throwStatement.Location);
+            return new Completion(CompletionType.Throw, jsValue, null, throwStatement.Location);
         }
 
         /// <summary>
@@ -491,7 +473,7 @@ namespace Jint.Runtime
         public Completion ExecuteTryStatement(TryStatement tryStatement)
         {
             var b = ExecuteStatement(tryStatement.Block);
-            if (b.Type == Completion.Throw)
+            if (b.Type == CompletionType.Throw)
             {
                 // execute catch
                 var catchClause = tryStatement.Handler;
@@ -510,7 +492,7 @@ namespace Jint.Runtime
             if (tryStatement.Finalizer != null)
             {
                 var f = ExecuteStatement(tryStatement.Finalizer);
-                if (f.Type == Completion.Normal)
+                if (f.Type == CompletionType.Normal)
                 {
                     return b;
                 }

+ 2 - 2
Jint/Runtime/TypeConverter.cs

@@ -461,8 +461,8 @@ namespace Jint.Runtime
                 return;
             }
 
-            if (engine.Options._ReferenceResolver != null &&
-                engine.Options._ReferenceResolver.CheckCoercible(o))
+            var referenceResolver = engine.Options.ReferenceResolver;
+            if (referenceResolver != null && referenceResolver.CheckCoercible(o))
             {
                 return;
             }