Przeglądaj źródła

#451 optimize ArrayInstance

* optimize methods and make tweak for better inlining
* use flags enum for PropertyDescriptor
* remove Get and Set backing fields from PropertyDescriptor, separate GetSetPropertyDescriptor
Marko Lahma 7 lat temu
rodzic
commit
573f1a1515

+ 2 - 2
Jint/Native/Argument/ArgumentsInstance.cs

@@ -93,8 +93,8 @@ namespace Jint.Native.Argument
             else
             {
                 var thrower = Engine.Function.ThrowTypeError;
-                self.DefineOwnProperty("caller", new PropertyDescriptor(get: thrower, set: thrower, enumerable: false, configurable: false), false);
-                self.DefineOwnProperty("callee", new PropertyDescriptor(get: thrower, set: thrower, enumerable: false, configurable: false), false);
+                self.DefineOwnProperty("caller", new GetSetPropertyDescriptor(get: thrower, set: thrower, enumerable: false, configurable: false), false);
+                self.DefineOwnProperty("callee", new GetSetPropertyDescriptor(get: thrower, set: thrower, enumerable: false, configurable: false), false);
             }
         }
 

+ 35 - 37
Jint/Native/Array/ArrayInstance.cs

@@ -1,9 +1,11 @@
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
+
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
+
 using PropertyDescriptor = Jint.Runtime.Descriptors.PropertyDescriptor;
 using TypeConverter = Jint.Runtime.TypeConverter;
 
@@ -130,22 +132,7 @@ namespace Jint.Native.Array
                     return false;
                 }
 
-                int count = 0;
-                if (_dense != null)
-                {
-                    for (int i = 0; i < _dense.Length; ++i)
-                    {
-                        if (_dense[i] != null)
-                        {
-                            count++;
-                        }
-                    }
-                }
-                else
-                {
-                    count = _sparse.Count;
-                }
-
+                var count = _dense?.Length ?? _sparse.Count;
                 if (count < oldLen - newLen)
                 {
                     if (_dense != null)
@@ -160,7 +147,7 @@ namespace Jint.Native.Array
                             // is it the index of the array
                             if (keyIndex >= newLen && keyIndex < oldLen)
                             {
-                                var deleteSucceeded = Delete(TypeConverter.ToString(keyIndex), false);
+                                var deleteSucceeded = DeleteAt(keyIndex);
                                 if (!deleteSucceeded)
                                 {
                                     newLenDesc.Value = keyIndex + 1;
@@ -190,7 +177,7 @@ namespace Jint.Native.Array
                         for (var i = 0; i < keysCount; i++)
                         {
                             var keyIndex = keys[i];
-                            
+
                             // is it the index of the array
                             if (keyIndex >= newLen && keyIndex < oldLen)
                             {
@@ -287,7 +274,7 @@ namespace Jint.Native.Array
 
         public uint GetLength()
         {
-            return TypeConverter.ToUint32(_length.Value);
+            return (uint) ((JsNumber) _length.Value)._value;
         }
 
         protected override void AddProperty(string propertyName, IPropertyDescriptor descriptor)
@@ -297,6 +284,7 @@ namespace Jint.Native.Array
                 _length = descriptor;
                 return;
             }
+
             base.AddProperty(propertyName, descriptor);
         }
 
@@ -307,6 +295,7 @@ namespace Jint.Native.Array
                 descriptor = _length;
                 return _length != null;
             }
+
             return base.TryGetProperty(propertyName, out descriptor);
         }
 
@@ -319,7 +308,8 @@ namespace Jint.Native.Array
 
             if (_dense != null)
             {
-                for (var i = 0; i < _dense.Length; i++)
+                var length = System.Math.Min(_dense.Length, GetLength());
+                for (var i = 0; i < length; i++)
                 {
                     if (_dense[i] != null)
                     {
@@ -383,7 +373,7 @@ namespace Jint.Native.Array
             {
                 return index < GetLength()
                        && (_sparse == null || _sparse.ContainsKey(index))
-                       && (_dense == null || _dense[index] != null);
+                       && (_dense == null || (index < _dense.Length && _dense[index] != null));
             }
 
             if (p == PropertyNameLength)
@@ -396,8 +386,7 @@ namespace Jint.Native.Array
 
         public override void RemoveOwnProperty(string p)
         {
-            uint index;
-            if (IsArrayIndex(p, out index))
+            if (IsArrayIndex(p, out var index))
             {
                 DeleteAt(index);
             }
@@ -515,19 +504,22 @@ namespace Jint.Native.Array
             return false;
         }
 
-        internal void DeleteAt(uint index)
+        internal bool DeleteAt(uint index)
         {
             if (_dense != null)
             {
                 if (index < _dense.Length)
                 {
                     _dense[index] = null;
+                    return true;
                 }
             }
             else
             {
-                _sparse.Remove(index);
+                return _sparse.Remove(index);
             }
+
+            return false;
         }
 
         private bool TryGetDescriptor(uint index, out IPropertyDescriptor descriptor)
@@ -547,6 +539,7 @@ namespace Jint.Native.Array
             return _sparse.TryGetValue(index, out descriptor);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal void WriteArrayValue(uint index, IPropertyDescriptor desc)
         {
             // calculate eagerly so we know if we outgrow
@@ -572,23 +565,28 @@ namespace Jint.Native.Array
             {
                 if (_dense != null)
                 {
-                    _sparse = new Dictionary<uint, IPropertyDescriptor>(_dense.Length <= 1024 ? _dense.Length : 0);
-                    // need to move data
-                    for (uint i = 0; i < _dense.Length; ++i)
-                    {
-                        if (_dense[i] != null)
-                        {
-                            _sparse[i] = _dense[i];
-                        }
-                    }
-
-                    _dense = null;
+                    ConvertToSparse();
                 }
-
                 _sparse[index] = desc;
             }
         }
 
+        private void ConvertToSparse()
+        {
+            _sparse = new Dictionary<uint, IPropertyDescriptor>(_dense.Length <= 1024 ? _dense.Length : 0);
+            // need to move data
+            for (uint i = 0; i < _dense.Length; ++i)
+            {
+                if (_dense[i] != null)
+                {
+                    _sparse[i] = _dense[i];
+                }
+            }
+
+            _dense = null;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal void EnsureCapacity(uint capacity)
         {
             if (capacity > _dense.Length)

+ 2 - 3
Jint/Native/Function/FunctionPrototype.cs

@@ -1,6 +1,5 @@
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
@@ -64,8 +63,8 @@ namespace Jint.Native.Function
 
 
             var thrower = Engine.Function.ThrowTypeError;
-            f.DefineOwnProperty("caller", new PropertyDescriptor(thrower, thrower, false, false), false);
-            f.DefineOwnProperty("arguments", new PropertyDescriptor(thrower, thrower, false, false), false);
+            f.DefineOwnProperty("caller", new GetSetPropertyDescriptor(thrower, thrower, false, false), false);
+            f.DefineOwnProperty("arguments", new GetSetPropertyDescriptor(thrower, thrower, false, false), false);
 
 
             return f;

+ 2 - 2
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -55,8 +55,8 @@ namespace Jint.Native.Function
             if (strict)
             {
                 var thrower = engine.Function.ThrowTypeError;
-                DefineOwnProperty("caller", new PropertyDescriptor(thrower, thrower, false, false), false);
-                DefineOwnProperty("arguments", new PropertyDescriptor(thrower, thrower, false, false), false);
+                DefineOwnProperty("caller", new GetSetPropertyDescriptor(thrower, thrower, false, false), false);
+                DefineOwnProperty("arguments", new GetSetPropertyDescriptor(thrower, thrower, false, false), false);
             }
         }
 

+ 1 - 1
Jint/Native/JsNumber.cs

@@ -6,7 +6,7 @@ namespace Jint.Native
 {
     public sealed class JsNumber : JsValue, IEquatable<JsNumber>
     {
-        private readonly double _value;
+        internal readonly double _value;
 
         // how many decimals to check when determining if double is actually an int
         private const double DoubleIsIntegerTolerance = double.Epsilon * 100;

+ 24 - 22
Jint/Native/JsValue.cs

@@ -205,10 +205,11 @@ namespace Jint.Native
                 return jsValue;
             }
 
-            var objectConvertersCount = engine.Options._ObjectConverters.Count;
-            for (var i = 0; i < objectConvertersCount; i++)
+            var converters = engine.Options._ObjectConverters;
+            var convertersCount = converters.Count;
+            for (var i = 0; i < convertersCount; i++)
             {
-                var converter = engine.Options._ObjectConverters[i];
+                var converter = converters[i];
                 if (converter.TryConvert(value, out var result))
                 {
                     return result;
@@ -233,25 +234,6 @@ namespace Jint.Native
 
             if (value is System.Array a)
             {
-                JsValue Convert(Engine e, object v)
-                {
-                    var array = (System.Array) v;
-                    var arrayLength = (uint) array.Length;
-
-                    var jsArray = new ArrayInstance(e, arrayLength);
-                    jsArray.Prototype = e.Array.PrototypeObject;
-                    jsArray.Extensible = true;
-                    
-                    for (uint i = 0; i < arrayLength; ++i)
-                    {
-                        var jsItem = FromObject(e, array.GetValue(i));
-                        jsArray.WriteArrayValue(i, new ConfigurableEnumerableWritablePropertyDescriptor(jsItem));
-                    }
-                    jsArray.SetOwnProperty("length", new WritablePropertyDescriptor(arrayLength));
-
-                    return jsArray;
-                }
-
                 // racy, we don't care, worst case we'll catch up later
                 Interlocked.CompareExchange(ref Engine.TypeMappers, new Dictionary<Type, Func<Engine, object, JsValue>>(typeMappers)
                 {
@@ -275,6 +257,26 @@ namespace Jint.Native
             return new ObjectWrapper(engine, value);
         }
 
+        private static JsValue Convert(Engine e, object v)
+        {
+            var array = (System.Array) v;
+            var arrayLength = (uint) array.Length;
+
+            var jsArray = new ArrayInstance(e, arrayLength);
+            jsArray.Prototype = e.Array.PrototypeObject;
+            jsArray.Extensible = true;
+
+            for (uint i = 0; i < arrayLength; ++i)
+            {
+                var jsItem = FromObject(e, array.GetValue(i));
+                jsArray.WriteArrayValue(i, new ConfigurableEnumerableWritablePropertyDescriptor(jsItem));
+            }
+
+            jsArray.SetOwnProperty("length", new WritablePropertyDescriptor(arrayLength));
+
+            return jsArray;
+        }
+
         /// <summary>
         /// Converts a <see cref="JsValue"/> to its underlying CLR value.
         /// </summary>

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

@@ -352,9 +352,9 @@ namespace Jint.Native.Object
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            foreach (var p in o.GetOwnProperties().Select(x => x.Key))
+            foreach (var pair in o.GetOwnProperties())
             {
-                var desc = o.GetOwnProperty(p);
+                var desc = pair.Value;
                 if (desc.IsDataDescriptor())
                 {
                     if (desc.Writable.HasValue && desc.Writable.Value)

+ 8 - 10
Jint/Native/Object/ObjectInstance.cs

@@ -532,12 +532,10 @@ namespace Jint.Native.Object
                     }
                     else
                     {
-                        SetOwnProperty(propertyName, new PropertyDescriptor(desc)
+                        SetOwnProperty(propertyName, new GetSetPropertyDescriptor(desc)
                         {
-                            Get = desc.Get,
-                            Set = desc.Set,
-                            Enumerable = desc.Enumerable.HasValue ? desc.Enumerable : false,
-                            Configurable = desc.Configurable.HasValue ? desc.Configurable : false,
+                            Enumerable = desc.Enumerable ?? false,
+                            Configurable = desc.Configurable ?? false,
                         });
                     }
                 }
@@ -608,7 +606,7 @@ namespace Jint.Native.Object
 
                     if (current.IsDataDescriptor())
                     {
-                        SetOwnProperty(propertyName, current = new PropertyDescriptor(
+                        SetOwnProperty(propertyName, current = new GetSetPropertyDescriptor(
                             get: JsValue.Undefined,
                             set: JsValue.Undefined,
                             enumerable: current.Enumerable,
@@ -698,14 +696,14 @@ namespace Jint.Native.Object
 
             if (desc.Get != null)
             {
-                current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
-                mutable.Get = desc.Get;
+                current = mutable = new GetSetPropertyDescriptor(current);
+                ((GetSetPropertyDescriptor) mutable).SetGet(desc.Get);
             }
 
             if (desc.Set != null)
             {
-                mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
-                mutable.Set = desc.Set;
+                current = mutable = new GetSetPropertyDescriptor(current);
+                ((GetSetPropertyDescriptor) mutable).SetSet(desc.Set);
             }
 
             if (mutable != null)

+ 84 - 40
Jint/Runtime/Descriptors/PropertyDescriptor.cs

@@ -1,4 +1,6 @@
-using Jint.Native;
+using System;
+using System.Runtime.CompilerServices;
+using Jint.Native;
 using Jint.Native.Object;
 using Jint.Runtime.Descriptors.Specialized;
 
@@ -6,8 +8,21 @@ namespace Jint.Runtime.Descriptors
 {
     public class PropertyDescriptor : IPropertyDescriptor
     {
+        [Flags]
+        private enum PropertyFlag
+        {
+            Enumerable = 1,
+            EnumerableSet = 2,
+            Writable = 4,
+            WritableSet = 8,
+            Configurable = 16,
+            ConfigurableSet = 32
+        }
+
         public static readonly IPropertyDescriptor Undefined = new PropertyDescriptor();
 
+        private PropertyFlag _flags;
+
         protected PropertyDescriptor()
         {
         }
@@ -15,54 +30,76 @@ namespace Jint.Runtime.Descriptors
         public PropertyDescriptor(JsValue value, bool? writable, bool? enumerable, bool? configurable)
         {
             Value = value;
+            Writable = writable;
+            Enumerable = enumerable;
+            Configurable = configurable;
+        }
 
-            if (writable.HasValue)
-            {
-                Writable = writable.Value;
-            }
+        public PropertyDescriptor(IPropertyDescriptor descriptor)
+        {
+            Value = descriptor.Value;
+            Enumerable = descriptor.Enumerable;
+            Configurable = descriptor.Configurable;
+            Writable = descriptor.Writable;
+        }
 
-            if (enumerable.HasValue)
-            {
-                Enumerable = enumerable.Value;
-            }
+        public virtual JsValue Get => null;
+        public virtual JsValue Set => null;
 
-            if (configurable.HasValue)
+        public bool? Enumerable
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => (_flags & PropertyFlag.EnumerableSet) != 0 ? (_flags & PropertyFlag.Enumerable) != 0 : (bool?) null;
+            set
             {
-                Configurable = configurable.Value;
+                _flags &= ~(PropertyFlag.EnumerableSet | PropertyFlag.Enumerable);
+                if (value != null)
+                {
+                    _flags |= PropertyFlag.EnumerableSet;
+                    if (value.Value)
+                    {
+                        _flags |= PropertyFlag.Enumerable;
+                    }
+                }
             }
         }
 
-        public PropertyDescriptor(JsValue get, JsValue set, bool? enumerable = null, bool? configurable = null)
+        public bool? Writable
         {
-            Get = get;
-            Set = set;
-
-            if (enumerable.HasValue)
-            {
-                Enumerable = enumerable.Value;
-            }
-
-            if (configurable.HasValue)
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => (_flags & PropertyFlag.WritableSet) != 0 ? (_flags & PropertyFlag.Writable) != 0 : (bool?) null;
+            set
             {
-                Configurable = configurable.Value;
+                _flags &= ~(PropertyFlag.WritableSet | PropertyFlag.Writable);
+                if (value != null)
+                {
+                    _flags |= PropertyFlag.WritableSet;
+                    if (value.Value)
+                    {
+                        _flags |= PropertyFlag.Writable;
+                    }
+                }
             }
         }
 
-        public PropertyDescriptor(IPropertyDescriptor descriptor)
+        public bool? Configurable
         {
-            Get = descriptor.Get;
-            Set = descriptor.Set;
-            Value = descriptor.Value;
-            Enumerable = descriptor.Enumerable;
-            Configurable = descriptor.Configurable;
-            Writable = descriptor.Writable;
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => (_flags & PropertyFlag.ConfigurableSet) != 0 ? (_flags & PropertyFlag.Configurable) != 0 : (bool?) null;
+            set
+            {
+                _flags &= ~(PropertyFlag.ConfigurableSet | PropertyFlag.Configurable);
+                if (value != null)
+                {
+                    _flags |= PropertyFlag.ConfigurableSet;
+                    if (value.Value)
+                    {
+                        _flags |= PropertyFlag.Configurable;
+                    }
+                }
+            }
         }
 
-        public JsValue Get { get; set; }
-        public JsValue Set { get; set; }
-        public bool? Enumerable { get; set; }
-        public bool? Writable { get; set; }
-        public bool? Configurable { get; set; }
         public virtual JsValue Value { get; set; }
 
         public static PropertyDescriptor ToPropertyDescriptor(Engine engine, JsValue o)
@@ -73,13 +110,18 @@ namespace Jint.Runtime.Descriptors
                 throw new JavaScriptException(engine.TypeError);
             }
 
+            var hasGetProperty = obj.HasProperty("get");
+            var hasSetProperty = obj.HasProperty("set");
+            
             if ((obj.HasProperty("value") || obj.HasProperty("writable")) &&
-                (obj.HasProperty("get") || obj.HasProperty("set")))
+                (hasGetProperty || hasSetProperty))
             {
                 throw new JavaScriptException(engine.TypeError);
             }
 
-            var desc = new PropertyDescriptor();
+            var desc = hasGetProperty || hasSetProperty
+                ? new GetSetPropertyDescriptor(null, null, null, null)
+                : new PropertyDescriptor();
 
             if (obj.HasProperty("enumerable"))
             {
@@ -102,24 +144,26 @@ namespace Jint.Runtime.Descriptors
                 desc.Writable = TypeConverter.ToBoolean(obj.Get("writable"));
             }
 
-            if (obj.HasProperty("get"))
+            if (hasGetProperty)
             {
                 var getter = obj.Get("get");
                 if (getter != JsValue.Undefined && getter.TryCast<ICallable>() == null)
                 {
                     throw new JavaScriptException(engine.TypeError);
                 }
-                desc.Get = getter;
+
+                ((GetSetPropertyDescriptor) desc).SetGet(getter);
             }
 
-            if (obj.HasProperty("set"))
+            if (hasSetProperty)
             {
                 var setter = obj.Get("set");
                 if (setter != Native.Undefined.Instance && setter.TryCast<ICallable>() == null)
                 {
                     throw new JavaScriptException(engine.TypeError);
                 }
-                desc.Set = setter;
+
+                ((GetSetPropertyDescriptor) desc).SetSet(setter);
             }
 
             if (desc.Get != null || desc.Get != null)

+ 4 - 9
Jint/Runtime/Descriptors/Specialized/ClrAccessDescriptor.cs

@@ -4,7 +4,7 @@ using Jint.Runtime.Interop;
 
 namespace Jint.Runtime.Descriptors.Specialized
 {
-    internal sealed class ClrAccessDescriptor : IPropertyDescriptor
+    internal sealed class ClrAccessDescriptor : PropertyDescriptor
     {
         private readonly EnvironmentRecord _env;
         private readonly Engine _engine;
@@ -17,20 +17,15 @@ namespace Jint.Runtime.Descriptors.Specialized
             EnvironmentRecord env,
             Engine engine,
             string name)
+            : base(value: null, writable: null, enumerable: null, configurable: true)
         {
             _env = env;
             _engine = engine;
             _name = name;
         }
 
-        public JsValue Get => _get = _get ?? new GetterFunctionInstance(_engine, DoGet);
-        public JsValue Set => _set = _set ?? new SetterFunctionInstance(_engine, DoSet);
-
-        public bool? Enumerable => null;
-        public bool? Writable => null;
-        public bool? Configurable => true;
-
-        public JsValue Value { get; set; }
+        public override JsValue Get => _get = _get ?? new GetterFunctionInstance(_engine, DoGet);
+        public override JsValue Set => _set = _set ?? new SetterFunctionInstance(_engine, DoSet);
 
         private JsValue DoGet(JsValue n)
         {

+ 37 - 0
Jint/Runtime/Descriptors/Specialized/GetSetPropertyDescriptor.cs

@@ -0,0 +1,37 @@
+using Jint.Native;
+
+namespace Jint.Runtime.Descriptors.Specialized
+{
+    public sealed class GetSetPropertyDescriptor : PropertyDescriptor
+    {
+        private JsValue _get;
+        private JsValue _set;
+
+        public GetSetPropertyDescriptor(JsValue get, JsValue set, bool? enumerable = null, bool? configurable = null)
+        {
+            _get = get;
+            _set = set;
+            Enumerable = enumerable;
+            Configurable = configurable;
+        }
+
+        public GetSetPropertyDescriptor(IPropertyDescriptor descriptor) : base(descriptor)
+        {
+            _get = descriptor.Get;
+            _set = descriptor.Set;
+        }
+
+        public override JsValue Get => _get;
+        public override JsValue Set => _set;
+
+        internal void SetGet(JsValue getter)
+        {
+            _get = getter;
+        }
+        
+        internal void SetSet(JsValue setter)
+        {
+            _set = setter;
+        }
+    }
+}

+ 2 - 2
Jint/Runtime/ExpressionIntepreter.cs

@@ -694,7 +694,7 @@ namespace Jint.Runtime
                             );
                         }
 
-                        propDesc = new PropertyDescriptor(get: get, set: null, enumerable: true, configurable: true);
+                        propDesc = new GetSetPropertyDescriptor(get: get, set: null, enumerable: true, configurable: true);
                         break;
 
                     case PropertyKind.Set:
@@ -716,7 +716,7 @@ namespace Jint.Runtime
                             );
                         }
 
-                        propDesc = new PropertyDescriptor(get: null, set: set, enumerable: true, configurable: true);
+                        propDesc = new GetSetPropertyDescriptor(get: null, set: set, enumerable: true, configurable: true);
                         break;
 
                     default: