123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- using System.Globalization;
- using System.Runtime.CompilerServices;
- using Jint.Native.ArrayBuffer;
- using Jint.Native.Number;
- using Jint.Native.Object;
- using Jint.Native.TypedArray;
- using Jint.Runtime;
- using Jint.Runtime.Descriptors;
- namespace Jint.Native;
- public sealed class JsTypedArray : ObjectInstance
- {
- internal const uint LengthAuto = uint.MaxValue;
- internal readonly TypedArrayContentType _contentType;
- internal readonly TypedArrayElementType _arrayElementType;
- internal JsArrayBuffer _viewedArrayBuffer;
- internal uint _byteLength;
- internal int _byteOffset;
- private readonly Intrinsics _intrinsics;
- internal uint _arrayLength;
- internal JsTypedArray(
- Engine engine,
- Intrinsics intrinsics,
- TypedArrayElementType type,
- uint length) : base(engine)
- {
- _intrinsics = intrinsics;
- _viewedArrayBuffer = new JsArrayBuffer(engine, []);
- _arrayElementType = type;
- _contentType = type != TypedArrayElementType.BigInt64 && type != TypedArrayElementType.BigUint64
- ? TypedArrayContentType.Number
- : TypedArrayContentType.BigInt;
- _arrayLength = length;
- }
- public JsValue this[int index]
- {
- get => IntegerIndexedElementGet(index);
- set => IntegerIndexedElementSet(index, value);
- }
- public JsValue this[uint index]
- {
- get => IntegerIndexedElementGet(index);
- set => IntegerIndexedElementSet(index, value);
- }
- public uint Length => GetLength();
- internal override uint GetLength()
- {
- var record = IntrinsicTypedArrayPrototype.MakeTypedArrayWithBufferWitnessRecord(this, ArrayBufferOrder.Unordered);
- return record.IsTypedArrayOutOfBounds ? 0 : record.TypedArrayLength;
- }
- public override bool PreventExtensions()
- {
- if (!IsTypedArrayFixedLength)
- {
- return false;
- }
- return base.PreventExtensions();
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-istypedarrayfixedlength
- /// </summary>
- private bool IsTypedArrayFixedLength
- {
- get
- {
- if (_arrayLength == LengthAuto)
- {
- return false;
- }
- var buffer = _viewedArrayBuffer;
- if (!buffer.IsFixedLengthArrayBuffer && !buffer.IsSharedArrayBuffer)
- {
- return false;
- }
- return true;
- }
- }
- internal override bool IsArrayLike => true;
- internal override bool IsIntegerIndexedArray => true;
- /// <summary>
- /// https://tc39.es/ecma262/#sec-allocatetypedarraybuffer
- /// </summary>
- internal void AllocateTypedArrayBuffer(ulong len)
- {
- var elementSize = _arrayElementType.GetElementSize();
- var byteLength = elementSize * len;
- var data = _intrinsics.ArrayBuffer.AllocateArrayBuffer(_intrinsics.ArrayBuffer, byteLength);
- _byteLength = (uint) byteLength;
- _arrayLength = (uint) len;
- _viewedArrayBuffer = data;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal bool HasProperty(long numericIndex)
- {
- return IsValidIntegerIndex(numericIndex);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-hasproperty-p
- /// </summary>
- public override bool HasProperty(JsValue property)
- {
- var numericIndex = TypeConverter.CanonicalNumericIndexString(property);
- if (numericIndex is not null)
- {
- return IsValidIntegerIndex(numericIndex.Value);
- }
- return base.HasProperty(property);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-getownproperty-p
- /// </summary>
- public override PropertyDescriptor GetOwnProperty(JsValue property)
- {
- var numericIndex = TypeConverter.CanonicalNumericIndexString(property);
- if (numericIndex is not null)
- {
- var value = IntegerIndexedElementGet(numericIndex.Value);
- if (value.IsUndefined())
- {
- return PropertyDescriptor.Undefined;
- }
- return new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
- }
- return base.GetOwnProperty(property);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-get-p-receiver
- /// </summary>
- public override JsValue Get(JsValue property, JsValue receiver)
- {
- var numericIndex = TypeConverter.CanonicalNumericIndexString(property);
- if (numericIndex is not null)
- {
- return IntegerIndexedElementGet(numericIndex.Value);
- }
- return base.Get(property, receiver);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver
- /// </summary>
- public override bool Set(JsValue property, JsValue value, JsValue receiver)
- {
- var numericIndex = TypeConverter.CanonicalNumericIndexString(property);
- if (numericIndex is not null)
- {
- if (ReferenceEquals(this, receiver))
- {
- IntegerIndexedElementSet(numericIndex.Value, value);
- return true;
- }
- if (!IsValidIntegerIndex(numericIndex.Value))
- {
- return true;
- }
- }
- return base.Set(property, value, receiver);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-defineownproperty-p-desc
- /// </summary>
- public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
- {
- var numericIndex = TypeConverter.CanonicalNumericIndexString(property);
- if (numericIndex is not null)
- {
- if (!IsValidIntegerIndex(numericIndex.Value))
- {
- return false;
- }
- if (desc is { ConfigurableSet: true, Configurable: false })
- {
- return false;
- }
- if (desc is { EnumerableSet: true, Enumerable: false })
- {
- return false;
- }
- if (desc.IsAccessorDescriptor())
- {
- return false;
- }
- if (desc is { WritableSet: true, Writable: false })
- {
- return false;
- }
- IntegerIndexedElementSet(numericIndex.Value, desc.Value);
- return true;
- }
- return base.DefineOwnProperty(property, desc);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-ownpropertykeys
- /// </summary>
- public override List<JsValue> GetOwnPropertyKeys(Types types = Types.Empty | Types.String | Types.Symbol)
- {
- var taRecord = IntrinsicTypedArrayPrototype.MakeTypedArrayWithBufferWitnessRecord(this, ArrayBufferOrder.SeqCst);
- var keys = new List<JsValue>();
- if (!taRecord.IsTypedArrayOutOfBounds)
- {
- var length = GetLength();
- for (uint i = 0; i < length; ++i)
- {
- keys.Add(JsString.Create(i));
- }
- }
- if (_properties is not null)
- {
- foreach (var pair in _properties)
- {
- keys.Add(pair.Key.Name);
- }
- }
- if (_symbols is not null)
- {
- foreach (var pair in _symbols)
- {
- keys.Add(pair.Key);
- }
- }
- return keys;
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-delete-p
- /// </summary>
- public override bool Delete(JsValue property)
- {
- var numericIndex = TypeConverter.CanonicalNumericIndexString(property);
- if (numericIndex is not null)
- {
- return !IsValidIntegerIndex(numericIndex.Value);
- }
- return base.Delete(property);
- }
- // helper to prevent floating points
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private JsValue IntegerIndexedElementGet(int index)
- {
- if (!IsValidIntegerIndex(index))
- {
- return Undefined;
- }
- return DoIntegerIndexedElementGet(index);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-integerindexedelementget
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private JsValue IntegerIndexedElementGet(double index)
- {
- if (!IsValidIntegerIndex(index))
- {
- return Undefined;
- }
- return DoIntegerIndexedElementGet((int) index);
- }
- private JsValue DoIntegerIndexedElementGet(int index)
- {
- var offset = _byteOffset;
- var elementType = _arrayElementType;
- var elementSize = elementType.GetElementSize();
- var indexedPosition = index * elementSize + offset;
- var value = _viewedArrayBuffer.GetValueFromBuffer(indexedPosition, elementType, isTypedArray: true, ArrayBufferOrder.Unordered);
- if (value.Type == Types.Number)
- {
- return _arrayElementType.FitsInt32()
- ? JsNumber.Create((int) value.DoubleValue)
- : JsNumber.Create(value.DoubleValue);
- }
- return JsBigInt.Create(value.BigInteger);
- }
- // helper tot prevent floating point
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void IntegerIndexedElementSet(int index, JsValue value)
- {
- TypedArrayValue numValue = _contentType != TypedArrayContentType.BigInt
- ? TypeConverter.ToNumber(value)
- : value.ToBigInteger(_engine);
- if (IsValidIntegerIndex(index))
- {
- DoIntegerIndexedElementSet(index, numValue);
- }
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-integerindexedelementset
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void IntegerIndexedElementSet(double index, JsValue value)
- {
- if (_contentType != TypedArrayContentType.BigInt)
- {
- var numValue = TypeConverter.ToNumber(value);
- if (IsValidIntegerIndex(index))
- {
- DoIntegerIndexedElementSet((int) index, numValue);
- }
- }
- else
- {
- try
- {
- var numValue = TypeConverter.ToBigInt(value);
- if (IsValidIntegerIndex(index))
- {
- DoIntegerIndexedElementSet((int) index, numValue);
- }
- }
- catch (ParseErrorException ex)
- {
- Throw.SyntaxError(_engine.Realm, ex.Message);
- }
- }
- }
- internal void DoIntegerIndexedElementSet(int index, TypedArrayValue numValue)
- {
- var offset = _byteOffset;
- var elementType = _arrayElementType;
- var elementSize = elementType.GetElementSize();
- var indexedPosition = index * elementSize + offset;
- _viewedArrayBuffer.SetValueInBuffer(indexedPosition, elementType, numValue, true, ArrayBufferOrder.Unordered);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-isvalidintegerindex
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal bool IsValidIntegerIndex(double index)
- {
- if (_viewedArrayBuffer.IsDetachedBuffer)
- {
- return false;
- }
- if (!TypeConverter.IsIntegralNumber(index))
- {
- return false;
- }
- if (NumberInstance.IsNegativeZero(index))
- {
- return false;
- }
- return IsValidIntegerIndex((int) index);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-isvalidintegerindex
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal bool IsValidIntegerIndex(int index)
- {
- if (_viewedArrayBuffer.IsDetachedBuffer)
- {
- return false;
- }
- var taRecord = IntrinsicTypedArrayPrototype.MakeTypedArrayWithBufferWitnessRecord(this, ArrayBufferOrder.Unordered);
- if (taRecord.IsTypedArrayOutOfBounds)
- {
- return false;
- }
- var length = taRecord.TypedArrayLength;
- if (index < 0 || index >= length)
- {
- return false;
- }
- return true;
- }
- internal T[] ToNativeArray<T>()
- {
- var conversionType = typeof(T);
- var elementSize = _arrayElementType.GetElementSize();
- var byteOffset = _byteOffset;
- var buffer = _viewedArrayBuffer;
- var array = new T[GetLength()];
- for (var i = 0; i < array.Length; ++i)
- {
- var indexedPosition = i * elementSize + byteOffset;
- var value = buffer.RawBytesToNumeric(_arrayElementType, indexedPosition, BitConverter.IsLittleEndian);
- array[i] = (T) Convert.ChangeType(value, conversionType, CultureInfo.InvariantCulture);
- }
- return array;
- }
- }
|