using Jint.Native; using Jint.Native.Object; namespace Jint.Runtime.Descriptors { public class PropertyDescriptor { public static PropertyDescriptor Undefined = new PropertyDescriptor(); public PropertyDescriptor() { } public PropertyDescriptor(JsValue value, bool? writable, bool? enumerable, bool? configurable) { Value = value; if (writable.HasValue) { Writable = writable.Value; } if (enumerable.HasValue) { Enumerable = enumerable.Value; } if (configurable.HasValue) { Configurable = configurable.Value; } } public PropertyDescriptor(JsValue get, JsValue set, bool? enumerable = null, bool? configurable = null) { Get = get; Set = set; if (enumerable.HasValue) { Enumerable = enumerable.Value; } if (configurable.HasValue) { Configurable = configurable.Value; } } public PropertyDescriptor(PropertyDescriptor descriptor) { Get = descriptor.Get; Set = descriptor.Set; Value = descriptor.Value; Enumerable = descriptor.Enumerable; Configurable = descriptor.Configurable; Writable = descriptor.Writable; } public JsValue Get { get; set; } public JsValue Set { get; set; } public bool? Enumerable { get; set; } public bool? Writable { get; set; } public bool? Configurable { get; set; } public virtual JsValue Value { get; set; } public bool IsAccessorDescriptor() { if (Get ==null && Set == null) { return false; } return true; } public bool IsDataDescriptor() { if (!Writable.HasValue && Value == null) { return false; } return true; } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.10.3 /// /// public bool IsGenericDescriptor() { return !IsDataDescriptor() && !IsAccessorDescriptor(); } public static PropertyDescriptor ToPropertyDescriptor(Engine engine, JsValue o) { var obj = o.TryCast(); if (obj == null) { throw new JavaScriptException(engine.TypeError); } if ((obj.HasProperty("value") || obj.HasProperty("writable")) && (obj.HasProperty("get") || obj.HasProperty("set"))) { throw new JavaScriptException(engine.TypeError); } var desc = new PropertyDescriptor(); if (obj.HasProperty("enumerable")) { desc.Enumerable = TypeConverter.ToBoolean(obj.Get("enumerable")); } if (obj.HasProperty("configurable")) { desc.Configurable = TypeConverter.ToBoolean(obj.Get("configurable")); } if (obj.HasProperty("value")) { var value = obj.Get("value"); desc.Value = value; } if (obj.HasProperty("writable")) { desc.Writable = TypeConverter.ToBoolean(obj.Get("writable")); } if (obj.HasProperty("get")) { var getter = obj.Get("get"); if (getter != JsValue.Undefined && getter.TryCast() == null) { throw new JavaScriptException(engine.TypeError); } desc.Get = getter; } if (obj.HasProperty("set")) { var setter = obj.Get("set"); if (setter != Native.Undefined.Instance && setter.TryCast() == null) { throw new JavaScriptException(engine.TypeError); } desc.Set = setter; } if (desc.Get != null || desc.Get != null) { if (desc.Value != null || desc.Writable.HasValue) { throw new JavaScriptException(engine.TypeError); } } return desc; } public static JsValue FromPropertyDescriptor(Engine engine, PropertyDescriptor desc) { if (desc == Undefined) { return Native.Undefined.Instance; } var obj = engine.Object.Construct(Arguments.Empty); 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); } 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.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); return obj; } } }