Browse Source

#451 optimize DeclarativeEnvironmentRecord (#458)

* use MruPropertyCache2 and arguments as stored field
* add missing implicit JsValue operators for uint and char
* simplify often called JsValue Is* As*
Marko Lahma 7 years ago
parent
commit
761a0a469c

+ 1 - 1
Jint.Benchmark/UncacheableExpressionsBenchmark.cs

@@ -23,7 +23,7 @@ namespace Jint.Benchmark
         {
             public Config()
             {
-                Add(Job.MediumRun);
+                Add(Job.ShortRun);
                 Add(MemoryDiagnoser.Default);
             }
         }

+ 16 - 6
Jint/Native/JsValue.cs

@@ -142,19 +142,19 @@ namespace Jint.Native
         [Pure]
         public bool IsArray()
         {
-            return IsObject() && AsObject() is ArrayInstance;
+            return _type == Types.Object && _object is ArrayInstance;
         }
 
         [Pure]
         public bool IsDate()
         {
-            return IsObject() && AsObject() is DateInstance;
+            return _type == Types.Object && _object is DateInstance;
         }
 
         [Pure]
         public bool IsRegExp()
         {
-            return IsObject() && AsObject() is RegExpInstance;
+            return _type == Types.Object && _object is RegExpInstance;
         }
 
         [Pure]
@@ -285,7 +285,7 @@ namespace Jint.Native
 
         public bool Is<T>()
         {
-            return IsObject() && AsObject() is T;
+            return _type == Types.Object && _object is T;
         }
 
         public T As<T>() where T : ObjectInstance
@@ -732,12 +732,22 @@ namespace Jint.Native
             return !a.Equals(b);
         }
 
-        public static implicit operator JsValue(int value)
+        static public implicit operator JsValue(char value)
+        {
+            return FromChar(value);
+        }
+
+        static public implicit operator JsValue(int value)
+        {
+            return FromInt(value);
+        }
+
+        static public implicit operator JsValue(uint value)
         {
             return FromInt(value);
         }
 
-        public static implicit operator JsValue(double value)
+        static public implicit operator JsValue(double value)
         {
             if (value < 0 || value >= _doubleToJsValue.Count || !_doubleToJsValue.TryGetValue(value, out var jsValue))
             {

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

@@ -733,7 +733,7 @@ namespace Jint.Native.String
             {
                 return double.NaN;
             }
-            return s[position];
+            return (double) s[position];
         }
 
         private JsValue CharAt(JsValue thisObj, JsValue[] arguments)

+ 63 - 22
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -1,6 +1,4 @@
-using System.Collections.Generic;
-using System.Linq;
-using Jint.Native;
+using Jint.Native;
 
 namespace Jint.Runtime.Environments
 {
@@ -11,7 +9,11 @@ namespace Jint.Runtime.Environments
     public sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
     {
         private readonly Engine _engine;
-        private readonly Dictionary<string, Binding> _bindings = new Dictionary<string, Binding>();
+
+        private const string BindingNameArguments = "arguments";
+        private Binding _argumentsBinding;
+
+        private readonly MruPropertyCache2<string, Binding> _bindings = new MruPropertyCache2<string, Binding>();
 
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         {
@@ -20,22 +22,36 @@ namespace Jint.Runtime.Environments
 
         public override bool HasBinding(string name)
         {
+            if (name == BindingNameArguments)
+            {
+                return _argumentsBinding != null;
+            }
+
             return _bindings.ContainsKey(name);
         }
 
         public override void CreateMutableBinding(string name, bool canBeDeleted = false)
         {
-            _bindings.Add(name, new Binding
-                {
-                    Value = Undefined.Instance,
-                    CanBeDeleted =  canBeDeleted,
-                    Mutable = true
-                });
+            var binding = new Binding
+            {
+                Value = Undefined.Instance,
+                CanBeDeleted =  canBeDeleted,
+                Mutable = true
+            };
+
+            if (name == BindingNameArguments)
+            {
+                _argumentsBinding = binding;
+            }
+            else
+            {
+                _bindings.Add(name, binding);
+            }
         }
 
         public override void SetMutableBinding(string name, JsValue value, bool strict)
         {
-            var binding = _bindings[name];
+            var binding = name == BindingNameArguments ? _argumentsBinding : _bindings[name];
             if (binding.Mutable)
             {
                 binding.Value = value;
@@ -51,7 +67,7 @@ namespace Jint.Runtime.Environments
 
         public override JsValue GetBindingValue(string name, bool strict)
         {
-            var binding = _bindings[name];
+            var binding = name == BindingNameArguments ? _argumentsBinding : _bindings[name];
 
             if (!binding.Mutable && binding.Value == Undefined.Instance)
             {
@@ -69,7 +85,16 @@ namespace Jint.Runtime.Environments
         public override bool DeleteBinding(string name)
         {
             Binding binding;
-            if (!_bindings.TryGetValue(name, out binding))
+            if (name == BindingNameArguments)
+            {
+                binding = _argumentsBinding;
+            }
+            else
+            {
+                _bindings.TryGetValue(name, out binding);
+            }
+
+            if (binding == null)
             {
                 return true;
             }
@@ -79,7 +104,14 @@ namespace Jint.Runtime.Environments
                 return false;
             }
 
-            _bindings.Remove(name);
+            if (name == BindingNameArguments)
+            {
+                _argumentsBinding = null;
+            }
+            else
+            {
+                _bindings.Remove(name);
+            }
 
             return true;
         }
@@ -95,12 +127,21 @@ namespace Jint.Runtime.Environments
         /// <param name="name">The identifier of the binding.</param>
         public void CreateImmutableBinding(string name)
         {
-            _bindings.Add(name, new Binding
-                {
-                    Value = Undefined.Instance,
-                    Mutable = false,
-                    CanBeDeleted = false
-                });
+            var binding = new Binding
+            {
+                Value = Undefined.Instance,
+                Mutable = false,
+                CanBeDeleted = false
+            };
+
+            if (name == BindingNameArguments)
+            {
+                _argumentsBinding = binding;
+            }
+            else
+            {
+                _bindings.Add(name, binding);
+            }
         }
 
         /// <summary>
@@ -110,7 +151,7 @@ namespace Jint.Runtime.Environments
         /// <param name="value">The value of the binding.</param>
         public void InitializeImmutableBinding(string name, JsValue value)
         {
-            var binding = _bindings[name];
+            var binding = name == BindingNameArguments ? _argumentsBinding : _bindings[name];
             binding.Value = value;
         }
 
@@ -120,7 +161,7 @@ namespace Jint.Runtime.Environments
         /// <returns>The array of all defined bindings</returns>
         public override string[] GetAllBindingNames()
         {
-            return _bindings.Keys.ToArray();
+            return _bindings.GetKeys();
         }
     }
 }

+ 28 - 2
Jint/Runtime/MruPropertyCache2.cs

@@ -40,12 +40,13 @@ namespace Jint.Runtime
         {
             get
             {
+                int count = _set ? 1 : 0;
                 if (_dictionary != null)
                 {
-                    return _dictionary.Count;
+                    count += _dictionary.Count;
                 }
 
-                return _set ? 1 : 0;
+                return count;
             }
         }
 
@@ -143,5 +144,30 @@ namespace Jint.Runtime
                 _dictionary[_key] = _value;
             }
         }
+
+        public TKey[] GetKeys()
+        {
+            int size = _set ? 1 : 0;
+            if (_dictionary != null)
+            {
+                size += _dictionary.Count;
+            }
+
+            var keys = new TKey[size];
+            int n = 0;
+            if (_set)
+            {
+                keys[n++] = _key;
+            }
+            if (_dictionary != null)
+            {
+                foreach (var key in _dictionary.Keys)
+                {
+                    keys[n++] = key;
+                }
+            }
+
+            return keys;
+        }
     }
 }

+ 3 - 5
Jint/Runtime/StatementInterpreter.cs

@@ -365,7 +365,7 @@ namespace Jint.Runtime
             return r;
         }
 
-        public Completion ExecuteSwitchBlock(IEnumerable<SwitchCase> switchBlock, JsValue input)
+        public Completion ExecuteSwitchBlock(List<SwitchCase> switchBlock, JsValue input)
         {
             JsValue v = Undefined.Instance;
             SwitchCase defaultCase = null;
@@ -413,17 +413,15 @@ namespace Jint.Runtime
             return new Completion(Completion.Normal, v, null);
         }
 
-        public Completion ExecuteStatementList(IEnumerable<StatementListItem> statementList)
+        public Completion ExecuteStatementList(List<StatementListItem> statementList)
         {
             var c = Completion.Empty;
             Completion sl = c;
             Statement s = null;
 
-            var list = (List<StatementListItem>) statementList;
-
             try
             {
-                foreach (var statement in list)
+                foreach (var statement in statementList)
                 {
                     s = statement.As<Statement>();
                     c = ExecuteStatement(s);