Răsfoiți Sursa

#451 string comparison related performance improvements (#470)

* change MruPropertyCache2 to have string key
* remove shared string property checks from ObjectInstance
* check length in array instance
* move prototype and constructor to ScriptFunctionInstance, specialize ObjectInstanceWithConstructor
Marko Lahma 7 ani în urmă
părinte
comite
34defd2fbc

+ 6 - 6
Jint/Engine.cs

@@ -510,18 +510,18 @@ namespace Jint
         /// <returns></returns>
         public JsValue GetValue(object value)
         {
-            var reference = value as Reference;
+            if (value is JsValue jsValue)
+            {
+                return jsValue;
+            }
 
+            var reference = value as Reference;
             if (reference == null)
             {
-                var completion = value as Completion;
-
-                if (completion != null)
+                if (value is Completion completion)
                 {
                     return GetValue(completion.Value);
                 }
-
-                return (JsValue)value;
             }
 
             if (reference.IsUnresolvableReference())

+ 53 - 7
Jint/Native/Array/ArrayInstance.cs

@@ -13,6 +13,9 @@ namespace Jint.Native.Array
     {
         private readonly Engine _engine;
 
+        private const string PropertyNameLength = "length";
+        private IPropertyDescriptor _length;
+
         private const int MaxDenseArrayLength = 1024 * 10;
 
         // we have dense and sparse, we usually can start with dense and fall back to sparse when necessary
@@ -75,7 +78,7 @@ namespace Jint.Native.Array
 
         public override bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
         {
-            var oldLenDesc = GetOwnProperty("length");
+            var oldLenDesc = _length;
             var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc.Value);
 
             if (propertyName == "length")
@@ -282,11 +285,36 @@ namespace Jint.Native.Array
 
         public uint GetLength()
         {
-            return GetLengthValue();
+            return TypeConverter.ToUint32(_length.Value);
+        }
+
+        protected override void AddProperty(string propertyName, IPropertyDescriptor descriptor)
+        {
+            if (propertyName == PropertyNameLength)
+            {
+                _length = descriptor;
+                return;
+            }
+            base.AddProperty(propertyName, descriptor);
+        }
+
+        protected override bool TryGetProperty(string propertyName, out IPropertyDescriptor descriptor)
+        {
+            if (propertyName == PropertyNameLength)
+            {
+                descriptor = _length;
+                return _length != null;
+            }
+            return base.TryGetProperty(propertyName, out descriptor);
         }
 
         public override IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
         {
+            if (_length != null)
+            {
+                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameLength, _length);
+            }
+
             if (_dense != null)
             {
                 for (var i = 0; i < _dense.Length; i++)
@@ -323,6 +351,11 @@ namespace Jint.Native.Array
                 return PropertyDescriptor.Undefined;
             }
 
+            if (propertyName == PropertyNameLength)
+            {
+                return _length ?? PropertyDescriptor.Undefined;
+            }
+
             return base.GetOwnProperty(propertyName);
         }
 
@@ -332,6 +365,10 @@ namespace Jint.Native.Array
             {
                 WriteArrayValue(index, desc);
             }
+            else if (propertyName == PropertyNameLength)
+            {
+                _length = desc;
+            }
             else
             {
                 base.SetOwnProperty(propertyName, desc);
@@ -342,11 +379,16 @@ namespace Jint.Native.Array
         {
             if (IsArrayIndex(p, out var index))
             {
-                return index < GetLengthValue()
+                return index < GetLength()
                        && (_sparse == null || _sparse.ContainsKey(index))
                        && (_dense == null || _dense[index] != null);
             }
 
+            if (p == PropertyNameLength)
+            {
+                return _length != null;
+            }
+
             return base.HasOwnProperty(p);
         }
 
@@ -358,6 +400,11 @@ namespace Jint.Native.Array
                 DeleteAt(index);
             }
 
+            if (p == PropertyNameLength)
+            {
+                _length = null;
+            }
+
             base.RemoveOwnProperty(p);
         }
 
@@ -415,11 +462,10 @@ namespace Jint.Native.Array
 
         internal void SetIndexValue(uint index, JsValue value, bool throwOnError)
         {
-            var length = GetLengthValue();
+            var length = GetLength();
             if (index >= length)
             {
-                var p = base.GetOwnProperty("length");
-                p.Value = index + 1;
+                _length.Value = index + 1;
             }
 
             WriteArrayValue(index, new ConfigurableEnumerableWritablePropertyDescriptor(value));
@@ -448,7 +494,7 @@ namespace Jint.Native.Array
 
         public bool TryGetValue(uint index, out JsValue value)
         {
-            value = JsValue.Undefined;
+            value = Undefined;
 
             if (!TryGetDescriptor(index, out var desc)
                 || desc == null

+ 0 - 19
Jint/Native/Function/FunctionConstructor.cs

@@ -3,7 +3,6 @@ using System.Linq;
 using Esprima;
 using Esprima.Ast;
 using Jint.Native.Object;
-using Jint.Native.String;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Environments;
@@ -47,23 +46,6 @@ namespace Jint.Native.Function
             return Construct(arguments);
         }
 
-        private string[] ParseArgumentNames(string parameterDeclaration)
-        {
-            if (string.IsNullOrWhiteSpace(parameterDeclaration))
-            {
-                return System.Array.Empty<string>();
-            }
-
-            string[] values = parameterDeclaration.Split(ArgumentNameSeparator, StringSplitOptions.RemoveEmptyEntries);
-
-            var newValues = new string[values.Length];
-            for (var i = 0; i < values.Length; i++)
-            {
-                newValues[i] = StringPrototype.TrimEx(values[i]);
-            }
-            return newValues;
-        }
-
         public ObjectInstance Construct(JsValue[] arguments)
         {
             var argCount = arguments.Length;
@@ -87,7 +69,6 @@ namespace Jint.Native.Function
                 body = TypeConverter.ToString(arguments[argCount-1]);
             }
 
-            var parameters = this.ParseArgumentNames(p);
             IFunction function;
             try
             {

+ 93 - 16
Jint/Native/Function/FunctionInstance.cs

@@ -1,11 +1,18 @@
-using Jint.Native.Object;
+using System.Collections.Generic;
+using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Environments;
 
 namespace Jint.Native.Function
 {
     public abstract class FunctionInstance : ObjectInstance, ICallable
     {
+        private const string PropertyNamePrototype = "prototype";
+        private IPropertyDescriptor _prototype;
+        private const string PropertyNameLength = "length";
+        private IPropertyDescriptor _length;
+
         private readonly Engine _engine;
 
         protected FunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict) : base(engine)
@@ -24,10 +31,10 @@ namespace Jint.Native.Function
         /// <returns></returns>
         public abstract JsValue Call(JsValue thisObject, JsValue[] arguments);
 
-        public LexicalEnvironment Scope { get; private set; }
-        
-        public string[] FormalParameters { get; private set; }
-        public bool Strict { get; private set; }
+        public LexicalEnvironment Scope { get; }
+
+        public string[] FormalParameters { get; }
+        public bool Strict { get; }
 
         public virtual bool HasInstance(JsValue v)
         {
@@ -36,7 +43,7 @@ namespace Jint.Native.Function
             {
                 return false;
             }
-            
+
             var po = Get("prototype");
             if (!po.IsObject())
             {
@@ -44,10 +51,10 @@ namespace Jint.Native.Function
             }
 
             var o = po.AsObject();
-            
+
             if (o == null)
             {
-                throw new JavaScriptException(_engine.TypeError);    
+                throw new JavaScriptException(_engine.TypeError);
             }
 
             while (true)
@@ -58,6 +65,7 @@ namespace Jint.Native.Function
                 {
                     return false;
                 }
+
                 if (vObj == o)
                 {
                     return true;
@@ -65,13 +73,7 @@ namespace Jint.Native.Function
             }
         }
 
-        public override string Class
-        {
-            get
-            {
-                return "Function";
-            }
-        }
+        public override string Class => "Function";
 
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5.4
@@ -90,5 +92,80 @@ namespace Jint.Native.Function
 
             return v;
         }
+
+        public override IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
+        {
+            if (_prototype != null)
+            {
+                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNamePrototype, _prototype);
+            }
+            if (_length != null)
+            {
+                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameLength, _length);
+            }
+
+            foreach (var entry in base.GetOwnProperties())
+            {
+                yield return entry;
+            }
+        }
+
+        public override IPropertyDescriptor GetOwnProperty(string propertyName)
+        {
+            if (propertyName == PropertyNamePrototype)
+            {
+                return _prototype ?? PropertyDescriptor.Undefined;
+            }
+            if (propertyName == PropertyNameLength)
+            {
+                return _length ?? PropertyDescriptor.Undefined;
+            }
+
+            return base.GetOwnProperty(propertyName);
+        }
+
+        protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
+        {
+            if (propertyName == PropertyNamePrototype)
+            {
+                _prototype = desc;
+            }
+            else if (propertyName == PropertyNameLength)
+            {
+                _length = desc;
+            }
+            else
+            {
+                base.SetOwnProperty(propertyName, desc);
+            }
+        }
+
+        public override bool HasOwnProperty(string propertyName)
+        {
+            if (propertyName == PropertyNamePrototype)
+            {
+                return _prototype != null;
+            }
+            if (propertyName == PropertyNameLength)
+            {
+                return _length != null;
+            }
+
+            return base.HasOwnProperty(propertyName);
+        }
+
+        public override void RemoveOwnProperty(string propertyName)
+        {
+            if (propertyName == PropertyNamePrototype)
+            {
+                _prototype = null;
+            }
+            if (propertyName == PropertyNameLength)
+            {
+                _prototype = null;
+            }
+
+            base.RemoveOwnProperty(propertyName);
+        }
     }
-}
+}

+ 142 - 8
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -1,4 +1,5 @@
-using System.Runtime.CompilerServices;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Jint.Native.Object;
 using Jint.Runtime;
@@ -13,6 +14,10 @@ namespace Jint.Native.Function
     /// </summary>
     public sealed class ScriptFunctionInstance : FunctionInstance, IConstructor
     {
+        private const string PropertyNameName = "name";
+
+        private IPropertyDescriptor _name;
+
         private readonly IFunction _functionDeclaration;
 
         /// <summary>
@@ -27,18 +32,22 @@ namespace Jint.Native.Function
         {
             _functionDeclaration = functionDeclaration;
 
-            Engine = engine;
             Extensible = true;
             Prototype = engine.Function.PrototypeObject;
 
             DefineOwnProperty("length", new AllForbiddenPropertyDescriptor(JsNumber.Create(FormalParameters.Length)), false);
 
-            var proto = engine.Object.Construct(Arguments.Empty);
-            proto.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(this));
+            var proto = new ObjectInstanceWithConstructor(engine, this)
+            {
+                Extensible = true,
+                Prototype = Engine.Object.PrototypeObject
+            };
+
             SetOwnProperty("prototype", new WritablePropertyDescriptor(proto));
+
             if (_functionDeclaration.Id != null)
             {
-                DefineOwnProperty("name", new NullConfigurationPropertyDescriptor(_functionDeclaration.Id.Name), false);
+                _name = new NullConfigurationPropertyDescriptor(_functionDeclaration.Id.Name);
             }
 
             if (strict)
@@ -49,6 +58,61 @@ namespace Jint.Native.Function
             }
         }
 
+        public override IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
+        {
+            if (_name != null)
+            {
+                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameName, _name);
+            }
+
+            foreach (var entry in base.GetOwnProperties())
+            {
+                yield return entry;
+            }
+        }
+
+        public override IPropertyDescriptor GetOwnProperty(string propertyName)
+        {
+            if (propertyName == PropertyNameName)
+            {
+                return _name ?? PropertyDescriptor.Undefined;
+            }
+
+            return base.GetOwnProperty(propertyName);
+        }
+
+        protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
+        {
+            if (propertyName == PropertyNameName)
+            {
+                _name = desc;
+            }
+            else
+            {
+                base.SetOwnProperty(propertyName, desc);
+            }
+        }
+
+        public override bool HasOwnProperty(string propertyName)
+        {
+            if (propertyName == PropertyNameName)
+            {
+                return _name != null;
+            }
+
+            return base.HasOwnProperty(propertyName);
+        }
+
+        public override void RemoveOwnProperty(string propertyName)
+        {
+            if (propertyName == PropertyNameName)
+            {
+                _name = null;
+            }
+
+            base.RemoveOwnProperty(propertyName);
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static string[] GetParameterNames(IFunction functionDeclaration)
         {
@@ -142,9 +206,11 @@ namespace Jint.Native.Function
         public ObjectInstance Construct(JsValue[] arguments)
         {
             var proto = Get("prototype").TryCast<ObjectInstance>();
-            var obj = new ObjectInstance(Engine);
-            obj.Extensible = true;
-            obj.Prototype = proto ?? Engine.Object.PrototypeObject;
+            var obj = new ObjectInstance(Engine)
+            {
+                Extensible = true,
+                Prototype = proto ?? Engine.Object.PrototypeObject
+            };
 
             var result = Call(obj, arguments).TryCast<ObjectInstance>();
             if (result != null)
@@ -154,5 +220,73 @@ namespace Jint.Native.Function
 
             return obj;
         }
+
+        private class ObjectInstanceWithConstructor : ObjectInstance
+        {
+            private const string PropertyNameConstructor = "constructor";
+            private IPropertyDescriptor _constructor;
+
+            public ObjectInstanceWithConstructor(Engine engine, ObjectInstance thisObj) : base(engine)
+            {
+                _constructor = new NonEnumerablePropertyDescriptor(thisObj);
+            }
+
+            public override IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
+            {
+                if (_constructor != null)
+                {
+                    yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameConstructor, _constructor);
+                }
+
+                foreach (var entry in base.GetOwnProperties())
+                {
+                    yield return entry;
+                }
+            }
+
+            public override IPropertyDescriptor GetOwnProperty(string propertyName)
+            {
+                if (propertyName == PropertyNameConstructor)
+                {
+                    return _constructor ?? PropertyDescriptor.Undefined;
+                }
+
+                return base.GetOwnProperty(propertyName);
+            }
+
+            protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
+            {
+                if (propertyName == PropertyNameConstructor)
+                {
+                    _constructor = desc;
+                }
+                else
+                {
+                    base.SetOwnProperty(propertyName, desc);
+                }
+            }
+
+            public override bool HasOwnProperty(string propertyName)
+            {
+                if (propertyName == PropertyNameConstructor)
+                {
+                    return _constructor != null;
+                }
+
+                return base.HasOwnProperty(propertyName);
+            }
+
+            public override void RemoveOwnProperty(string propertyName)
+            {
+                if (propertyName == PropertyNameConstructor)
+                {
+                    _constructor = null;
+                }
+                else
+                {
+                    base.RemoveOwnProperty(propertyName);
+                }
+            }
+        }
     }
 }

+ 0 - 1
Jint/Native/Global/GlobalObject.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Globalization;
 using System.Linq;
-using System.Runtime.CompilerServices;
 using System.Text;
 using Jint.Native.Object;
 using Jint.Native.String;

+ 9 - 132
Jint/Native/Object/ObjectInstance.cs

@@ -18,23 +18,15 @@ namespace Jint.Native.Object
 {
     public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     {
-        private const string PropertyNamePrototype = "prototype";
-        private const string PropertyNameConstructor = "constructor";
-        private const string PropertyNameLength = "length";
-
         private Dictionary<string, IPropertyDescriptor> _intrinsicProperties;
-        private MruPropertyCache2<string, IPropertyDescriptor> _properties;
-
-        private IPropertyDescriptor _prototype;
-        private IPropertyDescriptor _constructor;
-        private IPropertyDescriptor _length;
+        private MruPropertyCache2<IPropertyDescriptor> _properties;
 
         public ObjectInstance(Engine engine)
         {
             Engine = engine;
         }
 
-        public Engine Engine { get; set; }
+        public Engine Engine { get; }
 
         protected bool TryGetIntrinsicValue(JsSymbol symbol, out JsValue value)
         {
@@ -91,21 +83,6 @@ namespace Jint.Native.Object
         {
             EnsureInitialized();
 
-            if (_prototype != null)
-            {
-                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNamePrototype, _prototype);
-            }
-
-            if (_constructor != null)
-            {
-                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameConstructor, _constructor);
-            }
-
-            if (_length != null)
-            {
-                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameLength, _length);
-            }
-
             if (_properties != null)
             {
                 foreach (var pair in _properties.GetEnumerator())
@@ -115,54 +92,18 @@ namespace Jint.Native.Object
             }
         }
 
-        protected void AddProperty(string propertyName, IPropertyDescriptor descriptor)
+        protected virtual void AddProperty(string propertyName, IPropertyDescriptor descriptor)
         {
-            if (propertyName == PropertyNamePrototype)
-            {
-                _prototype = descriptor;
-                return;
-            }
-
-            if (propertyName == PropertyNameConstructor)
-            {
-                _constructor = descriptor;
-                return;
-            }
-
-            if (propertyName == PropertyNameLength)
-            {
-                _length = descriptor;
-                return;
-            }
-
             if (_properties == null)
             {
-                _properties = new MruPropertyCache2<string, IPropertyDescriptor>();
+                _properties = new MruPropertyCache2<IPropertyDescriptor>();
             }
 
             _properties.Add(propertyName, descriptor);
         }
 
-        protected bool TryGetProperty(string propertyName, out IPropertyDescriptor descriptor)
+        protected virtual bool TryGetProperty(string propertyName, out IPropertyDescriptor descriptor)
         {
-            if (propertyName == PropertyNamePrototype)
-            {
-                descriptor = _prototype;
-                return _prototype != null;
-            }
-
-            if (propertyName == PropertyNameConstructor)
-            {
-                descriptor = _constructor;
-                return _constructor != null;
-            }
-
-            if (propertyName == PropertyNameLength)
-            {
-                descriptor = _length;
-                return _length != null;
-            }
-
             if (_properties == null)
             {
                 descriptor = null;
@@ -176,21 +117,6 @@ namespace Jint.Native.Object
         {
             EnsureInitialized();
 
-            if (propertyName == PropertyNamePrototype)
-            {
-                return _prototype != null;
-            }
-
-            if (propertyName == PropertyNameConstructor)
-            {
-                return _constructor != null;
-            }
-
-            if (propertyName == PropertyNameLength)
-            {
-                return _length != null;
-            }
-
             return _properties?.ContainsKey(propertyName) ?? false;
         }
 
@@ -198,21 +124,6 @@ namespace Jint.Native.Object
         {
             EnsureInitialized();
 
-            if (propertyName == PropertyNamePrototype)
-            {
-                _prototype = null;
-            }
-
-            if (propertyName == PropertyNameConstructor)
-            {
-                _constructor = null;
-            }
-
-            if (propertyName == PropertyNameLength)
-            {
-                _length = null;
-            }
-
             _properties?.Remove(propertyName);
         }
 
@@ -241,7 +152,7 @@ namespace Jint.Native.Object
 
             if (getter.IsUndefined())
             {
-                return JsValue.Undefined;
+                return Undefined;
             }
 
             // if getter is not undefined it must be ICallable
@@ -261,21 +172,6 @@ namespace Jint.Native.Object
         {
             EnsureInitialized();
 
-            if (propertyName == PropertyNamePrototype)
-            {
-                return _prototype ?? PropertyDescriptor.Undefined;
-            }
-
-            if (propertyName == PropertyNameConstructor)
-            {
-                return _constructor ?? PropertyDescriptor.Undefined;
-            }
-
-            if (propertyName == PropertyNameLength)
-            {
-                return _length ?? PropertyDescriptor.Undefined;
-            }
-
             if (_properties != null && _properties.TryGetValue(propertyName, out var x))
             {
                 return x;
@@ -288,27 +184,9 @@ namespace Jint.Native.Object
         {
             EnsureInitialized();
 
-            if (propertyName == PropertyNamePrototype)
-            {
-                _prototype = desc;
-                return;
-            }
-
-            if (propertyName == PropertyNameConstructor)
-            {
-                _constructor = desc;
-                return;
-            }
-
-            if (propertyName == PropertyNameLength)
-            {
-                _length = desc;
-                return;
-            }
-
             if (_properties == null)
             {
-                _properties = new MruPropertyCache2<string, IPropertyDescriptor>();
+                _properties = new MruPropertyCache2<IPropertyDescriptor>();
             }
 
             _properties[propertyName] = desc;
@@ -642,6 +520,7 @@ namespace Jint.Native.Object
                                 Configurable = desc.Configurable.HasValue ? desc.Configurable.Value : false
                             };
                         }
+
                         SetOwnProperty(propertyName, propertyDescriptor);
                     }
                     else
@@ -862,9 +741,7 @@ namespace Jint.Native.Object
             return TypeConverter.ToString(this);
         }
 
-        protected uint GetLengthValue() => TypeConverter.ToUint32(_length.Value);
-
-                public override Types Type => Types.Object;
+        public override Types Type => Types.Object;
 
         [Pure]
         public override bool IsArray()

+ 55 - 1
Jint/Native/String/StringInstance.cs

@@ -1,4 +1,5 @@
-using Jint.Native.Object;
+using System.Collections.Generic;
+using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
@@ -7,6 +8,9 @@ namespace Jint.Native.String
 {
     public class StringInstance : ObjectInstance, IPrimitiveInstance
     {
+        private const string PropertyNameLength = "length";
+        private IPropertyDescriptor _length;
+
         public StringInstance(Engine engine)
             : base(engine)
         {
@@ -38,6 +42,11 @@ namespace Jint.Native.String
                 return PropertyDescriptor.Undefined;
             }
 
+            if (propertyName == PropertyNameLength)
+            {
+                return _length ?? PropertyDescriptor.Undefined;
+            }
+
             var desc = base.GetOwnProperty(propertyName);
             if (desc != PropertyDescriptor.Undefined)
             {
@@ -65,5 +74,50 @@ namespace Jint.Native.String
             var resultStr = TypeConverter.ToString(str.AsString()[index]);
             return new EnumerablePropertyDescriptor(resultStr);
         }
+
+        public override IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
+        {
+            if (_length != null)
+            {
+                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameLength, _length);
+            }
+
+            foreach (var entry in base.GetOwnProperties())
+            {
+                yield return entry;
+            }
+        }
+
+        protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
+        {
+            if (propertyName == PropertyNameLength)
+            {
+                _length = desc;
+            }
+            else
+            {
+                base.SetOwnProperty(propertyName, desc);
+            }
+        }
+
+        public override bool HasOwnProperty(string propertyName)
+        {
+            if (propertyName == PropertyNameLength)
+            {
+                return _length != null;
+            }
+
+            return base.HasOwnProperty(propertyName);
+        }
+
+        public override void RemoveOwnProperty(string propertyName)
+        {
+            if (propertyName == PropertyNameLength)
+            {
+                _length = null;
+            }
+
+            base.RemoveOwnProperty(propertyName);
+        }
     }
 }

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

@@ -13,7 +13,7 @@ namespace Jint.Runtime.Environments
         private const string BindingNameArguments = "arguments";
         private Binding _argumentsBinding;
 
-        private readonly MruPropertyCache2<string, Binding> _bindings = new MruPropertyCache2<string, Binding>();
+        private readonly MruPropertyCache2<Binding> _bindings = new MruPropertyCache2<Binding>();
 
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         {

+ 28 - 13
Jint/Runtime/ExpressionIntepreter.cs

@@ -203,11 +203,27 @@ namespace Jint.Runtime
 
         public JsValue EvaluateBinaryExpression(BinaryExpression expression)
         {
-            var leftExpression = EvaluateExpression(expression.Left);
-            JsValue left = _engine.GetValue(leftExpression);
+            JsValue left;
+            if (expression.Left.Type == Nodes.Literal)
+            {
+                left = EvaluateLiteral(expression.Left.As<Literal>());
+            }
+            else
+            {
+                var leftExpression = EvaluateExpression(expression.Left);
+                left = _engine.GetValue(leftExpression);
+            }
 
-            var rightExpression = EvaluateExpression(expression.Right);
-            JsValue right = _engine.GetValue(rightExpression);
+            JsValue right;
+            if (expression.Right.Type == Nodes.Literal)
+            {
+                right = EvaluateLiteral(expression.Right.As<Literal>());
+            }
+            else
+            {
+                var rightExpression = EvaluateExpression(expression.Right);
+                right = _engine.GetValue(rightExpression);
+            }
 
             JsValue value;
 
@@ -451,11 +467,6 @@ namespace Jint.Runtime
                 return true;
             }
 
-            if (typea == Types.None)
-            {
-                return true;
-            }
-
             if (typea == Types.Number)
             {
                 var nx = x.AsNumber();
@@ -486,15 +497,18 @@ namespace Jint.Runtime
 
 			if (typea == Types.Object)
 			{
-				var xw = x.AsObject() as IObjectWrapper;
-
-				if (xw != null)
+			    if (x.AsObject() is IObjectWrapper xw)
 				{
 					var yw = y.AsObject() as IObjectWrapper;
-					return Object.Equals(xw.Target, yw.Target);
+					return Equals(xw.Target, yw.Target);
 				}
 			}
 
+            if (typea == Types.None)
+            {
+                return true;
+            }
+
             return x == y;
         }
 
@@ -614,6 +628,7 @@ namespace Jint.Runtime
             return LexicalEnvironment.GetIdentifierReference(env, identifier.Name, strict);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue EvaluateLiteral(Literal literal)
         {
             switch (literal.TokenType)

+ 34 - 32
Jint/Runtime/MruPropertyCache2.cs

@@ -3,18 +3,17 @@ using System.Runtime.CompilerServices;
 
 namespace Jint.Runtime
 {
-    internal class MruPropertyCache2<TKey, TValue> where TKey : class where TValue : class
+    internal class MruPropertyCache2<TValue> where TValue : class
     {
-        private Dictionary<TKey, TValue> _dictionary;
-        private bool _set;
-        private TKey _key;
+        private Dictionary<string, TValue> _dictionary;
+        private string _key;
         private TValue _value;
 
-        public TValue this[TKey key]
+        public TValue this[string key]
         {
             get
             {
-                if (_set && key.Equals(_key))
+                if (_key != null && _key == key)
                 {
                     return _value;
                 }
@@ -25,7 +24,6 @@ namespace Jint.Runtime
             set
             {
                 EnsureInitialized(key);
-                _set = true;
                 _key = key;
                 _value = value;
 
@@ -40,7 +38,7 @@ namespace Jint.Runtime
         {
             get
             {
-                int count = _set ? 1 : 0;
+                int count = _key != null ? 1 : 0;
                 if (_dictionary != null)
                 {
                     count += _dictionary.Count;
@@ -50,10 +48,9 @@ namespace Jint.Runtime
             }
         }
 
-        public void Add(TKey key, TValue value)
+        public void Add(string key, TValue value)
         {
             EnsureInitialized(key);
-            _set = true;
             _key = key;
             _value = value;
 
@@ -65,16 +62,15 @@ namespace Jint.Runtime
 
         public void Clear()
         {
-            _set = false;
-            _key = default(TKey);
+            _key = default(string);
             _value = null;
 
             _dictionary?.Clear();
         }
 
-        public bool ContainsKey(TKey key)
+        public bool ContainsKey(string key)
         {
-            if (_set && key.Equals(_key))
+            if (_key != null && _key == key)
             {
                 return true;
             }
@@ -82,13 +78,13 @@ namespace Jint.Runtime
             return _dictionary != null && _dictionary.ContainsKey(key);
         }
 
-        public IEnumerable<KeyValuePair<TKey, TValue>> GetEnumerator()
+        public IEnumerable<KeyValuePair<string, TValue>> GetEnumerator()
         {
             if (_dictionary == null)
             {
-                if (_set)
+                if (_key != null)
                 {
-                    yield return new KeyValuePair<TKey, TValue>(_key, _value);
+                    yield return new KeyValuePair<string, TValue>(_key, _value);
                 }
 
                 yield break;
@@ -100,24 +96,23 @@ namespace Jint.Runtime
             }
         }
 
-        public bool Remove(TKey key)
+        public bool Remove(string key)
         {
             bool removed = false;
-            if (_set && key.Equals(_key))
+            if (_key != null && _key == key)
             {
-                _set = false;
-                _key = default(TKey);
+                _key = null;
                 _value = null;
                 removed = true;
             }
 
-            _dictionary?.Remove(key);
+            removed |= _dictionary?.Remove(key) ?? false;
             return removed;
         }
 
-        public bool TryGetValue(TKey key, out TValue value)
+        public bool TryGetValue(string key, out TValue value)
         {
-            if (_set && _key.Equals(key))
+            if (_key != null && _key == key)
             {
                 value = _value;
                 return true;
@@ -129,33 +124,40 @@ namespace Jint.Runtime
                 return false;
             }
 
-            return _dictionary.TryGetValue(key, out value);
+            if (_dictionary.TryGetValue(key, out value))
+            {
+                _key = key;
+                _value = value;
+                return true;
+            }
+
+            return false;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private void EnsureInitialized(TKey key)
+        private void EnsureInitialized(string key)
         {
-            if (_set && !Equals(_key, key))
+            if (_key != null && _key != key)
             {
                 if (_dictionary == null)
                 {
-                    _dictionary = new Dictionary<TKey, TValue>();
+                    _dictionary = new Dictionary<string, TValue>();
                 }
                 _dictionary[_key] = _value;
             }
         }
 
-        public TKey[] GetKeys()
+        public string[] GetKeys()
         {
-            int size = _set ? 1 : 0;
+            int size = _key != null ? 1 : 0;
             if (_dictionary != null)
             {
                 size += _dictionary.Count;
             }
 
-            var keys = new TKey[size];
+            var keys = new string[size];
             int n = 0;
-            if (_set)
+            if (_key != null)
             {
                 keys[n++] = _key;
             }