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.ArrayBuffer; /// /// https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-constructor /// public sealed class ArrayBufferConstructor : Constructor { private static readonly JsString _functionName = new("ArrayBuffer"); internal ArrayBufferConstructor( Engine engine, Realm realm, FunctionPrototype functionPrototype, ObjectPrototype objectPrototype) : base(engine, realm, _functionName) { _prototype = functionPrototype; PrototypeObject = new ArrayBufferPrototype(engine, this, objectPrototype); _length = new PropertyDescriptor(1, PropertyFlag.Configurable); _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden); } internal ArrayBufferPrototype PrototypeObject { get; } protected override void Initialize() { const PropertyFlag lengthFlags = PropertyFlag.Configurable; var properties = new PropertyDictionary(1, checkExistingKeys: false) { ["isView"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunction(Engine, "isView", IsView, 1, lengthFlags), PropertyFlag.Configurable | PropertyFlag.Writable)), }; SetProperties(properties); var symbols = new SymbolDictionary(1) { [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunction(Engine, "get [Symbol.species]", Species, 0, lengthFlags), set: Undefined, PropertyFlag.Configurable), }; SetSymbols(symbols); } /// /// Constructs a new JsArrayBuffer instance and takes ownership of the given byte array and uses it as backing store. /// public JsArrayBuffer Construct(byte[] data) { return CreateJsArrayBuffer(this, data, byteLength: (ulong) data.Length, maxByteLength: null); } /// /// Constructs a new JsArrayBuffer with given byte length and optional max byte length. /// public JsArrayBuffer Construct(ulong byteLength, uint? maxByteLength = null) { return AllocateArrayBuffer(this, byteLength, maxByteLength); } public override ObjectInstance Construct(JsCallArguments arguments, JsValue newTarget) { if (newTarget.IsUndefined()) { Throw.TypeError(_realm); } var length = arguments.At(0); var options = arguments.At(1); var byteLength = TypeConverter.ToIndex(_realm, length); var requestedMaxByteLength = options.GetArrayBufferMaxByteLengthOption(); return AllocateArrayBuffer(newTarget, byteLength, requestedMaxByteLength); } /// /// https://tc39.es/ecma262/#sec-get-arraybuffer-@@species /// private static JsValue Species(JsValue thisObject, JsCallArguments arguments) { return thisObject; } /// /// https://tc39.es/ecma262/#sec-arraybuffer.isview /// private static JsValue IsView(JsValue thisObject, JsCallArguments arguments) { var arg = arguments.At(0); return arg is JsDataView or JsTypedArray; } /// /// https://tc39.es/ecma262/#sec-allocatearraybuffer /// internal JsArrayBuffer AllocateArrayBuffer(JsValue constructor, ulong byteLength, uint? maxByteLength = null) { var allocatingResizableBuffer = maxByteLength != null; if (allocatingResizableBuffer && byteLength > maxByteLength) { Throw.RangeError(_realm); } return CreateJsArrayBuffer(constructor, block: null, byteLength, maxByteLength); } private JsArrayBuffer CreateJsArrayBuffer(JsValue constructor, byte[]? block, ulong byteLength, uint? maxByteLength) { var obj = OrdinaryCreateFromConstructor( constructor, static intrinsics => intrinsics.ArrayBuffer.PrototypeObject, static (engine, _, state) => { var buffer = new JsArrayBuffer(engine, [], state.MaxByteLength) { _arrayBufferData = state.Block ?? (state.ByteLength > 0 ? JsArrayBuffer.CreateByteDataBlock(engine.Realm, state.ByteLength) : []), }; return buffer; }, new ConstructState(block, byteLength, maxByteLength)); return obj; } private readonly record struct ConstructState(byte[]? Block, ulong ByteLength, uint? MaxByteLength); }