using System.Collections.Generic; using System.Linq; using Jint.Native.Array; using Jint.Native.Function; using Jint.Native.String; using Jint.Runtime; using Jint.Runtime.Descriptors; using Jint.Runtime.Interop; namespace Jint.Native.Object { public sealed class ObjectConstructor : FunctionInstance, IConstructor { private ObjectConstructor(Engine engine) : base(engine, null, null, false) { } public static ObjectConstructor CreateObjectConstructor(Engine engine) { var obj = new ObjectConstructor(engine); obj.Extensible = true; obj.PrototypeObject = ObjectPrototype.CreatePrototypeObject(engine, obj); obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden)); obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden)); return obj; } public void Configure() { Prototype = Engine.Function.PrototypeObject; FastAddProperty("getPrototypeOf", new ClrFunctionInstance(Engine, GetPrototypeOf, 1), true, false, true); FastAddProperty("getOwnPropertyDescriptor", new ClrFunctionInstance(Engine, GetOwnPropertyDescriptor, 2), true, false, true); FastAddProperty("getOwnPropertyNames", new ClrFunctionInstance(Engine, GetOwnPropertyNames, 1), true, false, true); FastAddProperty("create", new ClrFunctionInstance(Engine, Create, 2), true, false, true); FastAddProperty("defineProperty", new ClrFunctionInstance(Engine, DefineProperty, 3), true, false, true); FastAddProperty("defineProperties", new ClrFunctionInstance(Engine, DefineProperties, 2), true, false, true); FastAddProperty("seal", new ClrFunctionInstance(Engine, Seal, 1), true, false, true); FastAddProperty("freeze", new ClrFunctionInstance(Engine, Freeze, 1), true, false, true); FastAddProperty("preventExtensions", new ClrFunctionInstance(Engine, PreventExtensions, 1), true, false, true); FastAddProperty("isSealed", new ClrFunctionInstance(Engine, IsSealed, 1), true, false, true); FastAddProperty("isFrozen", new ClrFunctionInstance(Engine, IsFrozen, 1), true, false, true); FastAddProperty("isExtensible", new ClrFunctionInstance(Engine, IsExtensible, 1), true, false, true); FastAddProperty("keys", new ClrFunctionInstance(Engine, Keys, 1), true, false, true); } public ObjectPrototype PrototypeObject { get; private set; } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.1.1 /// /// /// /// public override JsValue Call(JsValue thisObject, JsValue[] arguments) { if (arguments.Length == 0) { return Construct(arguments); } if(arguments[0].IsNull() || arguments[0].IsUndefined()) { return Construct(arguments); } return TypeConverter.ToObject(_engine, arguments[0]); } /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.2.1 /// /// /// public ObjectInstance Construct(JsValue[] arguments) { if (arguments.Length > 0) { var value = arguments[0]; var valueObj = value.TryCast(); if (!ReferenceEquals(valueObj, null)) { return valueObj; } var type = value.Type; if (type == Types.String || type == Types.Number || type == Types.Boolean) { return TypeConverter.ToObject(_engine, value); } } var obj = new ObjectInstance(_engine) { Extensible = true, Prototype = Engine.Object.PrototypeObject }; return obj; } public JsValue GetPrototypeOf(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null)) { throw new JavaScriptException(Engine.TypeError); } return o.Prototype ?? Null; } public JsValue GetOwnPropertyDescriptor(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null)) { throw new JavaScriptException(Engine.TypeError); } var p = arguments.At(1); var name = TypeConverter.ToString(p); var desc = o.GetOwnProperty(name); return PropertyDescriptor.FromPropertyDescriptor(Engine, desc); } public JsValue GetOwnPropertyNames(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null)) { throw new JavaScriptException(Engine.TypeError); } uint n = 0; ArrayInstance array = null; var ownProperties = o.GetOwnProperties().ToList(); if (o is StringInstance s) { var length = s.PrimitiveValue.AsStringWithoutTypeCheck().Length; array = Engine.Array.Construct(ownProperties.Count + length); for (var i = 0; i < length; i++) { array.SetIndexValue(n, TypeConverter.ToString(i), updateLength: false); n++; } } array = array ?? Engine.Array.ConstructFast((uint) ownProperties.Count); for (var i = 0; i < ownProperties.Count; i++) { var p = ownProperties[i]; array.SetIndexValue(n, p.Key, false); n++; } array.SetLength(n); return array; } public JsValue Create(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null) && !oArg.IsNull()) { throw new JavaScriptException(Engine.TypeError); } var obj = Engine.Object.Construct(Arguments.Empty); obj.Prototype = o; var properties = arguments.At(1); if (!properties.IsUndefined()) { DefineProperties(thisObject, new [] {obj, properties}); } return obj; } public JsValue DefineProperty(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null)) { throw new JavaScriptException(Engine.TypeError); } var p = arguments.At(1); var name = TypeConverter.ToString(p); var attributes = arguments.At(2); var desc = PropertyDescriptor.ToPropertyDescriptor(Engine, attributes); o.DefineOwnProperty(name, desc, true); return o; } public JsValue DefineProperties(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null)) { throw new JavaScriptException(Engine.TypeError); } var properties = arguments.At(1); var props = TypeConverter.ToObject(Engine, properties); var descriptors = new List>(); foreach (var p in props.GetOwnProperties()) { if (!p.Value.Enumerable) { continue; } var descObj = props.Get(p.Key); var desc = PropertyDescriptor.ToPropertyDescriptor(Engine, descObj); descriptors.Add(new KeyValuePair(p.Key, desc)); } foreach (var pair in descriptors) { o.DefineOwnProperty(pair.Key, pair.Value, true); } return o; } public JsValue Seal(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null)) { throw new JavaScriptException(Engine.TypeError); } var properties = new List>(o.GetOwnProperties()); foreach (var prop in properties) { var propertyDescriptor = prop.Value; if (propertyDescriptor.Configurable) { propertyDescriptor.Configurable = false; FastSetProperty(prop.Key, propertyDescriptor); } o.DefineOwnProperty(prop.Key, propertyDescriptor, true); } o.Extensible = false; return o; } public JsValue Freeze(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null)) { throw new JavaScriptException(Engine.TypeError); } var properties = new List>(o.GetOwnProperties()); foreach (var p in properties) { var desc = o.GetOwnProperty(p.Key); if (desc.IsDataDescriptor()) { if (desc.Writable) { var mutable = desc as PropertyDescriptor ?? new PropertyDescriptor(desc); mutable.Writable = false; desc = mutable; } } if (desc.Configurable) { var mutable = desc as PropertyDescriptor ?? new PropertyDescriptor(desc); mutable.Configurable = false; desc = mutable; } o.DefineOwnProperty(p.Key, desc, true); } o.Extensible = false; return o; } public JsValue PreventExtensions(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null)) { throw new JavaScriptException(Engine.TypeError); } o.Extensible = false; return o; } public JsValue IsSealed(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null)) { throw new JavaScriptException(Engine.TypeError); } foreach (var prop in o.GetOwnProperties()) { if (prop.Value.Configurable) { return false; } } if (o.Extensible == false) { return true; } return false; } public JsValue IsFrozen(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null)) { throw new JavaScriptException(Engine.TypeError); } foreach (var pair in o.GetOwnProperties()) { var desc = pair.Value; if (desc.IsDataDescriptor()) { if (desc.Writable) { return false; } } if (desc.Configurable) { return false; } } if (o.Extensible == false) { return true; } return false; } public JsValue IsExtensible(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null)) { throw new JavaScriptException(Engine.TypeError); } return o.Extensible; } public JsValue Keys(JsValue thisObject, JsValue[] arguments) { var oArg = arguments.At(0); var o = oArg.TryCast(); if (ReferenceEquals(o, null)) { throw new JavaScriptException(Engine.TypeError); } var enumerableProperties = o.GetOwnProperties() .Where(x => x.Value.Enumerable) .ToArray(); var n = enumerableProperties.Length; var args = _engine.JsValueArrayPool.RentArray(1); args[0] = n; var array = Engine.Array.Construct(args, (uint) n); _engine.JsValueArrayPool.ReturnArray(args); uint index = 0; foreach (var prop in enumerableProperties) { var p = prop.Key; array.SetIndexValue(index, p, updateLength: false); index++; } array.SetLength(index); return array; } } }