Browse Source

Refactoring ObjectInstance to support specialization

ArrayInstance can now have its custom data structure.
Sebastien Ros 10 years ago
parent
commit
3e95f2d81a

+ 138 - 19
Jint/Native/Array/ArrayInstance.cs

@@ -1,4 +1,5 @@
-using System.Linq;
+using System.Collections.Generic;
+using System.Linq;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
@@ -8,7 +9,9 @@ namespace Jint.Native.Array
     public class ArrayInstance : ObjectInstance
     {
         private readonly Engine _engine;
- 
+        private IDictionary<uint, PropertyDescriptor> _array = new MruPropertyCache2<uint, PropertyDescriptor>();
+        private PropertyDescriptor _length;
+
         public ArrayInstance(Engine engine) : base(engine)
         {
             _engine = engine;
@@ -64,7 +67,9 @@ namespace Jint.Native.Array
         public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
         {
             var oldLenDesc = GetOwnProperty("length");
-            var oldLen = TypeConverter.ToNumber(oldLenDesc.Value.Value);
+            var oldLen = (uint)TypeConverter.ToNumber(oldLenDesc.Value.Value);
+            uint index;
+
             if (propertyName == "length")
             {
                 if (!desc.Value.HasValue)
@@ -82,7 +87,7 @@ namespace Jint.Native.Array
                 newLenDesc.Value = newLen;
                 if (newLen >= oldLen)
                 {
-                    return base.DefineOwnProperty("length", newLenDesc, throwOnError);
+                    return base.DefineOwnProperty("length", _length = newLenDesc, throwOnError);
                 }
                 if (!oldLenDesc.Writable.Value)
                 {
@@ -103,33 +108,35 @@ namespace Jint.Native.Array
                     newWritable = false;
                     newLenDesc.Writable = true;
                 }
-                
-                var succeeded = base.DefineOwnProperty("length", newLenDesc, throwOnError);
+
+                var succeeded = base.DefineOwnProperty("length", _length = newLenDesc, throwOnError);
                 if (!succeeded)
                 {
                     return false;
                 }
+
                 // in the case of sparse arrays, treat each concrete element instead of
                 // iterating over all indexes
 
-                if (Properties.Count < oldLen - newLen)
+                if (_array.Count < oldLen - newLen)
                 {
-                    var keys = Properties.Keys.ToArray();
+                    var keys = _array.Keys.ToArray();
                     foreach (var key in keys)
                     {
-                        uint index;
+                        uint keyIndex;
                         // is it the index of the array
-                        if (uint.TryParse(key, out index) && index >= newLen && index < oldLen)
+                        if (IsArrayIndex(key, out keyIndex) && keyIndex >= newLen && keyIndex < oldLen)
                         {
-                            var deleteSucceeded = Delete(key, false);
+                            var deleteSucceeded = Delete(key.ToString(), false);
                             if (!deleteSucceeded)
                             {
-                                newLenDesc.Value = new JsValue(index + 1);
+                                newLenDesc.Value = new JsValue(keyIndex + 1);
                                 if (!newWritable)
                                 {
                                     newLenDesc.Writable = false;
                                 }
-                                base.DefineOwnProperty("length", newLenDesc, false);
+                                base.DefineOwnProperty("length", _length = newLenDesc, false);
+
                                 if (throwOnError)
                                 {
                                     throw new JavaScriptException(_engine.TypeError);
@@ -154,7 +161,8 @@ namespace Jint.Native.Array
                             {
                                 newLenDesc.Writable = false;
                             }
-                            base.DefineOwnProperty("length", newLenDesc, false);
+                            base.DefineOwnProperty("length", _length = newLenDesc, false);
+
                             if (throwOnError)
                             {
                                 throw new JavaScriptException(_engine.TypeError);
@@ -170,9 +178,8 @@ namespace Jint.Native.Array
                 }
                 return true;
             }
-            else if (IsArrayIndex(propertyName))
+            else if (IsArrayIndex(propertyName, out index))
             {
-                var index = TypeConverter.ToUint32(propertyName);
                 if (index >= oldLen && !oldLenDesc.Writable.Value)
                 {
                     if (throwOnError)
@@ -195,7 +202,7 @@ namespace Jint.Native.Array
                 if (index >= oldLen)
                 {
                     oldLenDesc.Value = index + 1;
-                    base.DefineOwnProperty("length", oldLenDesc, false);
+                    base.DefineOwnProperty("length", _length = oldLenDesc, false);
                 }
                 return true;
             }
@@ -203,10 +210,122 @@ namespace Jint.Native.Array
             return base.DefineOwnProperty(propertyName, desc, throwOnError);
         }
 
-        public static bool IsArrayIndex(JsValue p)
+        private uint GetLength()
+        {
+            return TypeConverter.ToUint32(_length.Value.Value);
+        }
+
+        public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
+        {
+            foreach(var entry in _array)
+            {
+                yield return new KeyValuePair<string, PropertyDescriptor>(entry.Key.ToString(), entry.Value);
+            }
+
+            foreach(var entry in base.GetOwnProperties())
+            {
+                yield return entry;
+            }
+        }
+
+        public override PropertyDescriptor GetOwnProperty(string propertyName)
         {
-            return TypeConverter.ToString(TypeConverter.ToUint32(p)) == TypeConverter.ToString(p) && TypeConverter.ToUint32(p) != uint.MaxValue;
+            uint index;
+            if (IsArrayIndex(propertyName, out index))
+            {
+                PropertyDescriptor result;
+                if (_array.TryGetValue(index, out result))
+                {
+                    return result;
+                }
+                else
+                {
+                    return PropertyDescriptor.Undefined;
+                } 
+            }
+
+            return base.GetOwnProperty(propertyName);
+        }
+
+        protected override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
+        {
+            uint index;
+            if (IsArrayIndex(propertyName, out index))
+            {
+                _array[index] = desc;
+            }
+            else
+            {
+                if(propertyName == "length")
+                {
+                    _length = desc;
+                }
+
+                base.SetOwnProperty(propertyName, desc);
+            }            
         }
 
+        public override bool HasOwnProperty(string p)
+        {
+            uint index;
+            if (IsArrayIndex(p, out index))
+            {
+                return index < GetLength() && _array.ContainsKey(index);
+            }
+
+            return base.HasOwnProperty(p);
+        }
+
+        public override void RemoveOwnProperty(string p)
+        {
+            uint index;
+            if(IsArrayIndex(p, out index))
+            {
+                _array.Remove(index);
+            }
+
+            base.RemoveOwnProperty(p);
+        }
+
+        public static bool IsArrayIndex(JsValue p, out uint index)
+        {
+            index = ParseArrayIndex(TypeConverter.ToString(p));
+
+            return index != uint.MaxValue;
+
+            // 15.4 - Use an optimized version of the specification
+            // return TypeConverter.ToString(index) == TypeConverter.ToString(p) && index != uint.MaxValue;
+        }
+
+        internal static uint ParseArrayIndex(string p)
+        {
+            int d = p[0] - '0';
+
+            if (d < 0 || d > 9)
+            {
+                return uint.MaxValue;
+            }
+
+            ulong result = (uint)d;
+
+            for (int i = 1; i < p.Length; i++)
+            {
+                d = p[i] - '0';
+
+                if (d < 0 || d > 9)
+                {
+                    return uint.MaxValue;
+                }
+
+                result = result * 10 + (uint)d;
+                
+                if (result >= uint.MaxValue)
+                {
+                    return uint.MaxValue;
+                }
+            }
+
+            return (uint)result;
+        }
     }
 }

+ 1 - 1
Jint/Native/JsValue.cs

@@ -516,7 +516,7 @@ namespace Jint.Native
                             IDictionary<string, object> o = new ExpandoObject();
 #endif
 
-                            foreach (var p in _object.Properties)
+                            foreach (var p in _object.GetOwnProperties())
                             {
                                 if (!p.Value.Enumerable.HasValue || p.Value.Enumerable.Value == false)
                                 {

+ 2 - 2
Jint/Native/Json/JsonSerializer.cs

@@ -48,7 +48,7 @@ namespace Jint.Native.Json
                         _propertyList = new List<string>();
                     }
 
-                    foreach (var property in replacerObj.Properties.Values)
+                    foreach (var property in replacerObj.GetOwnProperties().Select(x => x.Value))
                     {
                         JsValue v = _engine.GetValue(property);
                         string item = null;
@@ -316,7 +316,7 @@ namespace Jint.Native.Json
             var stepback = _indent;
             _indent += _gap;
             
-            var k = _propertyList ?? value.Properties
+            var k = _propertyList ?? value.GetOwnProperties()
                 .Where(x => x.Value.Enumerable.HasValue && x.Value.Enumerable.Value == true)
                 .Select(x => x.Key)
                 .ToList();

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

@@ -153,7 +153,7 @@ namespace Jint.Native.Object
                 }  
             }
 
-            foreach (var p in o.Properties)
+            foreach (var p in o.GetOwnProperties())
             {
                 array.DefineOwnProperty(n.ToString(), new PropertyDescriptor(p.Key, true, true, true), false);
                 n++;
@@ -215,7 +215,7 @@ namespace Jint.Native.Object
             var properties = arguments.At(1);
             var props = TypeConverter.ToObject(Engine, properties);
             var descriptors = new List<KeyValuePair<string, PropertyDescriptor>>();
-            foreach (var p in props.Properties)
+            foreach (var p in props.GetOwnProperties())
             {
                 if (!p.Value.Enumerable.HasValue || !p.Value.Enumerable.Value)
                 {
@@ -243,7 +243,7 @@ namespace Jint.Native.Object
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            foreach (var prop in o.Properties)
+            foreach (var prop in o.GetOwnProperties())
             {
                 if (prop.Value.Configurable.HasValue && prop.Value.Configurable.Value)
                 {
@@ -267,7 +267,7 @@ namespace Jint.Native.Object
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            var keys = o.Properties.Keys.ToArray();
+            var keys = o.GetOwnProperties().Select(x => x.Key);
             foreach (var p in keys)
             {
                 var desc = o.GetOwnProperty(p);
@@ -313,7 +313,7 @@ namespace Jint.Native.Object
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            foreach (var prop in o.Properties)
+            foreach (var prop in o.GetOwnProperties())
             {
                 if (prop.Value.Configurable.Value == true)
                 {
@@ -338,7 +338,7 @@ namespace Jint.Native.Object
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            foreach (var p in o.Properties.Keys)
+            foreach (var p in o.GetOwnProperties().Select(x => x.Key))
             {
                 var desc = o.GetOwnProperty(p);
                 if (desc.IsDataDescriptor())
@@ -383,7 +383,7 @@ namespace Jint.Native.Object
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            var enumerableProperties = o.Properties
+            var enumerableProperties = o.GetOwnProperties()
                 .Where(x => x.Value.Enumerable.HasValue && x.Value.Enumerable.Value)
                 .ToArray();
             var n = enumerableProperties.Length;

+ 33 - 12
Jint/Native/Object/ObjectInstance.cs

@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using System;
 
 namespace Jint.Native.Object
 {
@@ -14,7 +15,7 @@ namespace Jint.Native.Object
 
         public Engine Engine { get; set; }
 
-        public IDictionary<string, PropertyDescriptor> Properties { get; private set; }
+        protected IDictionary<string, PropertyDescriptor> Properties { get; private set; }
 
         /// <summary>
         /// The prototype of this object.
@@ -36,6 +37,21 @@ namespace Jint.Native.Object
             get { return "Object"; }
         }
 
+        public virtual IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
+        {
+            return Properties;
+        }
+
+        public virtual bool HasOwnProperty(string p)
+        {
+            return Properties.ContainsKey(p);
+        }
+
+        public virtual void RemoveOwnProperty(string p)
+        {
+            Properties.Remove(p);
+        }
+
         /// <summary>
         /// Returns the value of the named property.
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.3
@@ -102,6 +118,11 @@ namespace Jint.Native.Object
             return PropertyDescriptor.Undefined;
         }
 
+        protected virtual void SetOwnProperty(string propertyName, PropertyDescriptor desc)
+        {
+            Properties[propertyName] = desc;
+        }
+
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.2
         /// </summary>
@@ -262,7 +283,7 @@ namespace Jint.Native.Object
 
             if (desc.Configurable.HasValue && desc.Configurable.Value)
             {
-                Properties.Remove(propertyName);
+                RemoveOwnProperty(propertyName);
                 return true;
             }
             else
@@ -369,23 +390,23 @@ namespace Jint.Native.Object
                 {
                     if (desc.IsGenericDescriptor() || desc.IsDataDescriptor())
                     {
-                        Properties[propertyName] = new PropertyDescriptor(desc)
+                        SetOwnProperty(propertyName, new PropertyDescriptor(desc)
                         {
                             Value = desc.Value.HasValue ? desc.Value : JsValue.Undefined,
                             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)
+                        SetOwnProperty(propertyName, new PropertyDescriptor(desc)
                         {
                             Get = desc.Get,
                             Set = desc.Set,
                             Enumerable = desc.Enumerable.HasValue ? desc.Enumerable : false,
                             Configurable = desc.Configurable.HasValue ? desc.Configurable : false,
-                        };
+                        });
                     }
                 }
 
@@ -457,21 +478,21 @@ namespace Jint.Native.Object
 
                     if (current.IsDataDescriptor())
                     {
-                        Properties[propertyName] = current = new PropertyDescriptor(
+                        SetOwnProperty(propertyName, current = new PropertyDescriptor(
                             get: Undefined.Instance,
                             set: Undefined.Instance,
                             enumerable: current.Enumerable,
                             configurable: current.Configurable
-                            );
+                            ));
                     }
                     else
                     {
-                        Properties[propertyName] = current = new PropertyDescriptor(
+                        SetOwnProperty(propertyName, current = new PropertyDescriptor(
                             value: Undefined.Instance, 
                             writable: null,
                             enumerable: current.Enumerable,
                             configurable: current.Configurable
-                            );
+                            ));
                     }
                 }
                 else if (current.IsDataDescriptor() && desc.IsDataDescriptor())
@@ -564,7 +585,7 @@ namespace Jint.Native.Object
         /// <param name="enumerable"></param>
         public void FastAddProperty(string name, JsValue value, bool writable, bool enumerable, bool configurable)
         {
-            Properties.Add(name, new PropertyDescriptor(value, writable, enumerable, configurable));
+            SetOwnProperty(name, new PropertyDescriptor(value, writable, enumerable, configurable));
         }
 
         /// <summary>
@@ -574,7 +595,7 @@ namespace Jint.Native.Object
         /// <param name="value"></param>
         public void FastSetProperty(string name, PropertyDescriptor value)
         {
-            Properties[name] = value;
+            SetOwnProperty(name, value);
         }
 
         public override string ToString()

+ 3 - 2
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -76,10 +76,11 @@ namespace Jint.Runtime.Environments
 
         public override string[] GetAllBindingNames()
         {
-            if (_bindingObject != null && _bindingObject.Properties != null)
+            if (_bindingObject != null)
             {
-                return _bindingObject.Properties.Keys.ToArray();
+                return _bindingObject.GetOwnProperties().Select( x=> x.Key).ToArray();
             }
+
             return new string[0];
         }
     }

+ 28 - 39
Jint/Runtime/MruPropertyCache2.cs

@@ -1,15 +1,13 @@
-using System;
-using System.Collections;
+using System.Collections;
 using System.Collections.Generic;
-using System.Linq;
-using System.Text;
 
-namespace Jint.Runtime {
+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 bool _set;
         private TKey _key;
         private TValue _value;
 
@@ -18,23 +16,18 @@ namespace Jint.Runtime {
 
         public TValue this[TKey key] {
             get {
-                if (_value != null)
+                if (_set && key.Equals(_key))
                 {
-                    if (key.Equals(_key))
-                    {
-                        return _value;
-                    }
+                    return _value;
                 }
 
                 return _dictionary[key];
             }
 
             set {
-                if(value != null)
-                {
-                    _key = key;
-                    _value = value;
-                }
+                _set = true;
+                _key = key;
+                _value = value;
 
                 _dictionary[key] = value;
             }
@@ -65,34 +58,31 @@ namespace Jint.Runtime {
         }
 
         public void Add(KeyValuePair<TKey, TValue> item) {
-            if(item.Value != null)
-            {
-                _key = item.Key;
-                _value = item.Value;
-            }
+            _set = true;
+            _key = item.Key;
+            _value = item.Value;
 
             _dictionary.Add(item);
         }
 
         public void Add(TKey key, TValue value) {
-            if (value != null)
-            {
-                _key = key;
-                _value = value;
-            }
+            _set = true;
+            _key = key;
+            _value = value;
 
             _dictionary.Add(key, value);
         }
 
         public void Clear() {
-            _key = null;
+            _set = false;
+            _key = default(TKey);
             _value = null;
 
             _dictionary.Clear();
         }
 
         public bool Contains(KeyValuePair<TKey, TValue> item) {
-            if(item.Key.Equals(_key) && _value != null)
+            if(_set && item.Key.Equals(_key))
             {
                 return true;
             }
@@ -101,7 +91,7 @@ namespace Jint.Runtime {
         }
 
         public bool ContainsKey(TKey key) {
-            if (key.Equals(_key) && _value != null)
+            if (_set && key.Equals(_key))
             {
                 return true;
             }
@@ -118,9 +108,10 @@ namespace Jint.Runtime {
         }
 
         public bool Remove(KeyValuePair<TKey, TValue> item) {
-            if(item.Key.Equals(_key))
+            if(_set && item.Key.Equals(_key))
             {
-                _key = null;
+                _set = false;
+                _key = default(TKey);
                 _value = null;
             }
 
@@ -128,9 +119,10 @@ namespace Jint.Runtime {
         }
 
         public bool Remove(TKey key) {
-            if (key.Equals(_key))
+            if (_set && key.Equals(_key))
             {
-                _key = null;
+                _set = false;
+                _key = default(TKey);
                 _value = null;
             }
 
@@ -138,13 +130,10 @@ namespace Jint.Runtime {
         }
 
         public bool TryGetValue(TKey key, out TValue value) {
-            if (_value != null)
+            if (_set && key.Equals(_key))
             {
-                if (key.Equals(_key))
-                {
-                    value = _value;
-                    return true;
-                }
+                value = _value;
+                return true;
             }
 
             return _dictionary.TryGetValue(key, out value);

+ 3 - 3
Jint/Runtime/StatementInterpreter.cs

@@ -227,7 +227,7 @@ namespace Jint.Runtime
 
             while (cursor != null)
             {
-                var keys = cursor.Properties.Keys.ToArray();
+                var keys = cursor.GetOwnProperties().Select(x => x.Key).ToArray();
                 foreach (var p in keys)
                 {
                     if (processedKeys.Contains(p))
@@ -238,12 +238,12 @@ namespace Jint.Runtime
                     processedKeys.Add(p);
                     
                     // collection might be modified by inner statement 
-                    if (!cursor.Properties.ContainsKey(p))
+                    if (!cursor.HasOwnProperty(p))
                     {
                         continue;
                     }
 
-                    var value = cursor.Properties[p];
+                    var value = cursor.GetOwnProperty(p);
                     if (!value.Enumerable.HasValue || !value.Enumerable.Value)
                     {
                         continue;