فهرست منبع

Introduce IPropertyDescriptor and specialized descriptors (#461)

Marko Lahma 7 سال پیش
والد
کامیت
f5f432951f
45فایلهای تغییر یافته به همراه635 افزوده شده و 343 حذف شده
  1. 1 1
      Jint/Engine.cs
  2. 42 44
      Jint/Native/Argument/ArgumentsInstance.cs
  3. 11 9
      Jint/Native/Array/ArrayConstructor.cs
  4. 21 19
      Jint/Native/Array/ArrayInstance.cs
  5. 3 2
      Jint/Native/Array/ArrayPrototype.cs
  6. 4 3
      Jint/Native/Boolean/BooleanConstructor.cs
  7. 2 1
      Jint/Native/Boolean/BooleanPrototype.cs
  8. 4 3
      Jint/Native/Date/DateConstructor.cs
  9. 2 1
      Jint/Native/Date/DatePrototype.cs
  10. 4 3
      Jint/Native/Error/ErrorConstructor.cs
  11. 2 1
      Jint/Native/Error/ErrorPrototype.cs
  12. 2 2
      Jint/Native/Function/BindFunctionInstance.cs
  13. 2 1
      Jint/Native/Function/EvalFunctionInstance.cs
  14. 10 4
      Jint/Native/Function/FunctionConstructor.cs
  15. 5 4
      Jint/Native/Function/FunctionPrototype.cs
  16. 11 4
      Jint/Native/Function/ScriptFunctionInstance.cs
  17. 3 3
      Jint/Native/Function/ThrowTypeError.cs
  18. 13 13
      Jint/Native/Json/JsonSerializer.cs
  19. 10 9
      Jint/Native/Number/NumberConstructor.cs
  20. 18 9
      Jint/Native/Object/ObjectConstructor.cs
  21. 91 55
      Jint/Native/Object/ObjectInstance.cs
  22. 15 17
      Jint/Native/RegExp/RegExpConstructor.cs
  23. 4 3
      Jint/Native/RegExp/RegExpPrototype.cs
  24. 7 6
      Jint/Native/String/StringConstructor.cs
  25. 3 2
      Jint/Native/String/StringInstance.cs
  26. 3 2
      Jint/Native/String/StringPrototype.cs
  27. 3 2
      Jint/Native/Symbol/SymbolConstructor.cs
  28. 2 1
      Jint/Native/Symbol/SymbolPrototype.cs
  29. 16 0
      Jint/Runtime/Descriptors/IPropertyDescriptor .cs
  30. 12 70
      Jint/Runtime/Descriptors/PropertyDescriptor.cs
  31. 62 0
      Jint/Runtime/Descriptors/PropertyDescriptorExtensions.cs
  32. 24 0
      Jint/Runtime/Descriptors/Specialized/AllForbiddenPropertyDescriptor.cs
  33. 33 10
      Jint/Runtime/Descriptors/Specialized/ClrAccessDescriptor.cs
  34. 24 0
      Jint/Runtime/Descriptors/Specialized/ConfigurableEnumerableWritablePropertyDescriptor.cs
  35. 24 0
      Jint/Runtime/Descriptors/Specialized/EnumerablePropertyDescriptor.cs
  36. 24 0
      Jint/Runtime/Descriptors/Specialized/NonConfigurablePropertyDescriptor.cs
  37. 24 0
      Jint/Runtime/Descriptors/Specialized/NonEnumerablePropertyDescriptor.cs
  38. 24 0
      Jint/Runtime/Descriptors/Specialized/NullConfigurationPropertyDescriptor.cs
  39. 24 0
      Jint/Runtime/Descriptors/Specialized/WritablePropertyDescriptor.cs
  40. 9 3
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  41. 3 2
      Jint/Runtime/ExpressionIntepreter.cs
  42. 2 1
      Jint/Runtime/Interop/ClrFunctionInstance.cs
  43. 22 22
      Jint/Runtime/Interop/NamespaceReference.cs
  44. 4 5
      Jint/Runtime/Interop/ObjectWrapper.cs
  45. 6 6
      Jint/Runtime/Interop/TypeReference.cs

+ 1 - 1
Jint/Engine.cs

@@ -157,7 +157,7 @@ namespace Jint
                 options(Options);
             }
 
-            Eval = new EvalFunctionInstance(this, new string[0], LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
+            Eval = new EvalFunctionInstance(this, System.Array.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
             Global.FastAddProperty("eval", Eval, true, false, true);
 
             _statements = new StatementInterpreter(this);

+ 42 - 44
Jint/Native/Argument/ArgumentsInstance.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Threading;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
@@ -14,6 +15,12 @@ namespace Jint.Native.Argument
     /// </summary>
     public class ArgumentsInstance : ObjectInstance
     {
+        // cache key container for array iteration for less allocations
+        private static readonly ThreadLocal<List<string>> _mappedNamed = new ThreadLocal<List<string>>(() => new List<string>());
+
+        private readonly Action<ArgumentsInstance> _initializer;
+        private bool _initialized;
+
         private ArgumentsInstance(Engine engine, Action<ArgumentsInstance> initializer) : base(engine)
         {
             _initializer = initializer;
@@ -22,12 +29,9 @@ namespace Jint.Native.Argument
 
         public bool Strict { get; set; }
 
-        private Action<ArgumentsInstance> _initializer;
-        private bool _initialized;
-
         protected override void EnsureInitialized()
         {
-            if(_initialized)
+            if (_initialized)
             {
                 return;
             }
@@ -39,43 +43,42 @@ namespace Jint.Native.Argument
 
         public static ArgumentsInstance CreateArgumentsObject(Engine engine, FunctionInstance func, string[] names, JsValue[] args, EnvironmentRecord env, bool strict)
         {
-            var obj = new ArgumentsInstance(engine, self =>
+            void Initializer(ArgumentsInstance self)
             {
                 var len = args.Length;
-                self.FastAddProperty("length", len, true, false, true);
-                var map = engine.Object.Construct(Arguments.Empty);
-                var mappedNamed = new List<string>();
-                var indx = 0;
-                while (indx <= len - 1)
+                self.SetOwnProperty("length", new NonEnumerablePropertyDescriptor(len));
+                if (args.Length > 0)
                 {
-                    var indxStr = TypeConverter.ToString(indx);
-                    var val = args[indx];
-                    self.FastAddProperty(indxStr, val, true, true, true);
-                    if (indx < names.Length)
+                    var map = engine.Object.Construct(Arguments.Empty);
+                    var mappedNamed = _mappedNamed.Value;
+                    mappedNamed.Clear();
+                    for (var indx = 0; indx < len; indx++)
                     {
-                        var name = names[indx];
-                        if (!strict && !mappedNamed.Contains(name))
+                        var indxStr = TypeConverter.ToString(indx);
+                        var val = args[indx];
+                        self.SetOwnProperty(indxStr, new ConfigurableEnumerableWritablePropertyDescriptor(val));
+                        if (indx < names.Length)
                         {
-                            mappedNamed.Add(name);
-                            Func<JsValue, JsValue> g = n => env.GetBindingValue(name, false);
-                            var p = new Action<JsValue, JsValue>((n, o) => env.SetMutableBinding(name, o, true));
-
-                            map.DefineOwnProperty(indxStr, new ClrAccessDescriptor(engine, g, p) { Configurable = true }, false);
+                            var name = names[indx];
+                            if (!strict && !mappedNamed.Contains(name))
+                            {
+                                mappedNamed.Add(name);
+                                map.SetOwnProperty(indxStr, new ClrAccessDescriptor(env, engine, name));
+                            }
                         }
                     }
-                    indx++;
-                }
 
-                // step 12
-                if (mappedNamed.Count > 0)
-                {
-                    self.ParameterMap = map;
+                    // step 12
+                    if (mappedNamed.Count > 0)
+                    {
+                        self.ParameterMap = map;
+                    }
                 }
 
                 // step 13
                 if (!strict)
                 {
-                    self.FastAddProperty("callee", func, true, false, true);
+                    self.SetOwnProperty("callee", new NonEnumerablePropertyDescriptor(func));
                 }
                 // step 14
                 else
@@ -84,7 +87,9 @@ namespace Jint.Native.Argument
                     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);
                 }
-            });
+            }
+
+            var obj = new ArgumentsInstance(engine, Initializer);
 
             // These properties are pre-initialized as their don't trigger
             // the EnsureInitialized() event and are cheap
@@ -92,22 +97,14 @@ namespace Jint.Native.Argument
             obj.Extensible = true;
             obj.Strict = strict;
 
-
             return obj;
         }
 
         public ObjectInstance ParameterMap { get; set; }
 
-        public override string Class
-        {
-            get
-            {
-                return "Arguments";
-            }
-        }
+        public override string Class => "Arguments";
 
-
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override IPropertyDescriptor GetOwnProperty(string propertyName)
         {
             EnsureInitialized();
 
@@ -152,7 +149,7 @@ namespace Jint.Native.Argument
 
             if (ownDesc.IsDataDescriptor())
             {
-                var valueDesc = new PropertyDescriptor(value: value, writable: null, enumerable: null, configurable: null);
+                var valueDesc = new NullConfigurationPropertyDescriptor(value);
                 DefineOwnProperty(propertyName, valueDesc, throwOnError);
                 return;
             }
@@ -163,16 +160,16 @@ namespace Jint.Native.Argument
             if (desc.IsAccessorDescriptor())
             {
                 var setter = desc.Set.TryCast<ICallable>();
-                setter.Call(JsValue, new[] { value });
+                setter.Call(JsValue, new[] {value});
             }
             else
             {
-                var newDesc = new PropertyDescriptor(value, true, true, true);
+                var newDesc = new ConfigurableEnumerableWritablePropertyDescriptor(value);
                 DefineOwnProperty(propertyName, newDesc, throwOnError);
             }
         }
 
-        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
         {
             EnsureInitialized();
 
@@ -188,6 +185,7 @@ namespace Jint.Native.Argument
                         throw new JavaScriptException(Engine.TypeError);
                     }
                 }
+
                 if (isMapped != PropertyDescriptor.Undefined)
                 {
                     if (desc.IsAccessorDescriptor())
@@ -234,4 +232,4 @@ namespace Jint.Native.Argument
             return base.Delete(propertyName, throwOnError);
         }
     }
-}
+}

+ 11 - 9
Jint/Native/Array/ArrayConstructor.cs

@@ -3,6 +3,7 @@ using System.Collections;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Array
@@ -24,17 +25,17 @@ namespace Jint.Native.Array
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = ArrayPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.FastAddProperty("length", 1, false, false, false);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
 
             // The initial value of Array.prototype is the Array prototype object
-            obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
+            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
 
             return obj;
         }
 
         public void Configure()
         {
-            FastAddProperty("isArray", new ClrFunctionInstance(Engine, IsArray, 1), true, false, true);
+            SetOwnProperty("isArray", new NonEnumerablePropertyDescriptor(new ClrFunctionInstance(Engine, IsArray, 1)));
         }
 
         private static JsValue IsArray(JsValue thisObj, JsValue[] arguments)
@@ -97,13 +98,11 @@ namespace Jint.Native.Array
                     throw new JavaScriptException(Engine.RangeError, "Invalid array length");
                 }
 
-                instance.FastAddProperty("length", length, true, false, false);
+                instance.SetOwnProperty("length", new WritablePropertyDescriptor(length));
             }
             else if (arguments.Length == 1 && arguments.At(0).IsObject() && arguments.At(0).As<ObjectWrapper>() != null )
             {
-                var enumerable = arguments.At(0).As<ObjectWrapper>().Target as IEnumerable;
-
-                if (enumerable != null)
+                if (arguments.At(0).As<ObjectWrapper>().Target is IEnumerable enumerable)
                 {
                     var jsArray = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
                     foreach (var item in enumerable)
@@ -117,8 +116,11 @@ namespace Jint.Native.Array
             }
             else
             {
-                instance.FastAddProperty("length", 0, true, false, false);
-                PrototypeObject.Push(instance, arguments);
+                instance.SetOwnProperty("length", new WritablePropertyDescriptor(0));
+                if (arguments.Length > 0)
+                {
+                    PrototypeObject.Push(instance, arguments);
+                }
             }
 
             return instance;

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

@@ -2,6 +2,8 @@
 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;
 
@@ -14,19 +16,19 @@ namespace Jint.Native.Array
         private const int MaxDenseArrayLength = 1024 * 10;
 
         // we have dense and sparse, we usually can start with dense and fall back to sparse when necessary
-        private PropertyDescriptor[] _dense;
-        private Dictionary<uint, PropertyDescriptor> _sparse;
+        private IPropertyDescriptor[] _dense;
+        private Dictionary<uint, IPropertyDescriptor> _sparse;
 
         public ArrayInstance(Engine engine, uint capacity = 0) : base(engine)
         {
             _engine = engine;
             if (capacity < MaxDenseArrayLength)
             {
-                _dense = capacity > 0 ? new PropertyDescriptor[capacity] : System.Array.Empty<PropertyDescriptor>();
+                _dense = capacity > 0 ? new IPropertyDescriptor[capacity] : System.Array.Empty<IPropertyDescriptor>();
             }
             else
             {
-                _sparse = new Dictionary<uint, PropertyDescriptor>((int) (capacity <= 1024 ? capacity : 1024));
+                _sparse = new Dictionary<uint, IPropertyDescriptor>((int) (capacity <= 1024 ? capacity : 1024));
             }
         }
 
@@ -51,7 +53,7 @@ namespace Jint.Native.Array
 
             if (ownDesc.IsDataDescriptor())
             {
-                var valueDesc = new PropertyDescriptor(value: value, writable: null, enumerable: null, configurable: null);
+                var valueDesc = new NullConfigurationPropertyDescriptor(value);
                 DefineOwnProperty(propertyName, valueDesc, throwOnError);
                 return;
             }
@@ -62,16 +64,16 @@ namespace Jint.Native.Array
             if (desc.IsAccessorDescriptor())
             {
                 var setter = desc.Set.TryCast<ICallable>();
-                setter.Call(JsValue, new[] { value });
+                setter.Call(JsValue, new[] {value});
             }
             else
             {
-                var newDesc = new PropertyDescriptor(value, true, true, true);
+                var newDesc = new ConfigurableEnumerableWritablePropertyDescriptor(value);
                 DefineOwnProperty(propertyName, newDesc, throwOnError);
             }
         }
 
-        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
         {
             var oldLenDesc = GetOwnProperty("length");
             var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc.Value);
@@ -190,7 +192,7 @@ namespace Jint.Native.Array
                                 var deleteSucceeded = Delete(TypeConverter.ToString(keyIndex), false);
                                 if (!deleteSucceeded)
                                 {
-                                newLenDesc.Value = JsValue.FromInt(keyIndex + 1);
+                                    newLenDesc.Value = JsValue.FromInt(keyIndex + 1);
                                     if (!newWritable)
                                     {
                                         newLenDesc.Writable = false;
@@ -283,7 +285,7 @@ namespace Jint.Native.Array
             return GetLengthValue();
         }
 
-        public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
+        public override IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
         {
             if (_dense != null)
             {
@@ -291,7 +293,7 @@ namespace Jint.Native.Array
                 {
                     if (_dense[i] != null)
                     {
-                        yield return new KeyValuePair<string, PropertyDescriptor>(TypeConverter.ToString(i), _dense[i]);
+                        yield return new KeyValuePair<string, IPropertyDescriptor>(TypeConverter.ToString(i), _dense[i]);
                     }
                 }
             }
@@ -299,7 +301,7 @@ namespace Jint.Native.Array
             {
                 foreach (var entry in _sparse)
                 {
-                    yield return new KeyValuePair<string, PropertyDescriptor>(TypeConverter.ToString(entry.Key), entry.Value);
+                    yield return new KeyValuePair<string, IPropertyDescriptor>(TypeConverter.ToString(entry.Key), entry.Value);
                 }
             }
 
@@ -309,7 +311,7 @@ namespace Jint.Native.Array
             }
         }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override IPropertyDescriptor GetOwnProperty(string propertyName)
         {
             if (IsArrayIndex(propertyName, out var index))
             {
@@ -324,7 +326,7 @@ namespace Jint.Native.Array
             return base.GetOwnProperty(propertyName);
         }
 
-        protected override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
+        protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
         {
             if (IsArrayIndex(propertyName, out var index))
             {
@@ -420,7 +422,7 @@ namespace Jint.Native.Array
                 p.Value = index + 1;
             }
 
-            WriteArrayValue(index, new PropertyDescriptor(value, true, true, true));
+            WriteArrayValue(index, new ConfigurableEnumerableWritablePropertyDescriptor(value));
         }
 
         internal uint GetSmallestIndex()
@@ -480,7 +482,7 @@ namespace Jint.Native.Array
             }
         }
 
-        private bool TryGetDescriptor(uint index, out PropertyDescriptor descriptor)
+        private bool TryGetDescriptor(uint index, out IPropertyDescriptor descriptor)
         {
             if (_dense != null)
             {
@@ -497,7 +499,7 @@ namespace Jint.Native.Array
             return _sparse.TryGetValue(index, out descriptor);
         }
 
-        private void WriteArrayValue(uint index, PropertyDescriptor desc)
+        private void WriteArrayValue(uint index, IPropertyDescriptor desc)
         {
             // calculate eagerly so we know if we outgrow
             var newSize = _dense != null && index >= _dense.Length
@@ -522,7 +524,7 @@ namespace Jint.Native.Array
             {
                 if (_dense != null)
                 {
-                    _sparse = new Dictionary<uint, PropertyDescriptor>(_dense.Length <= 1024 ? _dense.Length : 0);
+                    _sparse = new Dictionary<uint, IPropertyDescriptor>(_dense.Length <= 1024 ? _dense.Length : 0);
                     // need to move data
                     for (uint i = 0; i < _dense.Length; ++i)
                     {
@@ -544,7 +546,7 @@ namespace Jint.Native.Array
             if (capacity > _dense.Length)
             {
                 // need to grow
-                var newArray = new PropertyDescriptor[capacity];
+                var newArray = new IPropertyDescriptor[capacity];
                 System.Array.Copy(_dense, newArray, _dense.Length);
                 _dense = newArray;
             }

+ 3 - 2
Jint/Native/Array/ArrayPrototype.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Array
@@ -25,7 +26,7 @@ namespace Jint.Native.Array
             };
 
             obj.FastAddProperty("length", 0, true, false, false);
-            obj.FastAddProperty("constructor", arrayConstructor, true, false, true);
+            obj.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(arrayConstructor));
 
             return obj;
         }
@@ -804,7 +805,7 @@ namespace Jint.Native.Array
 
             // this is not in the specs, but is necessary in case the last element of the last
             // array doesn't exist, and thus the length would not be incremented
-            a.DefineOwnProperty("length", new PropertyDescriptor(n, null, null, null), false);
+            a.DefineOwnProperty("length", new NullConfigurationPropertyDescriptor(n), false);
 
             return a;
         }

+ 4 - 3
Jint/Native/Boolean/BooleanConstructor.cs

@@ -1,6 +1,7 @@
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Native.Boolean
 {
@@ -15,14 +16,14 @@ namespace Jint.Native.Boolean
             var obj = new BooleanConstructor(engine);
             obj.Extensible = true;
 
-            // The value of the [[Prototype]] internal property of the Boolean constructor is the Function prototype object 
+            // The value of the [[Prototype]] internal property of the Boolean constructor is the Function prototype object
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = BooleanPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.FastAddProperty("length", 1, false, false, false);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
 
             // The initial value of Boolean.prototype is the Boolean prototype object
-            obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
+            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
 
             return obj;
         }

+ 2 - 1
Jint/Native/Boolean/BooleanPrototype.cs

@@ -1,4 +1,5 @@
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Boolean
@@ -19,7 +20,7 @@ namespace Jint.Native.Boolean
             obj.PrimitiveValue = false;
             obj.Extensible = true;
 
-            obj.FastAddProperty("constructor", booleanConstructor, true, false, true);
+            obj.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(booleanConstructor));
 
             return obj;
         }

+ 4 - 3
Jint/Native/Date/DateConstructor.cs

@@ -3,6 +3,7 @@ using System.Globalization;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Date
@@ -20,14 +21,14 @@ namespace Jint.Native.Date
             var obj = new DateConstructor(engine);
             obj.Extensible = true;
 
-            // The value of the [[Prototype]] internal property of the Date constructor is the Function prototype object 
+            // The value of the [[Prototype]] internal property of the Date constructor is the Function prototype object
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = DatePrototype.CreatePrototypeObject(engine, obj);
 
-            obj.FastAddProperty("length", 7, false, false, false);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(7));
 
             // The initial value of Date.prototype is the Date prototype object
-            obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
+            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
 
             return obj;
         }

+ 2 - 1
Jint/Native/Date/DatePrototype.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Globalization;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Date
@@ -24,7 +25,7 @@ namespace Jint.Native.Date
                 PrimitiveValue = double.NaN
             };
 
-            obj.FastAddProperty("constructor", dateConstructor, true, false, true);
+            obj.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(dateConstructor));
 
             return obj;
         }

+ 4 - 3
Jint/Native/Error/ErrorConstructor.cs

@@ -1,6 +1,7 @@
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Native.Error
 {
@@ -22,17 +23,17 @@ namespace Jint.Native.Error
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = ErrorPrototype.CreatePrototypeObject(engine, obj, name);
 
-            obj.FastAddProperty("length", 1, false, false, false);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
 
             // The initial value of Error.prototype is the Error prototype object
-            obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
+            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
 
             return obj;
         }
 
         public void Configure()
         {
-            
+
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)

+ 2 - 1
Jint/Native/Error/ErrorPrototype.cs

@@ -1,5 +1,6 @@
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Error
@@ -17,7 +18,7 @@ namespace Jint.Native.Error
         public static ErrorPrototype CreatePrototypeObject(Engine engine, ErrorConstructor errorConstructor, string name)
         {
             var obj = new ErrorPrototype(engine, name) { Extensible = true };
-            obj.FastAddProperty("constructor", errorConstructor, true, false, true);
+            obj.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(errorConstructor));
             obj.FastAddProperty("message", "", true, false, true);
 
             if (name != "Error")

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

@@ -6,7 +6,7 @@ namespace Jint.Native.Function
 {
     public class BindFunctionInstance : FunctionInstance, IConstructor
     {
-        public BindFunctionInstance(Engine engine) : base(engine, new string[0], null, false)
+        public BindFunctionInstance(Engine engine) : base(engine, System.Array.Empty<string>(), null, false)
         {
         }
 
@@ -42,7 +42,7 @@ namespace Jint.Native.Function
             {
                 throw new JavaScriptException(Engine.TypeError);
             });
-              
+
             return f.HasInstance(v);
         }
     }

+ 2 - 1
Jint/Native/Function/EvalFunctionInstance.cs

@@ -1,5 +1,6 @@
 using Esprima;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Environments;
 
 namespace Jint.Native.Function
@@ -14,7 +15,7 @@ namespace Jint.Native.Function
         {
             _engine = engine;
             Prototype = Engine.Function.PrototypeObject;
-            FastAddProperty("length", 1, false, false, false);
+            SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)

+ 10 - 4
Jint/Native/Function/FunctionConstructor.cs

@@ -5,6 +5,7 @@ using Esprima.Ast;
 using Jint.Native.Object;
 using Jint.Native.String;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Environments;
 
 namespace Jint.Native.Function
@@ -28,9 +29,8 @@ namespace Jint.Native.Function
             // The value of the [[Prototype]] internal property of the Function constructor is the standard built-in Function prototype object
             obj.Prototype = obj.PrototypeObject;
 
-            obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
-
-            obj.FastAddProperty("length", 1, false, false, false);
+            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
 
             return obj;
         }
@@ -49,7 +49,12 @@ namespace Jint.Native.Function
 
         private string[] ParseArgumentNames(string parameterDeclaration)
         {
-            string[] values = parameterDeclaration.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+            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++)
@@ -124,6 +129,7 @@ namespace Jint.Native.Function
         }
 
         private FunctionInstance _throwTypeError;
+        private static readonly char[] ArgumentNameSeparator = new[] { ',' };
 
         public FunctionInstance ThrowTypeError
         {

+ 5 - 4
Jint/Native/Function/FunctionPrototype.cs

@@ -1,6 +1,7 @@
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Function
@@ -22,14 +23,14 @@ namespace Jint.Native.Function
             // The value of the [[Prototype]] internal property of the Function prototype object is the standard built-in Object prototype object
             obj.Prototype = engine.Object.PrototypeObject;
 
-            obj.FastAddProperty("length", 0, false, false, false);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(0));
 
             return obj;
         }
 
         public void Configure()
         {
-            FastAddProperty("constructor", Engine.Function, true, false, true);
+            SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(Engine.Function));
             FastAddProperty("toString", new ClrFunctionInstance(Engine, ToString), true, false, true);
             FastAddProperty("apply", new ClrFunctionInstance(Engine, Apply, 2), true, false, true);
             FastAddProperty("call", new ClrFunctionInstance(Engine, CallImpl, 1), true, false, true);
@@ -54,11 +55,11 @@ namespace Jint.Native.Function
             if (o != null)
             {
                 var l = TypeConverter.ToNumber(o.Get("length")) - (arguments.Length - 1);
-                f.FastAddProperty("length", System.Math.Max(l, 0), false, false, false);
+                f.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(System.Math.Max(l, 0)));
             }
             else
             {
-                f.FastAddProperty("length", 0, false, false, false);
+                f.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(0));
             }
 
 

+ 11 - 4
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -3,6 +3,7 @@ using Esprima.Ast;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Environments;
 
 namespace Jint.Native.Function
@@ -30,14 +31,14 @@ namespace Jint.Native.Function
             Extensible = true;
             Prototype = engine.Function.PrototypeObject;
 
-            DefineOwnProperty("length", new PropertyDescriptor(JsValue.FromInt(FormalParameters.Length), false, false, false ), false);
+            DefineOwnProperty("length", new AllForbiddenPropertyDescriptor(JsValue.FromInt(FormalParameters.Length)), false);
 
             var proto = engine.Object.Construct(Arguments.Empty);
-            proto.DefineOwnProperty("constructor", new PropertyDescriptor(this, true, false, true), false);
-            DefineOwnProperty("prototype", new PropertyDescriptor(proto, true, false, false ), false);
+            proto.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(this));
+            SetOwnProperty("prototype", new WritablePropertyDescriptor(proto));
             if (_functionDeclaration.Id != null)
             {
-                DefineOwnProperty("name", new PropertyDescriptor(_functionDeclaration.Id.Name, null, null, null), false);
+                DefineOwnProperty("name", new NullConfigurationPropertyDescriptor(_functionDeclaration.Id.Name), false);
             }
 
             if (strict)
@@ -53,6 +54,12 @@ namespace Jint.Native.Function
         {
             var list = functionDeclaration.Params;
             var count = list.Count;
+
+            if (count == 0)
+            {
+                return System.Array.Empty<string>();
+            }
+
             var names = new string[count];
             for (var i = 0; i < count; ++i)
             {

+ 3 - 3
Jint/Native/Function/ThrowTypeError.cs

@@ -1,5 +1,5 @@
 using Jint.Runtime;
-using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Native.Function
 {
@@ -7,10 +7,10 @@ namespace Jint.Native.Function
     {
         private readonly Engine _engine;
 
-        public ThrowTypeError(Engine engine): base(engine, new string[0], engine.GlobalEnvironment, false)
+        public ThrowTypeError(Engine engine): base(engine, System.Array.Empty<string>(), engine.GlobalEnvironment, false)
         {
             _engine = engine;
-            DefineOwnProperty("length", new PropertyDescriptor(0, false, false, false), false);
+            DefineOwnProperty("length", new AllForbiddenPropertyDescriptor(0), false);
             Extensible = false;
         }
 

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

@@ -5,7 +5,7 @@ using Jint.Native.Array;
 using Jint.Native.Global;
 using Jint.Native.Object;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Native.Json
 {
@@ -29,7 +29,7 @@ namespace Jint.Native.Json
 
             // for JSON.stringify(), any function passed as the first argument will return undefined
             // if the replacer is not defined. The function is not called either.
-            if (value.Is<ICallable>() && replacer == Undefined.Instance) 
+            if (value.Is<ICallable>() && replacer == Undefined.Instance)
             {
                 return Undefined.Instance;
             }
@@ -97,7 +97,7 @@ namespace Jint.Native.Json
                 if (space.AsNumber() > 0) {
                     _gap = new System.String(' ', (int)System.Math.Min(10, space.AsNumber()));
                 }
-                else 
+                else
                 {
                     _gap = string.Empty;
                 }
@@ -113,14 +113,14 @@ namespace Jint.Native.Json
             }
 
             var wrapper = _engine.Object.Construct(Arguments.Empty);
-            wrapper.DefineOwnProperty("", new PropertyDescriptor(value, true, true, true), false);
+            wrapper.DefineOwnProperty("", new ConfigurableEnumerableWritablePropertyDescriptor(value), false);
 
             return Str("", wrapper);
         }
 
         private JsValue Str(string key, ObjectInstance holder)
         {
-            
+
             var value = holder.Get(key);
             if (value.IsObject())
             {
@@ -134,14 +134,14 @@ namespace Jint.Native.Json
                     }
                 }
             }
-            
+
             if (_replacerFunction != Undefined.Instance)
             {
                 var replacerFunctionCallable = (ICallable)_replacerFunction.AsObject();
                 value = replacerFunctionCallable.Call(holder, Arguments.From(key, value));
             }
 
-            
+
             if (value.IsObject())
             {
                 var valueObj = value.AsObject();
@@ -156,7 +156,7 @@ namespace Jint.Native.Json
                     case "Boolean":
                         value = TypeConverter.ToPrimitive(value);
                         break;
-                    case "Array": 
+                    case "Array":
                         value = SerializeArray(value.As<ArrayInstance>());
                         return value;
                     case "Object":
@@ -164,7 +164,7 @@ namespace Jint.Native.Json
                         return value;
                 }
             }
-           
+
             if (value == Null.Instance)
             {
                 return "null";
@@ -191,7 +191,7 @@ namespace Jint.Native.Json
                 {
                     return TypeConverter.ToString(value);
                 }
-                
+
                 return "null";
             }
 
@@ -288,7 +288,7 @@ namespace Jint.Native.Json
                 var properties = System.String.Join(separator, partial.ToArray());
                 final = "[\n" + _indent + properties + "\n" + stepback + "]";
             }
-            
+
             _stack.Pop();
             _indent = stepback;
             return final;
@@ -315,7 +315,7 @@ namespace Jint.Native.Json
             _stack.Push(value);
             var stepback = _indent;
             _indent += _gap;
-            
+
             var k = _propertyList ?? value.GetOwnProperties()
                 .Where(x => x.Value.Enumerable.HasValue && x.Value.Enumerable.Value == true)
                 .Select(x => x.Key)
@@ -353,7 +353,7 @@ namespace Jint.Native.Json
                     var separator = ",\n" + _indent;
                     var properties = System.String.Join(separator, partial.ToArray());
                     final = "{\n" + _indent + properties + "\n" + stepback + "}";
-                }                
+                }
             }
             _stack.Pop();
             _indent = stepback;

+ 10 - 9
Jint/Native/Number/NumberConstructor.cs

@@ -1,6 +1,7 @@
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Native.Number
 {
@@ -9,7 +10,7 @@ namespace Jint.Native.Number
         public NumberConstructor(Engine engine)
             : base(engine, null, null, false)
         {
-            
+
         }
 
         public static NumberConstructor CreateNumberConstructor(Engine engine)
@@ -17,25 +18,25 @@ namespace Jint.Native.Number
             var obj = new NumberConstructor(engine);
             obj.Extensible = true;
 
-            // The value of the [[Prototype]] internal property of the Number constructor is the Function prototype object 
+            // The value of the [[Prototype]] internal property of the Number constructor is the Function prototype object
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = NumberPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.FastAddProperty("length", 1, false, false, false);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
 
             // The initial value of Number.prototype is the Number prototype object
-            obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
+            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
 
             return obj;
         }
 
         public void Configure()
         {
-            FastAddProperty("MAX_VALUE", double.MaxValue, false, false, false);
-            FastAddProperty("MIN_VALUE", double.Epsilon, false, false, false);
-            FastAddProperty("NaN", double.NaN, false, false, false);
-            FastAddProperty("NEGATIVE_INFINITY", double.NegativeInfinity, false, false, false);
-            FastAddProperty("POSITIVE_INFINITY", double.PositiveInfinity, false, false, false);
+            SetOwnProperty("MAX_VALUE", new AllForbiddenPropertyDescriptor(double.MaxValue));
+            SetOwnProperty("MIN_VALUE", new AllForbiddenPropertyDescriptor(double.Epsilon));
+            SetOwnProperty("NaN", new AllForbiddenPropertyDescriptor(double.NaN));
+            SetOwnProperty("NEGATIVE_INFINITY", new AllForbiddenPropertyDescriptor(double.NegativeInfinity));
+            SetOwnProperty("POSITIVE_INFINITY", new AllForbiddenPropertyDescriptor(double.PositiveInfinity));
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)

+ 18 - 9
Jint/Native/Object/ObjectConstructor.cs

@@ -5,6 +5,7 @@ using Jint.Native.Function;
 using Jint.Native.String;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Object
@@ -25,8 +26,8 @@ namespace Jint.Native.Object
 
             obj.PrototypeObject = ObjectPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.FastAddProperty("length", 1, false, false, false);
-            obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
+            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
 
             return obj;
         }
@@ -247,15 +248,19 @@ namespace Jint.Native.Object
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            var properties = new List<KeyValuePair<string, PropertyDescriptor>>(o.GetOwnProperties());
+            var properties = new List<KeyValuePair<string, IPropertyDescriptor>>(o.GetOwnProperties());
             foreach (var prop in properties)
             {
-                if (prop.Value.Configurable.HasValue && prop.Value.Configurable.Value)
+                var propertyDescriptor = prop.Value;
+                if (propertyDescriptor.Configurable.HasValue && propertyDescriptor.Configurable.Value)
                 {
-                    prop.Value.Configurable = false;
+                    var mutable = propertyDescriptor as PropertyDescriptor ?? new PropertyDescriptor(propertyDescriptor);
+                    mutable.Configurable = false;
+                    propertyDescriptor = mutable;
+                    FastSetProperty(prop.Key, mutable);
                 }
 
-                o.DefineOwnProperty(prop.Key, prop.Value, true);
+                o.DefineOwnProperty(prop.Key, propertyDescriptor, true);
             }
 
             o.Extensible = false;
@@ -272,7 +277,7 @@ namespace Jint.Native.Object
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            var properties = new List<KeyValuePair<string, PropertyDescriptor>>(o.GetOwnProperties());
+            var properties = new List<KeyValuePair<string, IPropertyDescriptor>>(o.GetOwnProperties());
             foreach (var p in properties)
             {
                 var desc = o.GetOwnProperty(p.Key);
@@ -280,12 +285,16 @@ namespace Jint.Native.Object
                 {
                     if (desc.Writable.HasValue && desc.Writable.Value)
                     {
-                        desc.Writable = false;
+                        var mutable = desc as PropertyDescriptor ?? new PropertyDescriptor(desc);
+                        mutable.Writable = false;
+                        desc = mutable;
                     }
                 }
                 if (desc.Configurable.HasValue && desc.Configurable.Value)
                 {
-                    desc.Configurable = false;
+                    var mutable = desc as PropertyDescriptor ?? new PropertyDescriptor(desc);
+                    mutable.Configurable = false;
+                    desc = mutable;
                 }
                 o.DefineOwnProperty(p.Key, desc, true);
             }

+ 91 - 55
Jint/Native/Object/ObjectInstance.cs

@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Native.Object
 {
@@ -12,12 +13,12 @@ namespace Jint.Native.Object
 
         private JsValue _jsValue;
 
-        private Dictionary<string, PropertyDescriptor> _intrinsicProperties;
-        private MruPropertyCache2<string, PropertyDescriptor> _properties;
+        private Dictionary<string, IPropertyDescriptor> _intrinsicProperties;
+        private MruPropertyCache2<string, IPropertyDescriptor> _properties;
 
-        private PropertyDescriptor _prototype;
-        private PropertyDescriptor _constructor;
-        private PropertyDescriptor _length;
+        private IPropertyDescriptor _prototype;
+        private IPropertyDescriptor _constructor;
+        private IPropertyDescriptor _length;
 
         public ObjectInstance(Engine engine)
         {
@@ -36,7 +37,7 @@ namespace Jint.Native.Object
 
         protected bool TryGetIntrinsicValue(JsSymbol symbol, out JsValue value)
         {
-            PropertyDescriptor descriptor;
+            IPropertyDescriptor descriptor;
 
             if (_intrinsicProperties != null && _intrinsicProperties.TryGetValue(symbol.AsSymbol(), out descriptor))
             {
@@ -62,7 +63,7 @@ namespace Jint.Native.Object
         {
             if (_intrinsicProperties == null)
             {
-                _intrinsicProperties = new Dictionary<string, PropertyDescriptor>();
+                _intrinsicProperties = new Dictionary<string, IPropertyDescriptor>();
             }
 
             _intrinsicProperties[symbol.AsSymbol()] = new PropertyDescriptor(value, writable, enumerable, configurable);
@@ -85,25 +86,27 @@ namespace Jint.Native.Object
         /// </summary>
         public virtual string Class => "Object";
 
-        public virtual IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
+        public virtual IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
         {
             EnsureInitialized();
 
             if (_prototype != null)
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNamePrototype, _prototype);
-        }
+                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNamePrototype, _prototype);
+            }
+
             if (_constructor != null)
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameConstructor, _constructor);
+                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameConstructor, _constructor);
             }
+
             if (_length != null)
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameLength, _length);
+                yield return new KeyValuePair<string, IPropertyDescriptor>(PropertyNameLength, _length);
             }
 
             if (_properties != null)
-        {
+            {
                 foreach (var pair in _properties.GetEnumerator())
                 {
                     yield return pair;
@@ -111,18 +114,20 @@ namespace Jint.Native.Object
             }
         }
 
-        protected void AddProperty(string propertyName, PropertyDescriptor descriptor)
+        protected void AddProperty(string propertyName, IPropertyDescriptor descriptor)
         {
             if (propertyName == PropertyNamePrototype)
             {
                 _prototype = descriptor;
                 return;
             }
+
             if (propertyName == PropertyNameConstructor)
             {
                 _constructor = descriptor;
                 return;
             }
+
             if (propertyName == PropertyNameLength)
             {
                 _length = descriptor;
@@ -131,24 +136,26 @@ namespace Jint.Native.Object
 
             if (_properties == null)
             {
-                _properties = new MruPropertyCache2<string, PropertyDescriptor>();
+                _properties = new MruPropertyCache2<string, IPropertyDescriptor>();
             }
 
             _properties.Add(propertyName, descriptor);
         }
 
-        protected bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
+        protected 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;
@@ -171,11 +178,13 @@ namespace Jint.Native.Object
             if (propertyName == PropertyNamePrototype)
             {
                 return _prototype != null;
-        }
+            }
+
             if (propertyName == PropertyNameConstructor)
             {
                 return _constructor != null;
             }
+
             if (propertyName == PropertyNameLength)
             {
                 return _length != null;
@@ -191,11 +200,13 @@ namespace Jint.Native.Object
             if (propertyName == PropertyNamePrototype)
             {
                 _prototype = null;
-        }
+            }
+
             if (propertyName == PropertyNameConstructor)
             {
                 _constructor = null;
             }
+
             if (propertyName == PropertyNameLength)
             {
                 _length = null;
@@ -245,7 +256,7 @@ namespace Jint.Native.Object
         /// </summary>
         /// <param name="propertyName"></param>
         /// <returns></returns>
-        public virtual PropertyDescriptor GetOwnProperty(string propertyName)
+        public virtual IPropertyDescriptor GetOwnProperty(string propertyName)
         {
             EnsureInitialized();
 
@@ -253,17 +264,18 @@ namespace Jint.Native.Object
             {
                 return _prototype ?? PropertyDescriptor.Undefined;
             }
+
             if (propertyName == PropertyNameConstructor)
-                {
+            {
                 return _constructor ?? PropertyDescriptor.Undefined;
-                }
+            }
+
             if (propertyName == PropertyNameLength)
-                {
+            {
                 return _length ?? PropertyDescriptor.Undefined;
-                }
+            }
 
-            PropertyDescriptor x;
-            if (_properties != null && _properties.TryGetValue(propertyName, out x))
+            if (_properties != null && _properties.TryGetValue(propertyName, out var x))
             {
                 return x;
             }
@@ -271,7 +283,7 @@ namespace Jint.Native.Object
             return PropertyDescriptor.Undefined;
         }
 
-        protected virtual void SetOwnProperty(string propertyName, PropertyDescriptor desc)
+        protected internal virtual void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
         {
             EnsureInitialized();
 
@@ -279,12 +291,14 @@ namespace Jint.Native.Object
             {
                 _prototype = desc;
                 return;
-        }
+            }
+
             if (propertyName == PropertyNameConstructor)
             {
                 _constructor = desc;
                 return;
             }
+
             if (propertyName == PropertyNameLength)
             {
                 _length = desc;
@@ -293,7 +307,7 @@ namespace Jint.Native.Object
 
             if (_properties == null)
             {
-                _properties = new MruPropertyCache2<string, PropertyDescriptor>();
+                _properties = new MruPropertyCache2<string, IPropertyDescriptor>();
             }
 
             _properties[propertyName] = desc;
@@ -304,7 +318,7 @@ namespace Jint.Native.Object
         /// </summary>
         /// <param name="propertyName"></param>
         /// <returns></returns>
-        public PropertyDescriptor GetProperty(string propertyName)
+        public IPropertyDescriptor GetProperty(string propertyName)
         {
             var prop = GetOwnProperty(propertyName);
 
@@ -313,7 +327,7 @@ namespace Jint.Native.Object
                 return prop;
             }
 
-            if(Prototype == null)
+            if (Prototype == null)
             {
                 return PropertyDescriptor.Undefined;
             }
@@ -352,7 +366,7 @@ namespace Jint.Native.Object
                 return true;
             }
 
-            if(Prototype == null)
+            if (Prototype == null)
             {
                 return false;
             }
@@ -399,11 +413,11 @@ namespace Jint.Native.Object
             if (desc.IsAccessorDescriptor())
             {
                 var setter = desc.Set.TryCast<ICallable>();
-                setter.Call(JsValue, new [] {value});
+                setter.Call(JsValue, new[] {value});
             }
             else
             {
-                var newDesc = new PropertyDescriptor(value, true, true, true);
+                var newDesc = new ConfigurableEnumerableWritablePropertyDescriptor(value);
                 DefineOwnProperty(propertyName, newDesc, throwOnError);
             }
         }
@@ -584,11 +598,12 @@ namespace Jint.Native.Object
         /// <param name="desc"></param>
         /// <param name="throwOnError"></param>
         /// <returns></returns>
-        public virtual bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
+        public virtual bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
         {
             var current = GetOwnProperty(propertyName);
 
-            if (current == desc) {
+            if (current == desc)
+            {
                 return true;
             }
 
@@ -607,13 +622,26 @@ namespace Jint.Native.Object
                 {
                     if (desc.IsGenericDescriptor() || desc.IsDataDescriptor())
                     {
-                        SetOwnProperty(propertyName, new PropertyDescriptor(desc)
+                        IPropertyDescriptor propertyDescriptor;
+                        if (desc.Configurable.GetValueOrDefault() && desc.Enumerable.GetValueOrDefault() && desc.Writable.GetValueOrDefault())
                         {
-                            Value = desc.Value != null ? 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
-                        });
+                            propertyDescriptor = new ConfigurableEnumerableWritablePropertyDescriptor(desc.Value != null ? desc.Value : JsValue.Undefined);
+                        }
+                        else if (!desc.Configurable.GetValueOrDefault() && !desc.Enumerable.GetValueOrDefault() && !desc.Writable.GetValueOrDefault())
+                        {
+                            propertyDescriptor = new AllForbiddenPropertyDescriptor(desc.Value != null ? desc.Value : JsValue.Undefined);
+                        }
+                        else
+                        {
+                            propertyDescriptor = new PropertyDescriptor(desc)
+                            {
+                                Value = desc.Value != null ? 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
+                            };
+                        }
+                        SetOwnProperty(propertyName, propertyDescriptor);
                     }
                     else
                     {
@@ -638,7 +666,6 @@ namespace Jint.Native.Object
                 current.Set == null &&
                 current.Value == null)
             {
-
                 return true;
             }
 
@@ -647,11 +674,11 @@ namespace Jint.Native.Object
                 current.Configurable == desc.Configurable &&
                 current.Writable == desc.Writable &&
                 current.Enumerable == desc.Enumerable &&
-
                 ((current.Get == null && desc.Get == null) || (current.Get != null && desc.Get != null && ExpressionInterpreter.SameValue(current.Get, desc.Get))) &&
                 ((current.Set == null && desc.Set == null) || (current.Set != null && desc.Set != null && ExpressionInterpreter.SameValue(current.Set, desc.Set))) &&
                 ((current.Value == null && desc.Value == null) || (current.Value != null && desc.Value != null && ExpressionInterpreter.StrictlyEqual(current.Value, desc.Value)))
-            ) {
+            )
+            {
                 return true;
             }
 
@@ -680,7 +707,6 @@ namespace Jint.Native.Object
 
             if (!desc.IsGenericDescriptor())
             {
-
                 if (current.IsDataDescriptor() != desc.IsDataDescriptor())
                 {
                     if (!current.Configurable.HasValue || !current.Configurable.Value)
@@ -700,7 +726,7 @@ namespace Jint.Native.Object
                             set: Undefined.Instance,
                             enumerable: current.Enumerable,
                             configurable: current.Configurable
-                            ));
+                        ));
                     }
                     else
                     {
@@ -709,7 +735,7 @@ namespace Jint.Native.Object
                             writable: null,
                             enumerable: current.Enumerable,
                             configurable: current.Configurable
-                            ));
+                        ));
                     }
                 }
                 else if (current.IsDataDescriptor() && desc.IsDataDescriptor())
@@ -764,29 +790,40 @@ namespace Jint.Native.Object
                 current.Value = desc.Value;
             }
 
+            PropertyDescriptor mutable = null;
             if (desc.Writable.HasValue)
             {
-                current.Writable = desc.Writable;
+                current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
+                mutable.Writable = desc.Writable;
             }
 
             if (desc.Enumerable.HasValue)
             {
-                current.Enumerable = desc.Enumerable;
+                current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
+                mutable.Enumerable = desc.Enumerable;
             }
 
             if (desc.Configurable.HasValue)
             {
-                current.Configurable = desc.Configurable;
+                current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
+                mutable.Configurable = desc.Configurable;
             }
 
             if (desc.Get != null)
             {
-                current.Get = desc.Get;
+                current = mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
+                mutable.Get = desc.Get;
             }
 
             if (desc.Set != null)
             {
-                current.Set = desc.Set;
+                mutable = current as PropertyDescriptor ?? new PropertyDescriptor(current);
+                mutable.Set = desc.Set;
+            }
+
+            if (mutable != null)
+            {
+                FastSetProperty(propertyName, mutable);
             }
 
             return true;
@@ -810,14 +847,13 @@ namespace Jint.Native.Object
         /// </summary>
         /// <param name="name"></param>
         /// <param name="value"></param>
-        public void FastSetProperty(string name, PropertyDescriptor value)
+        public void FastSetProperty(string name, IPropertyDescriptor value)
         {
             SetOwnProperty(name, value);
         }
 
         protected virtual void EnsureInitialized()
         {
-
         }
 
         public override string ToString()
@@ -827,4 +863,4 @@ namespace Jint.Native.Object
 
         protected uint GetLengthValue() => TypeConverter.ToUint32(_length.Value);
     }
-}
+}

+ 15 - 17
Jint/Native/RegExp/RegExpConstructor.cs

@@ -4,6 +4,7 @@ using Esprima;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Native.RegExp
 {
@@ -23,10 +24,10 @@ namespace Jint.Native.RegExp
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = RegExpPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.FastAddProperty("length", 2, false, false, false);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(2));
 
             // The initial value of RegExp.prototype is the RegExp prototype object
-            obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
+            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
 
             return obj;
         }
@@ -112,11 +113,7 @@ namespace Jint.Native.RegExp
             r.Source = s;
             AssignFlags(r, f);
 
-            r.FastAddProperty("global", r.Global, false, false, false);
-            r.FastAddProperty("ignoreCase", r.IgnoreCase, false, false, false);
-            r.FastAddProperty("multiline", r.Multiline, false, false, false);
-            r.FastAddProperty("source", r.Source, false, false, false);
-            r.FastAddProperty("lastIndex", 0, true, false, false);
+            SetRegexProperties(r);
 
             return r;
         }
@@ -136,11 +133,7 @@ namespace Jint.Native.RegExp
             AssignFlags(r, flags);
             r.Source = System.String.IsNullOrEmpty(body) ? "(?:)" : body;
 
-            r.FastAddProperty("global", r.Global, false, false, false);
-            r.FastAddProperty("ignoreCase", r.IgnoreCase, false, false, false);
-            r.FastAddProperty("multiline", r.Multiline, false, false, false);
-            r.FastAddProperty("source", r.Source, false, false, false);
-            r.FastAddProperty("lastIndex", 0, true, false, false);
+            SetRegexProperties(r);
 
             return r;
         }
@@ -157,15 +150,20 @@ namespace Jint.Native.RegExp
             r.Source = regExp.ToString();
             r.Value = regExp;
 
-            r.FastAddProperty("global", r.Global, false, false, false);
-            r.FastAddProperty("ignoreCase", r.IgnoreCase, false, false, false);
-            r.FastAddProperty("multiline", r.Multiline, false, false, false);
-            r.FastAddProperty("source", r.Source, false, false, false);
-            r.FastAddProperty("lastIndex", 0, true, false, false);
+            SetRegexProperties(r);
 
             return r;
         }
 
+        private static void SetRegexProperties(RegExpInstance r)
+        {
+            r.SetOwnProperty("global", new AllForbiddenPropertyDescriptor(r.Global));
+            r.SetOwnProperty("ignoreCase", new AllForbiddenPropertyDescriptor(r.IgnoreCase));
+            r.SetOwnProperty("multiline", new AllForbiddenPropertyDescriptor(r.Multiline));
+            r.SetOwnProperty("source", new AllForbiddenPropertyDescriptor(r.Source));
+            r.SetOwnProperty("lastIndex", new WritablePropertyDescriptor(0));
+        }
+
         private void AssignFlags(RegExpInstance r, string flags)
         {
             for(var i=0; i < flags.Length; i++)

+ 4 - 3
Jint/Native/RegExp/RegExpPrototype.cs

@@ -2,6 +2,7 @@
 using Jint.Native.Array;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.RegExp
@@ -124,9 +125,9 @@ namespace Jint.Native.RegExp
 
         private static ArrayInstance InitReturnValueArray(ArrayInstance array, string inputValue, int lengthValue, int indexValue)
         {
-            array.DefineOwnProperty("index", new PropertyDescriptor(indexValue, writable: true, enumerable: true, configurable: true), true);
-            array.DefineOwnProperty("input", new PropertyDescriptor(inputValue, writable: true, enumerable: true, configurable: true), true);
-            array.DefineOwnProperty("length", new PropertyDescriptor(value: lengthValue, writable: true, enumerable: false, configurable: false), true);
+            array.SetOwnProperty("index", new ConfigurableEnumerableWritablePropertyDescriptor(indexValue));
+            array.SetOwnProperty("input", new ConfigurableEnumerableWritablePropertyDescriptor(inputValue));
+            array.SetOwnProperty("length", new WritablePropertyDescriptor(lengthValue));
             return array;
         }
     }

+ 7 - 6
Jint/Native/String/StringConstructor.cs

@@ -1,6 +1,7 @@
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.String
@@ -17,21 +18,21 @@ namespace Jint.Native.String
             var obj = new StringConstructor(engine);
             obj.Extensible = true;
 
-            // The value of the [[Prototype]] internal property of the String constructor is the Function prototype object 
+            // The value of the [[Prototype]] internal property of the String constructor is the Function prototype object
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = StringPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.FastAddProperty("length", 1, false, false, false);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(1));
 
             // The initial value of String.prototype is the String prototype object
-            obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
+            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
 
             return obj;
         }
 
         public void Configure()
         {
-            FastAddProperty("fromCharCode", new ClrFunctionInstance(Engine, FromCharCode, 1), true, false, true);
+            SetOwnProperty("fromCharCode", new NonEnumerablePropertyDescriptor(new ClrFunctionInstance(Engine, FromCharCode, 1)));
         }
 
         private static JsValue FromCharCode(JsValue thisObj, JsValue[] arguments)
@@ -41,7 +42,7 @@ namespace Jint.Native.String
             {
                 chars[i] = (char)TypeConverter.ToUint16(arguments[i]);
             }
-            
+
             return new System.String(chars);
         }
 
@@ -74,7 +75,7 @@ namespace Jint.Native.String
             instance.PrimitiveValue = value;
             instance.Extensible = true;
 
-            instance.FastAddProperty("length", value.Length, false, false, false);
+            instance.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(value.Length));
 
             return instance;
         }

+ 3 - 2
Jint/Native/String/StringInstance.cs

@@ -1,6 +1,7 @@
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Native.String
 {
@@ -30,7 +31,7 @@ namespace Jint.Native.String
             return false;
         }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override IPropertyDescriptor GetOwnProperty(string propertyName)
         {
             if (propertyName == "Infinity")
             {
@@ -62,7 +63,7 @@ namespace Jint.Native.String
             }
 
             var resultStr = TypeConverter.ToString(str.AsString()[index]);
-            return new PropertyDescriptor(resultStr, false, true, false);
+            return new EnumerablePropertyDescriptor(resultStr);
         }
     }
 }

+ 3 - 2
Jint/Native/String/StringPrototype.cs

@@ -7,6 +7,7 @@ using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Native.RegExp;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.String
@@ -27,8 +28,8 @@ namespace Jint.Native.String
             obj.Prototype = engine.Object.PrototypeObject;
             obj.PrimitiveValue = "";
             obj.Extensible = true;
-            obj.FastAddProperty("length", 0, false, false, false);
-            obj.FastAddProperty("constructor", stringConstructor, true, false, true);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(0));
+            obj.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(stringConstructor));
 
             return obj;
         }

+ 3 - 2
Jint/Native/Symbol/SymbolConstructor.cs

@@ -1,6 +1,7 @@
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Symbol
@@ -25,10 +26,10 @@ namespace Jint.Native.Symbol
             obj.Prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = SymbolPrototype.CreatePrototypeObject(engine, obj);
 
-            obj.FastAddProperty("length", 0, false, false, false);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(0));
 
             // The initial value of String.prototype is the String prototype object
-            obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
+            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(obj.PrototypeObject));
 
 
             return obj;

+ 2 - 1
Jint/Native/Symbol/SymbolPrototype.cs

@@ -1,5 +1,6 @@
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Symbol
@@ -19,7 +20,7 @@ namespace Jint.Native.Symbol
             var obj = new SymbolPrototype(engine);
             obj.Prototype = engine.Object.PrototypeObject;
             obj.Extensible = true;
-            obj.FastAddProperty("length", 0, false, false, false);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(0));
             obj.FastAddProperty("constructor", symbolConstructor, true, false, true);
 
             return obj;

+ 16 - 0
Jint/Runtime/Descriptors/IPropertyDescriptor .cs

@@ -0,0 +1,16 @@
+using Jint.Native;
+
+namespace Jint.Runtime.Descriptors
+{
+    public interface IPropertyDescriptor
+    {
+        JsValue Get { get; }
+        JsValue Set { get; }
+
+        bool? Enumerable { get; }
+        bool? Writable { get; }
+        bool? Configurable { get; }
+
+        JsValue Value { get; set; }
+    }
+}

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

@@ -1,13 +1,14 @@
 using Jint.Native;
 using Jint.Native.Object;
+using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Runtime.Descriptors
 {
-    public class PropertyDescriptor
+    public class PropertyDescriptor : IPropertyDescriptor
     {
-        public static PropertyDescriptor Undefined = new PropertyDescriptor();
+        public static readonly IPropertyDescriptor Undefined = new PropertyDescriptor();
 
-        public PropertyDescriptor()
+        protected PropertyDescriptor()
         {
         }
 
@@ -47,7 +48,7 @@ namespace Jint.Runtime.Descriptors
             }
         }
 
-        public PropertyDescriptor(PropertyDescriptor descriptor)
+        public PropertyDescriptor(IPropertyDescriptor descriptor)
         {
             Get = descriptor.Get;
             Set = descriptor.Set;
@@ -64,35 +65,6 @@ namespace Jint.Runtime.Descriptors
         public bool? Configurable { get; set; }
         public virtual JsValue Value { get; set; }
 
-        public bool IsAccessorDescriptor()
-        {
-            if (Get ==null && Set == null)
-            {
-                return false;
-            }
-
-            return true;
-        }
-
-        public bool IsDataDescriptor()
-        {
-            if (!Writable.HasValue && Value == null)
-            {
-                return false;
-            }
-
-            return true;
-        }
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.10.3
-        /// </summary>
-        /// <returns></returns>
-        public bool IsGenericDescriptor()
-        {
-            return !IsDataDescriptor() && !IsAccessorDescriptor();
-        }
-
         public static PropertyDescriptor ToPropertyDescriptor(Engine engine, JsValue o)
         {
             var obj = o.TryCast<ObjectInstance>();
@@ -161,7 +133,7 @@ namespace Jint.Runtime.Descriptors
             return desc;
         }
 
-        public static JsValue FromPropertyDescriptor(Engine engine, PropertyDescriptor desc)
+        public static JsValue FromPropertyDescriptor(Engine engine, IPropertyDescriptor desc)
         {
             if (desc == Undefined)
             {
@@ -172,49 +144,19 @@ namespace Jint.Runtime.Descriptors
 
             if (desc.IsDataDescriptor())
             {
-                obj.DefineOwnProperty("value", new PropertyDescriptor(value: desc.Value != null ? desc.Value : Native.Undefined.Instance, writable: true, enumerable: true, configurable: true ), false);
-                obj.DefineOwnProperty("writable", new PropertyDescriptor(value: desc.Writable.HasValue && desc.Writable.Value, writable: true, enumerable: true, configurable: true), false);
+                obj.SetOwnProperty("value", new ConfigurableEnumerableWritablePropertyDescriptor(desc.Value != null ? desc.Value : Native.Undefined.Instance));
+                obj.SetOwnProperty("writable", new ConfigurableEnumerableWritablePropertyDescriptor(desc.Writable.HasValue && desc.Writable.Value));
             }
             else
             {
-                obj.DefineOwnProperty("get", new PropertyDescriptor(desc.Get ?? Native.Undefined.Instance, writable: true, enumerable: true, configurable: true ), false);
-                obj.DefineOwnProperty("set", new PropertyDescriptor(desc.Set ?? Native.Undefined.Instance, writable: true, enumerable: true, configurable: true), false);
+                obj.SetOwnProperty("get", new ConfigurableEnumerableWritablePropertyDescriptor(desc.Get ?? Native.Undefined.Instance));
+                obj.SetOwnProperty("set", new ConfigurableEnumerableWritablePropertyDescriptor(desc.Set ?? Native.Undefined.Instance));
             }
 
-            obj.DefineOwnProperty("enumerable", new PropertyDescriptor(value: desc.Enumerable.HasValue && desc.Enumerable.Value, writable: true, enumerable: true, configurable: true), false);
-            obj.DefineOwnProperty("configurable", new PropertyDescriptor(value: desc.Configurable.HasValue && desc.Configurable.Value, writable: true, enumerable: true, configurable: true), false);
+            obj.SetOwnProperty("enumerable", new ConfigurableEnumerableWritablePropertyDescriptor(desc.Enumerable.HasValue && desc.Enumerable.Value));
+            obj.SetOwnProperty("configurable", new ConfigurableEnumerableWritablePropertyDescriptor(desc.Configurable.HasValue && desc.Configurable.Value));
 
             return obj;
         }
-
-        internal bool TryGetValue(ObjectInstance thisArg, out JsValue value)
-        {
-            value = JsValue.Undefined;
-
-            if (this == Undefined)
-            {
-                value = JsValue.Undefined;
-                return false;
-            }
-
-            if (IsDataDescriptor())
-            {
-                var val = Value;
-                if (val != null)
-                {
-                    value = val;
-                    return true;
-                }
-            }
-
-            if (Get != null && !Get.IsUndefined())
-            {
-                // if getter is not undefined it must be ICallable
-                var callable = Get.TryCast<ICallable>();
-                value = callable.Call(thisArg, Arguments.Empty);
-            }
-
-            return true;
-        }
     }
 }

+ 62 - 0
Jint/Runtime/Descriptors/PropertyDescriptorExtensions.cs

@@ -0,0 +1,62 @@
+using System.Runtime.CompilerServices;
+using Jint.Native;
+using Jint.Native.Object;
+
+namespace Jint.Runtime.Descriptors
+{
+    public static class PropertyDescriptorExtensions
+    {
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool IsAccessorDescriptor(this IPropertyDescriptor descriptor)
+        {
+            return descriptor.Get != null || descriptor.Set != null;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool IsDataDescriptor(this IPropertyDescriptor descriptor)
+        {
+            return descriptor.Writable.HasValue || descriptor.Value != null;
+        }
+
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.10.3
+        /// </summary>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool IsGenericDescriptor(this IPropertyDescriptor descriptor)
+        {
+            return !descriptor.IsDataDescriptor() && !descriptor.IsAccessorDescriptor();
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static bool TryGetValue(this IPropertyDescriptor descriptor, ObjectInstance thisArg, out JsValue value)
+        {
+            value = JsValue.Undefined;
+
+            if (descriptor == PropertyDescriptor.Undefined)
+            {
+                value = JsValue.Undefined;
+                return false;
+            }
+
+            if (descriptor.IsDataDescriptor())
+            {
+                var val = descriptor.Value;
+                if (val != null)
+                {
+                    value = val;
+                    return true;
+                }
+            }
+
+            if (descriptor.Get != null && !descriptor.Get.IsUndefined())
+            {
+                // if getter is not undefined it must be ICallable
+                var callable = descriptor.Get.TryCast<ICallable>();
+                value = callable.Call(thisArg, Arguments.Empty);
+            }
+
+            return true;
+        }
+    }
+}

+ 24 - 0
Jint/Runtime/Descriptors/Specialized/AllForbiddenPropertyDescriptor.cs

@@ -0,0 +1,24 @@
+using Jint.Native;
+
+namespace Jint.Runtime.Descriptors.Specialized
+{
+    /// <summary>
+    /// configurable = false, enumerable = false, writable = false.
+    /// </summary>
+    internal sealed class AllForbiddenPropertyDescriptor : IPropertyDescriptor
+    {
+        public AllForbiddenPropertyDescriptor(JsValue value)
+        {
+            Value = value;
+        }
+
+        public JsValue Get => null;
+        public JsValue Set => null;
+
+        public bool? Enumerable => false;
+        public bool? Writable => false;
+        public bool? Configurable => false;
+
+        public JsValue Value { get; set; }
+    }
+}

+ 33 - 10
Jint/Runtime/Descriptors/Specialized/ClrAccessDescriptor.cs

@@ -1,22 +1,45 @@
-using System;
-using Jint.Native;
+using Jint.Native;
+using Jint.Runtime.Environments;
 using Jint.Runtime.Interop;
 
 namespace Jint.Runtime.Descriptors.Specialized
 {
-    public sealed class ClrAccessDescriptor : PropertyDescriptor
+    internal sealed class ClrAccessDescriptor : IPropertyDescriptor
     {
-        public ClrAccessDescriptor(Engine engine, Func<JsValue, JsValue> get)
-            : this(engine, get, null)
+        private readonly EnvironmentRecord _env;
+        private readonly Engine _engine;
+        private readonly string _name;
+
+        private GetterFunctionInstance _get;
+        private SetterFunctionInstance _set;
+
+        public ClrAccessDescriptor(
+            EnvironmentRecord env,
+            Engine engine,
+            string name)
+        {
+            _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; }
+
+        private JsValue DoGet(JsValue n)
         {
+            return _env.GetBindingValue(_name, false);
         }
 
-        public ClrAccessDescriptor(Engine engine, Func<JsValue, JsValue> get, Action<JsValue, JsValue> set)
-            : base(
-                get: new GetterFunctionInstance(engine, get),
-                set: set == null ? Native.Undefined.Instance : new SetterFunctionInstance(engine, set)
-                )
+        private void DoSet(JsValue n, JsValue o)
         {
+            _env.SetMutableBinding(_name, o, true);
         }
     }
 }

+ 24 - 0
Jint/Runtime/Descriptors/Specialized/ConfigurableEnumerableWritablePropertyDescriptor.cs

@@ -0,0 +1,24 @@
+using Jint.Native;
+
+namespace Jint.Runtime.Descriptors.Specialized
+{
+    /// <summary>
+    /// configurable = true, enumerable = true, writable = true.
+    /// </summary>
+    internal sealed class ConfigurableEnumerableWritablePropertyDescriptor : IPropertyDescriptor
+    {
+        public ConfigurableEnumerableWritablePropertyDescriptor(JsValue value)
+        {
+            Value = value;
+        }
+
+        public JsValue Get => null;
+        public JsValue Set => null;
+
+        public bool? Enumerable => true;
+        public bool? Writable => true;
+        public bool? Configurable => true;
+
+        public JsValue Value { get; set; }
+    }
+}

+ 24 - 0
Jint/Runtime/Descriptors/Specialized/EnumerablePropertyDescriptor.cs

@@ -0,0 +1,24 @@
+using Jint.Native;
+
+namespace Jint.Runtime.Descriptors.Specialized
+{
+    /// <summary>
+    /// configurable = true, enumerable = true, writable = true.
+    /// </summary>
+    internal sealed class EnumerablePropertyDescriptor : IPropertyDescriptor
+    {
+        public EnumerablePropertyDescriptor(JsValue value)
+        {
+            Value = value;
+        }
+
+        public JsValue Get => null;
+        public JsValue Set => null;
+
+        public bool? Enumerable => true;
+        public bool? Writable => false;
+        public bool? Configurable => false;
+
+        public JsValue Value { get; set; }
+    }
+}

+ 24 - 0
Jint/Runtime/Descriptors/Specialized/NonConfigurablePropertyDescriptor.cs

@@ -0,0 +1,24 @@
+using Jint.Native;
+
+namespace Jint.Runtime.Descriptors.Specialized
+{
+    /// <summary>
+    /// configurable = false, enumerable = true, writable = true.
+    /// </summary>
+    internal sealed class NonConfigurablePropertyDescriptor : IPropertyDescriptor
+    {
+        public NonConfigurablePropertyDescriptor(JsValue value)
+        {
+            Value = value;
+        }
+
+        public JsValue Get => null;
+        public JsValue Set => null;
+
+        public bool? Enumerable => true;
+        public bool? Writable => true;
+        public bool? Configurable => false;
+
+        public JsValue Value { get; set; }
+    }
+}

+ 24 - 0
Jint/Runtime/Descriptors/Specialized/NonEnumerablePropertyDescriptor.cs

@@ -0,0 +1,24 @@
+using Jint.Native;
+
+namespace Jint.Runtime.Descriptors.Specialized
+{
+    /// <summary>
+    /// configurable = true, enumerable = false, writable  = true.
+    /// </summary>
+    internal sealed class NonEnumerablePropertyDescriptor : IPropertyDescriptor
+    {
+        public NonEnumerablePropertyDescriptor(JsValue value)
+        {
+            Value = value;
+        }
+
+        public JsValue Get => null;
+        public JsValue Set => null;
+
+        public bool? Enumerable => false;
+        public bool? Writable => true;
+        public bool? Configurable => true;
+
+        public JsValue Value { get; set; }
+    }
+}

+ 24 - 0
Jint/Runtime/Descriptors/Specialized/NullConfigurationPropertyDescriptor.cs

@@ -0,0 +1,24 @@
+using Jint.Native;
+
+namespace Jint.Runtime.Descriptors.Specialized
+{
+    /// <summary>
+    /// configurable = null, enumerable = null, writable = null.
+    /// </summary>
+    internal sealed class NullConfigurationPropertyDescriptor : IPropertyDescriptor
+    {
+        public NullConfigurationPropertyDescriptor(JsValue value)
+        {
+            Value = value;
+        }
+
+        public JsValue Get => null;
+        public JsValue Set => null;
+
+        public bool? Enumerable => null;
+        public bool? Writable => null;
+        public bool? Configurable => null;
+
+        public JsValue Value { get; set; }
+    }
+}

+ 24 - 0
Jint/Runtime/Descriptors/Specialized/WritablePropertyDescriptor.cs

@@ -0,0 +1,24 @@
+using Jint.Native;
+
+namespace Jint.Runtime.Descriptors.Specialized
+{
+    /// <summary>
+    /// configurable = false, enumerable = false, writable  = true.
+    /// </summary>
+    internal sealed class WritablePropertyDescriptor : IPropertyDescriptor
+    {
+        public WritablePropertyDescriptor(JsValue value)
+        {
+            Value = value;
+        }
+
+        public JsValue Get => null;
+        public JsValue Set => null;
+
+        public bool? Enumerable => false;
+        public bool? Writable => true;
+        public bool? Configurable => false;
+
+        public JsValue Value { get; set; }
+    }
+}

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

@@ -1,7 +1,9 @@
-using System.Linq;
+using System;
+using System.Linq;
 using Jint.Native;
 using Jint.Native.Object;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Runtime.Environments
 {
@@ -34,7 +36,11 @@ namespace Jint.Runtime.Environments
         /// <param name="configurable"></param>
         public override void CreateMutableBinding(string name, bool configurable = true)
         {
-            _bindingObject.DefineOwnProperty(name, new PropertyDescriptor(Undefined.Instance, true, true, configurable), true);
+            var propertyDescriptor = configurable
+                ? (IPropertyDescriptor) new ConfigurableEnumerableWritablePropertyDescriptor(Undefined.Instance)
+                : new NonConfigurablePropertyDescriptor(Undefined.Instance);
+
+            _bindingObject.SetOwnProperty(name, propertyDescriptor);
         }
 
         public override void SetMutableBinding(string name, JsValue value, bool strict)
@@ -81,7 +87,7 @@ namespace Jint.Runtime.Environments
                 return _bindingObject.GetOwnProperties().Select( x=> x.Key).ToArray();
             }
 
-            return new string[0];
+            return Array.Empty<string>();
         }
     }
 }

+ 3 - 2
Jint/Runtime/ExpressionIntepreter.cs

@@ -7,6 +7,7 @@ using Jint.Native;
 using Jint.Native.Function;
 using Jint.Native.Number;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Environments;
 using Jint.Runtime.Interop;
 using Jint.Runtime.References;
@@ -641,7 +642,7 @@ namespace Jint.Runtime
             {
                 var propName = property.Key.GetKey();
                 var previous = obj.GetOwnProperty(propName);
-                PropertyDescriptor propDesc;
+                IPropertyDescriptor propDesc;
 
                 switch (property.Kind)
                 {
@@ -649,7 +650,7 @@ namespace Jint.Runtime
                     case PropertyKind.Data:
                         var exprValue = _engine.EvaluateExpression(property.Value.As<Expression>());
                         var propValue = _engine.GetValue(exprValue);
-                        propDesc = new PropertyDescriptor(propValue, true, true, true);
+                        propDesc = new ConfigurableEnumerableWritablePropertyDescriptor(propValue);
                         break;
 
                     case PropertyKind.Get:

+ 2 - 1
Jint/Runtime/Interop/ClrFunctionInstance.cs

@@ -1,6 +1,7 @@
 using System;
 using Jint.Native;
 using Jint.Native.Function;
+using Jint.Runtime.Descriptors.Specialized;
 
 namespace Jint.Runtime.Interop
 {
@@ -16,7 +17,7 @@ namespace Jint.Runtime.Interop
         {
             _func = func;
             Prototype = engine.Function.PrototypeObject;
-            FastAddProperty("length", length, false, false, false);
+            SetOwnProperty("length", new AllForbiddenPropertyDescriptor(length));
             Extensible = true;
         }
 

+ 22 - 22
Jint/Runtime/Interop/NamespaceReference.cs

@@ -23,7 +23,7 @@ namespace Jint.Runtime.Interop
             _path = path;
         }
 
-        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
         {
             if (throwOnError)
             {
@@ -98,13 +98,13 @@ namespace Jint.Runtime.Interop
                 return TypeReference.CreateTypeReference(Engine, type);
             }
 
-            // in CoreCLR, for example, classes that used to be in 
+            // in CoreCLR, for example, classes that used to be in
             // mscorlib were moved away, and only stubs remained, because
             // of that, we do the search on the lookup assemblies first,
             // and only then in mscorlib. Probelm usage: System.IO.File.CreateText
-            
+
             // search in loaded assemblies
-            var lookupAssemblies = new[] { Assembly.GetCallingAssembly(), Assembly.GetExecutingAssembly() };
+            var lookupAssemblies = new[] {Assembly.GetCallingAssembly(), Assembly.GetExecutingAssembly()};
 
             foreach (var assembly in lookupAssemblies)
             {
@@ -130,14 +130,14 @@ namespace Jint.Runtime.Interop
                 var trimPath = path.Substring(0, lastPeriodPos);
                 type = GetType(assembly, trimPath);
                 if (type != null)
-                  foreach (Type nType in GetAllNestedTypes(type))
-                  {
-                    if (nType.FullName.Replace("+", ".").Equals(path.Replace("+", ".")))
+                    foreach (Type nType in GetAllNestedTypes(type))
                     {
-                      Engine.TypeCache.Add(path.Replace("+", "."), nType);
-                      return TypeReference.CreateTypeReference(Engine, nType);
+                        if (nType.FullName.Replace("+", ".").Equals(path.Replace("+", ".")))
+                        {
+                            Engine.TypeCache.Add(path.Replace("+", "."), nType);
+                            return TypeReference.CreateTypeReference(Engine, nType);
+                        }
                     }
-                  }
             }
 
             // search for type in mscorlib
@@ -160,7 +160,6 @@ namespace Jint.Runtime.Interop
         /// <param name="typeName"> Name of the type. </param>
         ///
         /// <returns>   The type. </returns>
-
         private static Type GetType(Assembly assembly, string typeName)
         {
             Type[] types = assembly.GetTypes();
@@ -171,27 +170,28 @@ namespace Jint.Runtime.Interop
                     return t;
                 }
             }
+
             return null;
         }
 
         private static IEnumerable<Type> GetAllNestedTypes(Type type)
         {
-          var types = new List<Type>();
-          AddNestedTypesRecursively(types, type);
-          return types.ToArray();
+            var types = new List<Type>();
+            AddNestedTypesRecursively(types, type);
+            return types.ToArray();
         }
 
         private static void AddNestedTypesRecursively(List<Type> types, Type type)
         {
-          Type[] nestedTypes = type.GetNestedTypes(BindingFlags.Public);
-          foreach (Type nestedType in nestedTypes)
-          {
-            types.Add(nestedType);
-            AddNestedTypesRecursively(types, nestedType);
-          }
+            Type[] nestedTypes = type.GetNestedTypes(BindingFlags.Public);
+            foreach (Type nestedType in nestedTypes)
+            {
+                types.Add(nestedType);
+                AddNestedTypesRecursively(types, nestedType);
+            }
         }
 
-      public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override IPropertyDescriptor GetOwnProperty(string propertyName)
         {
             return PropertyDescriptor.Undefined;
         }
@@ -201,4 +201,4 @@ namespace Jint.Runtime.Interop
             return "[Namespace: " + _path + "]";
         }
     }
-}
+}

+ 4 - 5
Jint/Runtime/Interop/ObjectWrapper.cs

@@ -50,10 +50,9 @@ namespace Jint.Runtime.Interop
             ownDesc.Value = value;
         }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override IPropertyDescriptor GetOwnProperty(string propertyName)
         {
-            PropertyDescriptor x;
-            if (TryGetProperty(propertyName, out x))
+            if (TryGetProperty(propertyName, out var x))
                 return x;
 
             var type = Target.GetType();
@@ -87,7 +86,7 @@ namespace Jint.Runtime.Interop
 
             if (methods.Any())
             {
-                var descriptor = new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, methods), false, true, false);
+                var descriptor = new EnumerablePropertyDescriptor(new MethodInfoFunctionInstance(Engine, methods));
                 AddProperty(propertyName, descriptor);
                 return descriptor;
             }
@@ -121,7 +120,7 @@ namespace Jint.Runtime.Interop
 
             if (explicitMethods.Length > 0)
             {
-                var descriptor = new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, explicitMethods), false, true, false);
+                var descriptor = new EnumerablePropertyDescriptor(new MethodInfoFunctionInstance(Engine, explicitMethods));
                 AddProperty(propertyName, descriptor);
                 return descriptor;
             }

+ 6 - 6
Jint/Runtime/Interop/TypeReference.cs

@@ -28,10 +28,10 @@ namespace Jint.Runtime.Interop
             // The value of the [[Prototype]] internal property of the TypeReference constructor is the Function prototype object
             obj.Prototype = engine.Function.PrototypeObject;
 
-            obj.FastAddProperty("length", 0, false, false, false);
+            obj.SetOwnProperty("length", new AllForbiddenPropertyDescriptor(0));
 
             // The initial value of Boolean.prototype is the Boolean prototype object
-            obj.FastAddProperty("prototype", engine.Object.PrototypeObject, false, false, false);
+            obj.SetOwnProperty("prototype", new AllForbiddenPropertyDescriptor(engine.Object.PrototypeObject));
 
             return obj;
         }
@@ -108,7 +108,7 @@ namespace Jint.Runtime.Interop
             return wrapper.Target.GetType() == this.Type;
         }
 
-        public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
         {
             if (throwOnError)
             {
@@ -157,7 +157,7 @@ namespace Jint.Runtime.Interop
             ownDesc.Value = value;
         }
 
-        public override PropertyDescriptor GetOwnProperty(string propertyName)
+        public override IPropertyDescriptor GetOwnProperty(string propertyName)
         {
             // todo: cache members locally
 
@@ -170,7 +170,7 @@ namespace Jint.Runtime.Interop
                 {
                     if (enumNames.GetValue(i) as string == propertyName)
                     {
-                        return new PropertyDescriptor((int)enumValues.GetValue(i), false, false, false);
+                        return new AllForbiddenPropertyDescriptor((int) enumValues.GetValue(i));
                     }
                 }
                 return PropertyDescriptor.Undefined;
@@ -198,7 +198,7 @@ namespace Jint.Runtime.Interop
                 return PropertyDescriptor.Undefined;
             }
 
-            return new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, methodInfo), false, false, false);
+            return new AllForbiddenPropertyDescriptor(new MethodInfoFunctionInstance(Engine, methodInfo));
         }
 
         public object Target