using System.Numerics;
using Jint.Collections;
using Jint.Native.Function;
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;
namespace Jint.Native.BigInt;
///
/// https://tc39.es/ecma262/#sec-properties-of-the-bigint-constructor
///
internal sealed class BigIntConstructor : Constructor
{
private static readonly JsString _functionName = new("BigInt");
public BigIntConstructor(
Engine engine,
Realm realm,
FunctionPrototype functionPrototype,
ObjectPrototype objectPrototype)
: base(engine, realm, _functionName)
{
_prototype = functionPrototype;
PrototypeObject = new BigIntPrototype(engine, this, objectPrototype);
_length = new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.Configurable);
_prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
}
protected override void Initialize()
{
var properties = new PropertyDictionary(2, checkExistingKeys: false)
{
["asIntN"] = new(new ClrFunction(Engine, "asIntN", AsIntN, 2, PropertyFlag.Configurable), true, false, true),
["asUintN"] = new(new ClrFunction(Engine, "asUintN", AsUintN, 2, PropertyFlag.Configurable), true, false, true),
};
SetProperties(properties);
}
///
/// https://tc39.es/ecma262/#sec-bigint.asintn
///
private JsValue AsIntN(JsValue thisObject, JsValue[] arguments)
{
var bits = (int) TypeConverter.ToIndex(_realm, arguments.At(0));
var bigint = arguments.At(1).ToBigInteger(_engine);
var mod = TypeConverter.BigIntegerModulo(bigint, BigInteger.Pow(2, bits));
if (bits > 0 && mod >= BigInteger.Pow(2, bits - 1))
{
return (mod - BigInteger.Pow(2, bits));
}
return mod;
}
///
/// https://tc39.es/ecma262/#sec-bigint.asuintn
///
private JsValue AsUintN(JsValue thisObject, JsValue[] arguments)
{
var bits = (int) TypeConverter.ToIndex(_realm, arguments.At(0));
var bigint = arguments.At(1).ToBigInteger(_engine);
var result = TypeConverter.BigIntegerModulo(bigint, BigInteger.Pow(2, bits));
return result;
}
protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments)
{
if (arguments.Length == 0)
{
return JsBigInt.Zero;
}
var prim = TypeConverter.ToPrimitive(arguments.At(0), Types.Number);
if (prim.IsNumber())
{
return NumberToBigInt((JsNumber) prim);
}
return prim.ToBigInteger(_engine);
}
///
/// https://tc39.es/ecma262/#sec-numbertobigint
///
private JsBigInt NumberToBigInt(JsNumber value)
{
if (TypeConverter.IsIntegralNumber(value._value))
{
return JsBigInt.Create((long) value._value);
}
ExceptionHelper.ThrowRangeError(_realm, "The number " + value + " cannot be converted to a BigInt because it is not an integer");
return null;
}
///
/// https://tc39.es/ecma262/#sec-bigint-constructor-number-value
///
public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
{
var value = arguments.Length > 0
? JsBigInt.Create(arguments[0].ToBigInteger(_engine))
: JsBigInt.Zero;
if (newTarget.IsUndefined())
{
return Construct(value);
}
var o = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.BigInt.PrototypeObject,
static (engine, realm, state) => new BigIntInstance(engine, state!),
value);
return o;
}
public BigIntPrototype PrototypeObject { get; }
public BigIntInstance Construct(JsBigInt value)
{
var instance = new BigIntInstance(Engine, value)
{
_prototype = PrototypeObject
};
return instance;
}
}