#pragma warning disable CA1859 // Use concrete types when possible for improved performance -- most of prototype methods return JsValue
using Jint.Native.Function;
using Jint.Native.Object;
using Jint.Native.Symbol;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;
namespace Jint.Native.Reflect;
///
/// https://www.ecma-international.org/ecma-262/6.0/index.html#sec-reflect-object
///
internal sealed class ReflectInstance : ObjectInstance
{
private readonly Realm _realm;
internal ReflectInstance(
Engine engine,
Realm realm,
ObjectPrototype objectPrototype) : base(engine)
{
_realm = realm;
_prototype = objectPrototype;
}
protected override void Initialize()
{
var properties = new PropertyDictionary(14, checkExistingKeys: false)
{
["apply"] = new PropertyDescriptor(new ClrFunction(Engine, "apply", Apply, 3, PropertyFlag.Configurable), true, false, true),
["construct"] = new PropertyDescriptor(new ClrFunction(Engine, "construct", Construct, 2, PropertyFlag.Configurable), true, false, true),
["defineProperty"] = new PropertyDescriptor(new ClrFunction(Engine, "defineProperty", DefineProperty, 3, PropertyFlag.Configurable), true, false, true),
["deleteProperty"] = new PropertyDescriptor(new ClrFunction(Engine, "deleteProperty", DeleteProperty, 2, PropertyFlag.Configurable), true, false, true),
["get"] = new PropertyDescriptor(new ClrFunction(Engine, "get", Get, 2, PropertyFlag.Configurable), true, false, true),
["getOwnPropertyDescriptor"] = new PropertyDescriptor(new ClrFunction(Engine, "getOwnPropertyDescriptor", GetOwnPropertyDescriptor, 2, PropertyFlag.Configurable), true, false, true),
["getPrototypeOf"] = new PropertyDescriptor(new ClrFunction(Engine, "getPrototypeOf", GetPrototypeOf, 1, PropertyFlag.Configurable), true, false, true),
["has"] = new PropertyDescriptor(new ClrFunction(Engine, "has", Has, 2, PropertyFlag.Configurable), true, false, true),
["isExtensible"] = new PropertyDescriptor(new ClrFunction(Engine, "isExtensible", IsExtensible, 1, PropertyFlag.Configurable), true, false, true),
["ownKeys"] = new PropertyDescriptor(new ClrFunction(Engine, "ownKeys", OwnKeys, 1, PropertyFlag.Configurable), true, false, true),
["preventExtensions"] = new PropertyDescriptor(new ClrFunction(Engine, "preventExtensions", PreventExtensions, 1, PropertyFlag.Configurable), true, false, true),
["set"] = new PropertyDescriptor(new ClrFunction(Engine, "set", Set, 3, PropertyFlag.Configurable), true, false, true),
["setPrototypeOf"] = new PropertyDescriptor(new ClrFunction(Engine, "setPrototypeOf", SetPrototypeOf, 2, PropertyFlag.Configurable), true, false, true),
};
SetProperties(properties);
var symbols = new SymbolDictionary(1)
{
[GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("Reflect", false, false, true)
};
SetSymbols(symbols);
}
private JsValue Apply(JsValue thisObject, JsCallArguments arguments)
{
var target = arguments.At(0);
var thisArgument = arguments.At(1);
var argumentsList = arguments.At(2);
if (!target.IsCallable)
{
Throw.TypeError(_realm);
}
var args = FunctionPrototype.CreateListFromArrayLike(_realm, argumentsList);
// 3. Perform PrepareForTailCall().
return ((ICallable) target).Call(thisArgument, args);
}
///
/// https://tc39.es/ecma262/#sec-reflect.construct
///
private JsValue Construct(JsValue thisObject, JsCallArguments arguments)
{
var target = AssertConstructor(_engine, arguments.At(0));
var newTargetArgument = arguments.At(2, arguments[0]);
AssertConstructor(_engine, newTargetArgument);
var args = FunctionPrototype.CreateListFromArrayLike(_realm, arguments.At(1));
return target.Construct(args, newTargetArgument);
}
///
/// https://tc39.es/ecma262/#sec-reflect.defineproperty
///
private JsValue DefineProperty(JsValue thisObject, JsCallArguments arguments)
{
var target = arguments.At(0) as ObjectInstance;
if (target is null)
{
Throw.TypeError(_realm, "Reflect.defineProperty called on non-object");
}
var propertyKey = arguments.At(1);
var attributes = arguments.At(2);
var key = TypeConverter.ToPropertyKey(propertyKey);
var desc = PropertyDescriptor.ToPropertyDescriptor(_realm, attributes);
return target.DefineOwnProperty(key, desc);
}
private JsValue DeleteProperty(JsValue thisObject, JsCallArguments arguments)
{
var o = arguments.At(0) as ObjectInstance;
if (o is null)
{
Throw.TypeError(_realm, "Reflect.deleteProperty called on non-object");
}
var property = TypeConverter.ToPropertyKey(arguments.At(1));
return o.Delete(property) ? JsBoolean.True : JsBoolean.False;
}
private JsValue Has(JsValue thisObject, JsCallArguments arguments)
{
var o = arguments.At(0) as ObjectInstance;
if (o is null)
{
Throw.TypeError(_realm, "Reflect.has called on non-object");
}
var property = TypeConverter.ToPropertyKey(arguments.At(1));
return o.HasProperty(property) ? JsBoolean.True : JsBoolean.False;
}
private JsValue Set(JsValue thisObject, JsCallArguments arguments)
{
var target = arguments.At(0);
var property = TypeConverter.ToPropertyKey(arguments.At(1));
var value = arguments.At(2);
var receiver = arguments.At(3, target);
var o = target as ObjectInstance;
if (o is null)
{
Throw.TypeError(_realm, "Reflect.set called on non-object");
}
return o.Set(property, value, receiver);
}
private JsValue Get(JsValue thisObject, JsCallArguments arguments)
{
var target = arguments.At(0);
var o = target as ObjectInstance;
if (o is null)
{
Throw.TypeError(_realm, "Reflect.get called on non-object");
}
var receiver = arguments.At(2, target);
var property = TypeConverter.ToPropertyKey(arguments.At(1));
return o.Get(property, receiver);
}
private JsValue GetOwnPropertyDescriptor(JsValue thisObject, JsCallArguments arguments)
{
if (!arguments.At(0).IsObject())
{
Throw.TypeError(_realm, "Reflect.getOwnPropertyDescriptor called on non-object");
}
return _realm.Intrinsics.Object.GetOwnPropertyDescriptor(Undefined, arguments);
}
private JsValue OwnKeys(JsValue thisObject, JsCallArguments arguments)
{
var o = arguments.At(0) as ObjectInstance;
if (o is null)
{
Throw.TypeError(_realm, "Reflect.get called on non-object");
}
var keys = o.GetOwnPropertyKeys();
return _realm.Intrinsics.Array.CreateArrayFromList(keys);
}
private JsValue IsExtensible(JsValue thisObject, JsCallArguments arguments)
{
var o = arguments.At(0) as ObjectInstance;
if (o is null)
{
Throw.TypeError(_realm, "Reflect.isExtensible called on non-object");
}
return o.Extensible;
}
private JsValue PreventExtensions(JsValue thisObject, JsCallArguments arguments)
{
var o = arguments.At(0) as ObjectInstance;
if (o is null)
{
Throw.TypeError(_realm, "Reflect.preventExtensions called on non-object");
}
return o.PreventExtensions();
}
private JsValue GetPrototypeOf(JsValue thisObject, JsCallArguments arguments)
{
var target = arguments.At(0);
if (!target.IsObject())
{
Throw.TypeError(_realm, "Reflect.getPrototypeOf called on non-object");
}
return _realm.Intrinsics.Object.GetPrototypeOf(Undefined, arguments);
}
private JsValue SetPrototypeOf(JsValue thisObject, JsCallArguments arguments)
{
var target = arguments.At(0);
var o = target as ObjectInstance;
if (o is null)
{
Throw.TypeError(_realm, "Reflect.setPrototypeOf called on non-object");
}
var prototype = arguments.At(1);
if (!prototype.IsObject() && !prototype.IsNull())
{
Throw.TypeError(_realm, $"Object prototype may only be an Object or null: {prototype}");
}
return o.SetPrototypeOf(prototype);
}
}