using System.Collections.Generic;
using System.Linq;
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 readonly Engine _engine;
private ObjectConstructor(Engine engine) : base(engine, null, null, false)
{
_engine = engine;
}
public static ObjectConstructor CreateObjectConstructor(Engine engine)
{
var obj = new ObjectConstructor(engine);
obj.Extensible = true;
obj.PrototypeObject = ObjectPrototype.CreatePrototypeObject(engine, obj);
obj.FastAddProperty("length", 1, false, false, false);
obj.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
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] == Null.Instance || arguments[0] == Undefined.Instance)
{
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 (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 (o == null)
{
throw new JavaScriptException(Engine.TypeError);
}
return o.Prototype ?? Null.Instance;
}
public JsValue GetOwnPropertyDescriptor(JsValue thisObject, JsValue[] arguments)
{
var oArg = arguments.At(0);
var o = oArg.TryCast();
if (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 (o == null)
{
throw new JavaScriptException(Engine.TypeError);
}
var array = Engine.Array.Construct(Arguments.Empty);
var n = 0;
var s = o as StringInstance;
if (s != null)
{
for (var i = 0; i < s.PrimitiveValue.AsString().Length; i++)
{
array.DefineOwnProperty(n.ToString(), new PropertyDescriptor(i.ToString(), true, true, true), false);
n++;
}
}
foreach (var p in o.GetOwnProperties())
{
array.DefineOwnProperty(n.ToString(), new PropertyDescriptor(p.Key, true, true, true), false);
n++;
}
return array;
}
public JsValue Create(JsValue thisObject, JsValue[] arguments)
{
var oArg = arguments.At(0);
var o = oArg.TryCast();
if (o == null && oArg != Null.Instance)
{
throw new JavaScriptException(Engine.TypeError);
}
var obj = Engine.Object.Construct(Arguments.Empty);
obj.Prototype = o;
var properties = arguments.At(1);
if (properties != Undefined.Instance)
{
DefineProperties(thisObject, new [] {obj, properties});
}
return obj;
}
public JsValue DefineProperty(JsValue thisObject, JsValue[] arguments)
{
var oArg = arguments.At(0);
var o = oArg.TryCast();
if (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 (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.HasValue || !p.Value.Enumerable.Value)
{
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 (o == null)
{
throw new JavaScriptException(Engine.TypeError);
}
foreach (var prop in o.GetOwnProperties())
{
if (prop.Value.Configurable.HasValue && prop.Value.Configurable.Value)
{
prop.Value.Configurable = false;
}
o.DefineOwnProperty(prop.Key, prop.Value, true);
}
o.Extensible = false;
return o;
}
public JsValue Freeze(JsValue thisObject, JsValue[] arguments)
{
var oArg = arguments.At(0);
var o = oArg.TryCast();
if (o == null)
{
throw new JavaScriptException(Engine.TypeError);
}
var keys = o.GetOwnProperties().Select(x => x.Key);
foreach (var p in keys)
{
var desc = o.GetOwnProperty(p);
if (desc.IsDataDescriptor())
{
if (desc.Writable.HasValue && desc.Writable.Value)
{
desc.Writable = false;
}
}
if (desc.Configurable.HasValue && desc.Configurable.Value)
{
desc.Configurable = false;
}
o.DefineOwnProperty(p, desc, true);
}
o.Extensible = false;
return o;
}
public JsValue PreventExtensions(JsValue thisObject, JsValue[] arguments)
{
var oArg = arguments.At(0);
var o = oArg.TryCast();
if (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 (o == null)
{
throw new JavaScriptException(Engine.TypeError);
}
foreach (var prop in o.GetOwnProperties())
{
if (prop.Value.Configurable.Value == true)
{
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 (o == null)
{
throw new JavaScriptException(Engine.TypeError);
}
foreach (var p in o.GetOwnProperties().Select(x => x.Key))
{
var desc = o.GetOwnProperty(p);
if (desc.IsDataDescriptor())
{
if (desc.Writable.HasValue && desc.Writable.Value)
{
return false;
}
}
if (desc.Configurable.HasValue && desc.Configurable.Value)
{
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 (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 (o == null)
{
throw new JavaScriptException(Engine.TypeError);
}
var enumerableProperties = o.GetOwnProperties()
.Where(x => x.Value.Enumerable.HasValue && x.Value.Enumerable.Value)
.ToArray();
var n = enumerableProperties.Length;
var array = Engine.Array.Construct(new JsValue[] {n});
var index = 0;
foreach (var prop in enumerableProperties)
{
var p = prop.Key;
array.DefineOwnProperty(
TypeConverter.ToString(index),
new PropertyDescriptor(p, true, true, true),
false);
index++;
}
return array;
}
}
}