Bläddra i källkod

Optimizing property access performance

Sebastien Ros 10 år sedan
förälder
incheckning
55b14dcf34

+ 2 - 2
Jint/Engine.cs

@@ -732,7 +732,7 @@ namespace Jint
                     {
                         var go = Global;
                         var existingProp = go.GetProperty(fn);
-                        if (existingProp.Configurable.Value.AsBoolean())
+                        if (existingProp.Configurable.Value)
                         {
                             go.DefineOwnProperty(fn,
                                                  new PropertyDescriptor(
@@ -744,7 +744,7 @@ namespace Jint
                         }
                         else
                         {
-                            if (existingProp.IsAccessorDescriptor() || (!existingProp.Enumerable.Value.AsBoolean()))
+                            if (existingProp.IsAccessorDescriptor() || (!existingProp.Enumerable.Value))
                             {
                                 throw new JavaScriptException(TypeError);
                             }

+ 2 - 0
Jint/Jint.csproj

@@ -193,6 +193,8 @@
     <Compile Include="Runtime\Interop\TypeReference.cs" />
     <Compile Include="Runtime\Interop\TypeReferencePrototype.cs" />
     <Compile Include="Runtime\JavaScriptException.cs" />
+    <Compile Include="Runtime\MruPropertyCache2.cs" />
+    <Compile Include="Runtime\MruPropertyCache.cs" />
     <Compile Include="Runtime\RecursionDepthOverflowException.cs" />
     <Compile Include="Runtime\References\Reference.cs" />
     <Compile Include="Runtime\StatementInterpreter.cs" />

+ 39 - 0
Jint/Native/Argument/ArgumentsObject.cs

@@ -107,6 +107,45 @@ namespace Jint.Native.Argument
             return base.GetOwnProperty(propertyName);
         }
 
+        /// Implementation from ObjectInstance official specs as the one 
+        /// in ObjectInstance is optimized for the general case and wouldn't work 
+        /// for arrays
+        public override void Put(string propertyName, JsValue value, bool throwOnError)
+        {
+            if (!CanPut(propertyName))
+            {
+                if (throwOnError)
+                {
+                    throw new JavaScriptException(Engine.TypeError);
+                }
+
+                return;
+            }
+
+            var ownDesc = GetOwnProperty(propertyName);
+
+            if (ownDesc.IsDataDescriptor())
+            {
+                var valueDesc = new PropertyDescriptor(value: value, writable: null, enumerable: null, configurable: null);
+                DefineOwnProperty(propertyName, valueDesc, throwOnError);
+                return;
+            }
+
+            // property is an accessor or inherited
+            var desc = GetProperty(propertyName);
+
+            if (desc.IsAccessorDescriptor())
+            {
+                var setter = desc.Set.Value.TryCast<ICallable>();
+                setter.Call(new JsValue(this), new[] { value });
+            }
+            else
+            {
+                var newDesc = new PropertyDescriptor(value, true, true, true);
+                DefineOwnProperty(propertyName, newDesc, throwOnError);
+            }
+        }
+
         public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             if (!Strict && ParameterMap != null)

+ 43 - 4
Jint/Native/Array/ArrayInstance.cs

@@ -22,6 +22,45 @@ namespace Jint.Native.Array
             }
         }
 
+        /// Implementation from ObjectInstance official specs as the one 
+        /// in ObjectInstance is optimized for the general case and wouldn't work 
+        /// for arrays
+        public override void Put(string propertyName, JsValue value, bool throwOnError)
+        {
+            if (!CanPut(propertyName))
+            {
+                if (throwOnError)
+                {
+                    throw new JavaScriptException(Engine.TypeError);
+                }
+
+                return;
+            }
+
+            var ownDesc = GetOwnProperty(propertyName);
+
+            if (ownDesc.IsDataDescriptor())
+            {
+                var valueDesc = new PropertyDescriptor(value: value, writable: null, enumerable: null, configurable: null);
+                DefineOwnProperty(propertyName, valueDesc, throwOnError);
+                return;
+            }
+
+            // property is an accessor or inherited
+            var desc = GetProperty(propertyName);
+
+            if (desc.IsAccessorDescriptor())
+            {
+                var setter = desc.Set.Value.TryCast<ICallable>();
+                setter.Call(new JsValue(this), new[] { value });
+            }
+            else
+            {
+                var newDesc = new PropertyDescriptor(value, true, true, true);
+                DefineOwnProperty(propertyName, newDesc, throwOnError);
+            }
+        }
+
         public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             var oldLenDesc = GetOwnProperty("length");
@@ -45,7 +84,7 @@ namespace Jint.Native.Array
                 {
                     return base.DefineOwnProperty("length", newLenDesc, throwOnError);
                 }
-                if (!oldLenDesc.Writable.Value.AsBoolean())
+                if (!oldLenDesc.Writable.Value)
                 {
                     if (throwOnError)
                     {
@@ -55,7 +94,7 @@ namespace Jint.Native.Array
                     return false;
                 }
                 bool newWritable;
-                if (!newLenDesc.Writable.HasValue || newLenDesc.Writable.Value.AsBoolean())
+                if (!newLenDesc.Writable.HasValue || newLenDesc.Writable.Value)
                 {
                     newWritable = true;
                 }
@@ -88,7 +127,7 @@ namespace Jint.Native.Array
                                 newLenDesc.Value = new JsValue(index + 1);
                                 if (!newWritable)
                                 {
-                                    newLenDesc.Writable = JsValue.False;
+                                    newLenDesc.Writable = false;
                                 }
                                 base.DefineOwnProperty("length", newLenDesc, false);
                                 if (throwOnError)
@@ -134,7 +173,7 @@ namespace Jint.Native.Array
             else if (IsArrayIndex(propertyName))
             {
                 var index = TypeConverter.ToUint32(propertyName);
-                if (index >= oldLen && !oldLenDesc.Writable.Value.AsBoolean())
+                if (index >= oldLen && !oldLenDesc.Writable.Value)
                 {
                     if (throwOnError)
                     {

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

@@ -217,7 +217,7 @@ namespace Jint.Native.Object
             var descriptors = new List<KeyValuePair<string, PropertyDescriptor>>();
             foreach (var p in props.Properties)
             {
-                if (!p.Value.Enumerable.HasValue || !p.Value.Enumerable.Value.AsBoolean())
+                if (!p.Value.Enumerable.HasValue || !p.Value.Enumerable.Value)
                 {
                     continue;
                 }
@@ -245,9 +245,9 @@ namespace Jint.Native.Object
 
             foreach (var prop in o.Properties)
             {
-                if (prop.Value.Configurable.HasValue && prop.Value.Configurable.Value.AsBoolean())
+                if (prop.Value.Configurable.HasValue && prop.Value.Configurable.Value)
                 {
-                    prop.Value.Configurable = JsValue.False;
+                    prop.Value.Configurable = false;
                 }
 
                 o.DefineOwnProperty(prop.Key, prop.Value, true);
@@ -273,14 +273,14 @@ namespace Jint.Native.Object
                 var desc = o.GetOwnProperty(p);
                 if (desc.IsDataDescriptor())
                 {
-                    if (desc.Writable.HasValue && desc.Writable.Value.AsBoolean())
+                    if (desc.Writable.HasValue && desc.Writable.Value)
                     {
-                        desc.Writable = JsValue.False;
+                        desc.Writable = false;
                     }
                 }
-                if (desc.Configurable.HasValue && desc.Configurable.Value.AsBoolean())
+                if (desc.Configurable.HasValue && desc.Configurable.Value)
                 {
-                    desc.Configurable = JsValue.False;
+                    desc.Configurable = false;
                 }
                 o.DefineOwnProperty(p, desc, true);
             }
@@ -343,12 +343,12 @@ namespace Jint.Native.Object
                 var desc = o.GetOwnProperty(p);
                 if (desc.IsDataDescriptor())
                 {
-                    if (desc.Writable.HasValue && desc.Writable.Value.AsBoolean())
+                    if (desc.Writable.HasValue && desc.Writable.Value)
                     {
                         return false;
                     }
                 }
-                if (desc.Configurable.HasValue && desc.Configurable.Value.AsBoolean())
+                if (desc.Configurable.HasValue && desc.Configurable.Value)
                 {
                     return false;
                 }
@@ -384,7 +384,7 @@ namespace Jint.Native.Object
             }
 
             var enumerableProperties = o.Properties
-                .Where(x => x.Value.Enumerable.HasValue && x.Value.Enumerable.Value.AsBoolean())
+                .Where(x => x.Value.Enumerable.HasValue && x.Value.Enumerable.Value)
                 .ToArray();
             var n = enumerableProperties.Length;
             var array = Engine.Array.Construct(new JsValue[] {n});

+ 43 - 83
Jint/Native/Object/ObjectInstance.cs

@@ -1,6 +1,4 @@
 using System.Collections.Generic;
-using Jint.Native.Date;
-using Jint.Native.String;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 
@@ -11,7 +9,7 @@ namespace Jint.Native.Object
         public ObjectInstance(Engine engine)
         {
             Engine = engine;
-            Properties = new Dictionary<string, PropertyDescriptor>();
+            Properties = new MruPropertyCache2<string, PropertyDescriptor>();
         }
 
         public Engine Engine { get; set; }
@@ -69,7 +67,7 @@ namespace Jint.Native.Object
             var callable = getter.TryCast<ICallable>();
             return callable.Call(this, Arguments.Empty);
         }
-        
+
         /// <summary>
         /// Returns the Property Descriptor of the named 
         /// own property of this object, or undefined if 
@@ -150,9 +148,13 @@ namespace Jint.Native.Object
 
             if (ownDesc.IsDataDescriptor())
             {
-                var valueDesc = new PropertyDescriptor(value: value, writable: null, enumerable:null, configurable:null);
-                DefineOwnProperty(propertyName, valueDesc, throwOnError);
+                ownDesc.Value = value;
                 return;
+                
+                // as per specification
+                // var valueDesc = new PropertyDescriptor(value: value, writable: null, enumerable: null, configurable: null);
+                // DefineOwnProperty(propertyName, valueDesc, throwOnError);
+                // return;
             }
 
             // property is an accessor or inherited
@@ -194,7 +196,7 @@ namespace Jint.Native.Object
                     return true;
                 }
 
-                return desc.Writable.HasValue && desc.Writable.Value.AsBoolean();
+                return desc.Writable.HasValue && desc.Writable.Value;
             }
 
             if (Prototype == null)
@@ -225,7 +227,7 @@ namespace Jint.Native.Object
             }
             else
             {
-                return inherited.Writable.HasValue && inherited.Writable.Value.AsBoolean();
+                return inherited.Writable.HasValue && inherited.Writable.Value;
             }
         }
 
@@ -258,7 +260,7 @@ namespace Jint.Native.Object
                 return true;
             }
 
-            if (desc.Configurable.HasValue && desc.Configurable.Value.AsBoolean())
+            if (desc.Configurable.HasValue && desc.Configurable.Value)
             {
                 Properties.Remove(propertyName);
                 return true;
@@ -347,7 +349,11 @@ namespace Jint.Native.Object
         public virtual bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             var current = GetOwnProperty(propertyName);
-            
+
+            if (current == desc) {
+                return true;
+            }
+
             if (current == PropertyDescriptor.Undefined)
             {
                 if (!Extensible)
@@ -366,13 +372,17 @@ namespace Jint.Native.Object
                         Properties[propertyName] = new PropertyDescriptor(desc)
                         {
                             Value = desc.Value.HasValue ? desc.Value : JsValue.Undefined,
-                            Writable = desc.Writable.HasValue ? desc.Writable : false
+                            Writable = desc.Writable.HasValue ? desc.Writable.Value : false,
+                            Enumerable = desc.Enumerable.HasValue ? desc.Enumerable.Value : false,
+                            Configurable = desc.Configurable.HasValue ? desc.Configurable.Value : false
                         };
                     }
                     else
                     {
                         Properties[propertyName] = new PropertyDescriptor(desc)
                         {
+                            Get = desc.Get,
+                            Set = desc.Set,
                             Enumerable = desc.Enumerable.HasValue ? desc.Enumerable : false,
                             Configurable = desc.Configurable.HasValue ? desc.Configurable : false,
                         };
@@ -390,76 +400,26 @@ namespace Jint.Native.Object
                 !current.Set.HasValue &&
                 !current.Value.HasValue)
             {
+
                 return true;
             }
 
             // Step 6
-            var configurableIsSame = current.Configurable.HasValue
-                ? desc.Configurable.HasValue && (current.Configurable.Value == desc.Configurable.Value)
-                : !desc.Configurable.HasValue;
-
-            var enumerableIsSame = current.Enumerable.HasValue
-                ? desc.Enumerable.HasValue && (current.Enumerable.Value == desc.Enumerable.Value)
-                : !desc.Enumerable.HasValue;
-
-            var writableIsSame = true;
-            var valueIsSame = true;
-
-            if (current.IsDataDescriptor() && desc.IsDataDescriptor())
-            {
-                var currentDataDescriptor = current;
-                var descDataDescriptor = desc;
-                writableIsSame = currentDataDescriptor.Writable.HasValue
-                ? descDataDescriptor.Writable.HasValue && (currentDataDescriptor.Writable.Value == descDataDescriptor.Writable.Value)
-                : !descDataDescriptor.Writable.HasValue;
-
-                var valueA = currentDataDescriptor.Value.HasValue
-                    ? currentDataDescriptor.Value.Value
-                    : Undefined.Instance;
-
-                var valueB = descDataDescriptor.Value.HasValue
-                                    ? descDataDescriptor.Value.Value
-                                    : Undefined.Instance;
-
-                valueIsSame = ExpressionInterpreter.SameValue(valueA, valueB);
-            }
-            else if (current.IsAccessorDescriptor() && desc.IsAccessorDescriptor())
-            {
-                var currentAccessorDescriptor = current;
-                var descAccessorDescriptor = desc;
-
-                var getValueA = currentAccessorDescriptor.Get.HasValue
-                    ? currentAccessorDescriptor.Get.Value
-                    : Undefined.Instance;
-
-                var getValueB = descAccessorDescriptor.Get.HasValue
-                                    ? descAccessorDescriptor.Get.Value
-                                    : Undefined.Instance;
-
-                var setValueA = currentAccessorDescriptor.Set.HasValue
-                   ? currentAccessorDescriptor.Set.Value
-                   : Undefined.Instance;
-
-                var setValueB = descAccessorDescriptor.Set.HasValue
-                                    ? descAccessorDescriptor.Set.Value
-                                    : Undefined.Instance;
-
-                valueIsSame = ExpressionInterpreter.SameValue(getValueA, getValueB)
-                              && ExpressionInterpreter.SameValue(setValueA, setValueB);
-            }
-            else
-            {
-                valueIsSame = false;
-            }
-
-            if (configurableIsSame && enumerableIsSame && writableIsSame && valueIsSame)
-            {
+            if (
+                current.Configurable == desc.Configurable &&
+                current.Writable == desc.Writable &&
+                current.Enumerable == desc.Enumerable &&
+
+                ((!current.Get.HasValue && !desc.Get.HasValue) || (current.Get.HasValue && desc.Get.HasValue && ExpressionInterpreter.SameValue(current.Get.Value, desc.Get.Value))) &&
+                ((!current.Set.HasValue && !desc.Set.HasValue) || (current.Set.HasValue && desc.Set.HasValue && ExpressionInterpreter.SameValue(current.Set.Value, desc.Set.Value))) &&
+                ((!current.Value.HasValue && !desc.Value.HasValue) || (current.Value.HasValue && desc.Value.HasValue && ExpressionInterpreter.StrictlyEqual(current.Value.Value, desc.Value.Value)))
+            ) {
                 return true;
             }
 
-            if (!current.Configurable.HasValue || !current.Configurable.Value.AsBoolean())
+            if (!current.Configurable.HasValue || !current.Configurable.Value)
             {
-                if (desc.Configurable.HasValue && desc.Configurable.Value.AsBoolean())
+                if (desc.Configurable.HasValue && desc.Configurable.Value)
                 {
                     if (throwOnError)
                     {
@@ -485,7 +445,7 @@ namespace Jint.Native.Object
 
                 if (current.IsDataDescriptor() != desc.IsDataDescriptor())
                 {
-                    if (!current.Configurable.HasValue || !current.Configurable.Value.AsBoolean())
+                    if (!current.Configurable.HasValue || !current.Configurable.Value)
                     {
                         if (throwOnError)
                         {
@@ -500,8 +460,8 @@ namespace Jint.Native.Object
                         Properties[propertyName] = current = new PropertyDescriptor(
                             get: Undefined.Instance,
                             set: Undefined.Instance,
-                            enumerable: current.Enumerable.HasValue && current.Enumerable.Value.AsBoolean(),
-                            configurable: current.Configurable.HasValue && current.Configurable.Value.AsBoolean()
+                            enumerable: current.Enumerable,
+                            configurable: current.Configurable
                             );
                     }
                     else
@@ -509,16 +469,16 @@ namespace Jint.Native.Object
                         Properties[propertyName] = current = new PropertyDescriptor(
                             value: Undefined.Instance, 
                             writable: null,
-                            enumerable: current.Enumerable.HasValue && current.Enumerable.Value.AsBoolean(),
-                            configurable: current.Configurable.HasValue && current.Configurable.Value.AsBoolean()
+                            enumerable: current.Enumerable,
+                            configurable: current.Configurable
                             );
                     }
                 }
                 else if (current.IsDataDescriptor() && desc.IsDataDescriptor())
                 {
-                    if (!current.Configurable.HasValue || current.Configurable.Value.AsBoolean() == false)
+                    if (!current.Configurable.HasValue || current.Configurable.Value == false)
                     {
-                        if (!current.Writable.HasValue || !current.Writable.Value.AsBoolean() && desc.Writable.HasValue && desc.Writable.Value.AsBoolean())
+                        if (!current.Writable.HasValue || !current.Writable.Value && desc.Writable.HasValue && desc.Writable.Value)
                         {
                             if (throwOnError)
                             {
@@ -528,9 +488,9 @@ namespace Jint.Native.Object
                             return false;
                         }
 
-                        if (!current.Writable.Value.AsBoolean())
+                        if (!current.Writable.Value)
                         {
-                            if (desc.Value.HasValue && !valueIsSame)
+                            if (desc.Value.HasValue && !ExpressionInterpreter.SameValue(desc.Value.Value, current.Value.Value))
                             {
                                 if (throwOnError)
                                 {
@@ -544,7 +504,7 @@ namespace Jint.Native.Object
                 }
                 else if (current.IsAccessorDescriptor() && desc.IsAccessorDescriptor())
                 {
-                    if (!current.Configurable.HasValue || !current.Configurable.Value.AsBoolean())
+                    if (!current.Configurable.HasValue || !current.Configurable.Value)
                     {
                         if ((desc.Set.HasValue && !ExpressionInterpreter.SameValue(desc.Set.Value, current.Set.HasValue ? current.Set.Value : Undefined.Instance))
                             ||

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

@@ -38,7 +38,7 @@ namespace Jint.Native.Object
             {
                 return false;
             }
-            return desc.Enumerable.HasValue && desc.Enumerable.Value.AsBoolean();
+            return desc.Enumerable.HasValue && desc.Enumerable.Value;
         }
 
         private JsValue ValueOf(JsValue thisObject, JsValue[] arguments)

+ 12 - 22
Jint/Runtime/Descriptors/PropertyDescriptor.cs

@@ -3,16 +3,6 @@ using Jint.Native.Object;
 
 namespace Jint.Runtime.Descriptors
 {
-    public enum Fields
-    {
-        Get,
-        Set,
-        Enumerable,
-        Configurable,
-        Writable,
-        Value
-    }
-    
     public class PropertyDescriptor
     {
         public static PropertyDescriptor Undefined = new PropertyDescriptor();
@@ -27,17 +17,17 @@ namespace Jint.Runtime.Descriptors
 
             if (writable.HasValue)
             {
-                Writable = new JsValue(writable.Value);
+                Writable = writable.Value;
             }
 
             if (enumerable.HasValue)
             {
-                Enumerable = new JsValue(enumerable.Value);
+                Enumerable = enumerable.Value;
             }
 
             if (configurable.HasValue)
             {
-                Configurable = new JsValue(configurable.Value);
+                Configurable = configurable.Value;
             }
         }
 
@@ -48,12 +38,12 @@ namespace Jint.Runtime.Descriptors
 
             if (enumerable.HasValue)
             {
-                Enumerable = new JsValue(enumerable.Value);
+                Enumerable = enumerable.Value;
             }
 
             if (configurable.HasValue)
             {
-                Configurable = new JsValue(configurable.Value);
+                Configurable = configurable.Value;
             }
         }
 
@@ -69,9 +59,9 @@ namespace Jint.Runtime.Descriptors
 
         public JsValue? Get { get; set; }
         public JsValue? Set { get; set; }
-        public JsValue? Enumerable { get; set; }
-        public JsValue? Writable { get; set; }
-        public JsValue? Configurable { get; set; }
+        public bool? Enumerable { get; set; }
+        public bool? Writable { get; set; }
+        public bool? Configurable { get; set; }
         public virtual JsValue? Value { get; set; }
         
         public bool IsAccessorDescriptor()
@@ -182,8 +172,8 @@ namespace Jint.Runtime.Descriptors
 
             if (desc.IsDataDescriptor())
             {
-                obj.DefineOwnProperty("value", new PropertyDescriptor(value: desc.Value.HasValue ? desc.Value.Value : Native.Undefined.Instance, writable: true, enumerable: true, configurable: true ), false);
-                obj.DefineOwnProperty("writable", new PropertyDescriptor(value: desc.Writable.HasValue && desc.Writable.Value.AsBoolean(), writable: true, enumerable: true, configurable: true), false);
+                obj.DefineOwnProperty("value", new PropertyDescriptor(value: desc.Value.HasValue ? desc.Value : Native.Undefined.Instance, writable: true, enumerable: true, configurable: true ), false);
+                obj.DefineOwnProperty("writable", new PropertyDescriptor(value: desc.Writable.HasValue && desc.Writable.Value, writable: true, enumerable: true, configurable: true), false);
             }
             else
             {
@@ -191,8 +181,8 @@ namespace Jint.Runtime.Descriptors
                 obj.DefineOwnProperty("set", new PropertyDescriptor(desc.Set ?? Native.Undefined.Instance, writable: true, enumerable: true, configurable: true), false);
             }
 
-            obj.DefineOwnProperty("enumerable", new PropertyDescriptor(value: desc.Enumerable.HasValue && desc.Enumerable.Value.AsBoolean(), writable: true, enumerable: true, configurable: true), false);
-            obj.DefineOwnProperty("configurable", new PropertyDescriptor(value: desc.Configurable.HasValue && desc.Configurable.Value.AsBoolean(), writable: true, enumerable: true, configurable: true), false);
+            obj.DefineOwnProperty("enumerable", new PropertyDescriptor(value: desc.Enumerable.HasValue && desc.Enumerable.Value, writable: true, enumerable: true, configurable: true), false);
+            obj.DefineOwnProperty("configurable", new PropertyDescriptor(value: desc.Configurable.HasValue && desc.Configurable.Value, writable: true, enumerable: true, configurable: true), false);
 
             return obj;
         }

+ 165 - 0
Jint/Runtime/MruPropertyCache.cs

@@ -0,0 +1,165 @@
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Jint.Runtime
+{
+    public class MruPropertyCache<TKey, TValue> : IDictionary<TKey, TValue>
+    {
+        private IDictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>();
+        private LinkedList<KeyValuePair<TKey, TValue>> _list;
+        private uint _length;
+
+        public MruPropertyCache(uint length) {
+            _length = length;
+            _list = new LinkedList<KeyValuePair<TKey, TValue>>();
+            for(int i=0; i<length; i++) {
+                _list.AddLast(new KeyValuePair<TKey, TValue>(default(TKey), default(TValue)));
+            }
+        }
+
+        private bool Find(TKey key, out LinkedListNode<KeyValuePair<TKey, TValue>> result) {
+            result = _list.First;
+            while(result != null) {
+                if(key.Equals(result.Value.Key)) {
+                    return true;
+                }
+
+                result = result.Next;
+            }
+
+            return false;
+        }
+        public TValue this[TKey key] {
+            get {
+                LinkedListNode<KeyValuePair<TKey, TValue>> node;
+                if(Find(key, out node)) {
+                    return node.Value.Value;
+                }
+                
+                return _dictionary[key];
+            }
+
+            set {
+                LinkedListNode<KeyValuePair<TKey, TValue>> node;
+                if (!Find(key, out node)) {
+                    _list.AddFirst(new KeyValuePair<TKey, TValue>(key, value));
+                    _list.RemoveLast();
+                }
+                else
+                {
+                    node.Value = new KeyValuePair<TKey, TValue>(key, value);
+                }
+
+                _dictionary[key] = value;
+            }
+        }
+
+        public int Count {
+            get {
+                return _dictionary.Count;
+            }
+        }
+
+        public bool IsReadOnly {
+            get {
+                return _dictionary.IsReadOnly;
+            }
+        }
+
+        public ICollection<TKey> Keys {
+            get {
+                return _dictionary.Keys;
+            }
+        }
+
+        public ICollection<TValue> Values {
+            get {
+                return _dictionary.Values;
+            }
+        }
+
+        public void Add(KeyValuePair<TKey, TValue> item) {
+            LinkedListNode<KeyValuePair<TKey, TValue>> node;
+            if (!Find(item.Key, out node)) {
+                _list.AddFirst(item);
+                _list.RemoveLast();
+            }
+
+            _dictionary.Add(item);
+        }
+
+        public void Add(TKey key, TValue value) {
+            LinkedListNode<KeyValuePair<TKey, TValue>> node;
+            if (!Find(key, out node)) {
+                _list.AddFirst(new KeyValuePair<TKey, TValue>(key, value));
+                _list.RemoveLast();
+            }
+            _dictionary.Add(key, value);
+        }
+
+        public void Clear() {
+            _list.Clear();
+            _dictionary.Clear();
+        }
+
+        public bool Contains(KeyValuePair<TKey, TValue> item) {
+            LinkedListNode<KeyValuePair<TKey, TValue>> node;
+            if (Find(item.Key, out node)) {
+                return true;
+            }
+
+            return _dictionary.Contains(item);
+        }
+
+        public bool ContainsKey(TKey key) {
+            LinkedListNode<KeyValuePair<TKey, TValue>> node;
+            if (Find(key, out node)) {
+                return true;
+            }
+
+            return _dictionary.ContainsKey(key);
+        }
+
+        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
+            _dictionary.CopyTo(array, arrayIndex);
+        }
+
+        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
+            return _dictionary.GetEnumerator();
+        }
+
+        public bool Remove(KeyValuePair<TKey, TValue> item) {
+            LinkedListNode<KeyValuePair<TKey, TValue>> node;
+            if (Find(item.Key, out node))
+            {
+                _list.Remove(node);
+            }
+
+            return _dictionary.Remove(item);
+        }
+
+        public bool Remove(TKey key) {
+            LinkedListNode<KeyValuePair<TKey, TValue>> node;
+            if (Find(key, out node))
+            {
+                _list.Remove(node);
+            }
+
+            return _dictionary.Remove(key);
+        }
+
+        public bool TryGetValue(TKey key, out TValue value) {
+            LinkedListNode<KeyValuePair<TKey, TValue>> node;
+            if (Find(key, out node)) {
+                value = node.Value.Value;
+                return true;
+            }
+
+            return _dictionary.TryGetValue(key, out value);
+        }
+
+        IEnumerator IEnumerable.GetEnumerator() {
+            return _dictionary.GetEnumerator();
+        }
+    }
+}

+ 157 - 0
Jint/Runtime/MruPropertyCache2.cs

@@ -0,0 +1,157 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jint.Runtime {
+    public class MruPropertyCache2<TKey, TValue> : IDictionary<TKey, TValue> 
+        where TKey:class 
+        where TValue:class
+    {
+        private IDictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>();
+        private TKey _key;
+        private TValue _value;
+
+        public MruPropertyCache2() {
+        }
+
+        public TValue this[TKey key] {
+            get {
+                if (_value != null)
+                {
+                    if (key.Equals(_key))
+                    {
+                        return _value;
+                    }
+                }
+
+                return _dictionary[key];
+            }
+
+            set {
+                if(value != null)
+                {
+                    _key = key;
+                    _value = value;
+                }
+
+                _dictionary[key] = value;
+            }
+        }
+
+        public int Count {
+            get {
+                return _dictionary.Count;
+            }
+        }
+
+        public bool IsReadOnly {
+            get {
+                return _dictionary.IsReadOnly;
+            }
+        }
+
+        public ICollection<TKey> Keys {
+            get {
+                return _dictionary.Keys;
+            }
+        }
+
+        public ICollection<TValue> Values {
+            get {
+                return _dictionary.Values;
+            }
+        }
+
+        public void Add(KeyValuePair<TKey, TValue> item) {
+            if(item.Value != null)
+            {
+                _key = item.Key;
+                _value = item.Value;
+            }
+
+            _dictionary.Add(item);
+        }
+
+        public void Add(TKey key, TValue value) {
+            if (value != null)
+            {
+                _key = key;
+                _value = value;
+            }
+
+            _dictionary.Add(key, value);
+        }
+
+        public void Clear() {
+            _key = null;
+            _value = null;
+
+            _dictionary.Clear();
+        }
+
+        public bool Contains(KeyValuePair<TKey, TValue> item) {
+            if(item.Key.Equals(_key) && _value != null)
+            {
+                return true;
+            }
+
+            return _dictionary.Contains(item);
+        }
+
+        public bool ContainsKey(TKey key) {
+            if (key.Equals(_key) && _value != null)
+            {
+                return true;
+            }
+
+            return _dictionary.ContainsKey(key);
+        }
+
+        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
+            _dictionary.CopyTo(array, arrayIndex);
+        }
+
+        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
+            return _dictionary.GetEnumerator();
+        }
+
+        public bool Remove(KeyValuePair<TKey, TValue> item) {
+            if(item.Key.Equals(_key))
+            {
+                _key = null;
+                _value = null;
+            }
+
+            return _dictionary.Remove(item);
+        }
+
+        public bool Remove(TKey key) {
+            if (key.Equals(_key))
+            {
+                _key = null;
+                _value = null;
+            }
+
+            return _dictionary.Remove(key);
+        }
+
+        public bool TryGetValue(TKey key, out TValue value) {
+            if (_value != null)
+            {
+                if (key.Equals(_key))
+                {
+                    value = _value;
+                    return true;
+                }
+            }
+
+            return _dictionary.TryGetValue(key, out value);
+        }
+
+        IEnumerator IEnumerable.GetEnumerator() {
+            return _dictionary.GetEnumerator();
+        }
+    }
+}

+ 1 - 1
Jint/Runtime/StatementInterpreter.cs

@@ -244,7 +244,7 @@ namespace Jint.Runtime
                     }
 
                     var value = cursor.Properties[p];
-                    if (!value.Enumerable.HasValue || !value.Enumerable.Value.AsBoolean())
+                    if (!value.Enumerable.HasValue || !value.Enumerable.Value)
                     {
                         continue;
                     }