TypedArray.h 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #pragma once
  8. #include <LibJS/Runtime/AbstractOperations.h>
  9. #include <LibJS/Runtime/ArrayBuffer.h>
  10. #include <LibJS/Runtime/ByteLength.h>
  11. #include <LibJS/Runtime/Completion.h>
  12. #include <LibJS/Runtime/GlobalObject.h>
  13. #include <LibJS/Runtime/PropertyDescriptor.h>
  14. #include <LibJS/Runtime/PropertyKey.h>
  15. #include <LibJS/Runtime/TypedArrayConstructor.h>
  16. #include <LibJS/Runtime/VM.h>
  17. namespace JS {
  18. class TypedArrayBase : public Object {
  19. JS_OBJECT(TypedArrayBase, Object);
  20. public:
  21. enum class ContentType {
  22. BigInt,
  23. Number,
  24. };
  25. enum class Kind {
  26. #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  27. ClassName,
  28. JS_ENUMERATE_TYPED_ARRAYS
  29. #undef __JS_ENUMERATE
  30. };
  31. using IntrinsicConstructor = GC::Ref<TypedArrayConstructor> (Intrinsics::*)();
  32. ByteLength const& array_length() const { return m_array_length; }
  33. ByteLength const& byte_length() const { return m_byte_length; }
  34. u32 byte_offset() const { return m_byte_offset; }
  35. ContentType content_type() const { return m_content_type; }
  36. ArrayBuffer* viewed_array_buffer() const { return m_viewed_array_buffer; }
  37. IntrinsicConstructor intrinsic_constructor() const { return m_intrinsic_constructor; }
  38. void set_array_length(ByteLength length) { m_array_length = move(length); }
  39. void set_byte_length(ByteLength length) { m_byte_length = move(length); }
  40. void set_byte_offset(u32 offset) { m_byte_offset = offset; }
  41. void set_viewed_array_buffer(ArrayBuffer* array_buffer) { m_viewed_array_buffer = array_buffer; }
  42. [[nodiscard]] Kind kind() const { return m_kind; }
  43. u32 element_size() const { return m_element_size; }
  44. virtual DeprecatedFlyString const& element_name() const = 0;
  45. // 25.1.3.11 IsUnclampedIntegerElementType ( type ), https://tc39.es/ecma262/#sec-isunclampedintegerelementtype
  46. virtual bool is_unclamped_integer_element_type() const = 0;
  47. // 25.1.3.12 IsBigIntElementType ( type ), https://tc39.es/ecma262/#sec-isbigintelementtype
  48. virtual bool is_bigint_element_type() const = 0;
  49. // 25.1.3.16 GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ), https://tc39.es/ecma262/#sec-getvaluefrombuffer
  50. virtual Value get_value_from_buffer(size_t byte_index, ArrayBuffer::Order, bool is_little_endian = true) const = 0;
  51. // 25.1.3.18 SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] ), https://tc39.es/ecma262/#sec-setvalueinbuffer
  52. virtual void set_value_in_buffer(size_t byte_index, Value, ArrayBuffer::Order, bool is_little_endian = true) = 0;
  53. // 25.1.3.19 GetModifySetValueInBuffer ( arrayBuffer, byteIndex, type, value, op ), https://tc39.es/ecma262/#sec-getmodifysetvalueinbuffer
  54. virtual Value get_modify_set_value_in_buffer(size_t byte_index, Value value, ReadWriteModifyFunction operation, bool is_little_endian = true) = 0;
  55. protected:
  56. TypedArrayBase(Object& prototype, IntrinsicConstructor intrinsic_constructor, Kind kind, u32 element_size)
  57. : Object(ConstructWithPrototypeTag::Tag, prototype, MayInterfereWithIndexedPropertyAccess::Yes)
  58. , m_element_size(element_size)
  59. , m_kind(kind)
  60. , m_intrinsic_constructor(intrinsic_constructor)
  61. {
  62. set_is_typed_array();
  63. }
  64. u32 m_element_size { 0 };
  65. ByteLength m_array_length { 0 };
  66. ByteLength m_byte_length { 0 };
  67. u32 m_byte_offset { 0 };
  68. ContentType m_content_type { ContentType::Number };
  69. Kind m_kind {};
  70. GC::Ptr<ArrayBuffer> m_viewed_array_buffer;
  71. IntrinsicConstructor m_intrinsic_constructor { nullptr };
  72. private:
  73. virtual void visit_edges(Visitor&) override;
  74. };
  75. // 10.4.5.9 TypedArray With Buffer Witness Records, https://tc39.es/ecma262/#sec-typedarray-with-buffer-witness-records
  76. struct TypedArrayWithBufferWitness {
  77. GC::Ref<TypedArrayBase const> object; // [[Object]]
  78. ByteLength cached_buffer_byte_length; // [[CachedBufferByteLength]]
  79. };
  80. TypedArrayWithBufferWitness make_typed_array_with_buffer_witness_record(TypedArrayBase const&, ArrayBuffer::Order);
  81. u32 typed_array_byte_length(TypedArrayWithBufferWitness const&);
  82. u32 typed_array_length(TypedArrayWithBufferWitness const&);
  83. bool is_typed_array_out_of_bounds(TypedArrayWithBufferWitness const&);
  84. bool is_typed_array_fixed_length(TypedArrayBase const&);
  85. bool is_valid_integer_index_slow_case(TypedArrayBase const&, CanonicalIndex property_index);
  86. // 10.4.5.16 IsValidIntegerIndex ( O, index ), https://tc39.es/ecma262/#sec-isvalidintegerindex
  87. inline bool is_valid_integer_index(TypedArrayBase const& typed_array, CanonicalIndex property_index)
  88. {
  89. // 1. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false.
  90. if (typed_array.viewed_array_buffer()->is_detached())
  91. return false;
  92. // 2. If IsIntegralNumber(index) is false, return false.
  93. // 3. If index is -0𝔽, return false.
  94. if (!property_index.is_index())
  95. return false;
  96. // OPTIMIZATION: For TypedArrays with non-resizable ArrayBuffers, we can avoid most of the work performed by
  97. // IsValidIntegerIndex. We just need to check whether the array itself is out-of-bounds and if
  98. // the provided index is within the array bounds.
  99. if (auto const& array_length = typed_array.array_length(); !array_length.is_auto()) {
  100. auto byte_length = array_buffer_byte_length(*typed_array.viewed_array_buffer(), ArrayBuffer::Unordered);
  101. auto byte_offset_end = typed_array.byte_offset() + array_length.length() * typed_array.element_size();
  102. return typed_array.byte_offset() <= byte_length
  103. && byte_offset_end <= byte_length
  104. && property_index.as_index() < array_length.length();
  105. }
  106. return is_valid_integer_index_slow_case(typed_array, property_index);
  107. }
  108. // 10.4.5.17 TypedArrayGetElement ( O, index ), https://tc39.es/ecma262/#sec-typedarraygetelement
  109. template<typename T>
  110. inline ThrowCompletionOr<Value> typed_array_get_element(TypedArrayBase const& typed_array, CanonicalIndex property_index)
  111. {
  112. // 1. If IsValidIntegerIndex(O, index) is false, return undefined.
  113. if (!is_valid_integer_index(typed_array, property_index))
  114. return js_undefined();
  115. // 2. Let offset be O.[[ByteOffset]].
  116. auto offset = typed_array.byte_offset();
  117. // 3. Let elementSize be TypedArrayElementSize(O).
  118. // 4. Let byteIndexInBuffer be (ℝ(index) × elementSize) + offset.
  119. Checked<size_t> byte_index_in_buffer = property_index.as_index();
  120. byte_index_in_buffer *= typed_array.element_size();
  121. byte_index_in_buffer += offset;
  122. // FIXME: Not exactly sure what we should do when overflow occurs.
  123. // Just return as if it's an invalid index for now.
  124. if (byte_index_in_buffer.has_overflow()) {
  125. dbgln("typed_array_get_element(): byte_index_in_buffer overflowed, returning as if it's an invalid index.");
  126. return js_undefined();
  127. }
  128. // 5. Let elementType be TypedArrayElementType(O).
  129. // 6. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, elementType, true, unordered).
  130. return typed_array.viewed_array_buffer()->template get_value<T>(byte_index_in_buffer.value(), true, ArrayBuffer::Order::Unordered);
  131. }
  132. // 10.4.5.18 TypedArraySetElement ( O, index, value ), https://tc39.es/ecma262/#sec-typedarraysetelement
  133. // NOTE: In error cases, the function will return as if it succeeded.
  134. template<typename T>
  135. inline ThrowCompletionOr<void> typed_array_set_element(TypedArrayBase& typed_array, CanonicalIndex property_index, Value value)
  136. {
  137. VERIFY(!value.is_empty());
  138. auto& vm = typed_array.vm();
  139. Value num_value;
  140. // 1. If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value).
  141. if (typed_array.content_type() == TypedArrayBase::ContentType::BigInt)
  142. num_value = TRY(value.to_bigint(vm));
  143. // 2. Otherwise, let numValue be ? ToNumber(value).
  144. else
  145. num_value = TRY(value.to_number(vm));
  146. // 3. If IsValidIntegerIndex(O, index) is true, then
  147. // NOTE: Inverted for flattened logic.
  148. if (!is_valid_integer_index(typed_array, property_index))
  149. return {};
  150. // a. Let offset be O.[[ByteOffset]].
  151. auto offset = typed_array.byte_offset();
  152. // b. Let elementSize be TypedArrayElementSize(O).
  153. // c. Let byteIndexInBuffer be (ℝ(index) × elementSize) + offset.
  154. Checked<size_t> byte_index_in_buffer = property_index.as_index();
  155. byte_index_in_buffer *= typed_array.element_size();
  156. byte_index_in_buffer += offset;
  157. // FIXME: Not exactly sure what we should do when overflow occurs.
  158. // Just return as if it succeeded for now.
  159. if (byte_index_in_buffer.has_overflow()) {
  160. dbgln("typed_array_set_element(): byte_index_in_buffer overflowed, returning as if succeeded.");
  161. return {};
  162. }
  163. // d. Let elementType be TypedArrayElementType(O).
  164. // e. Perform SetValueInBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, elementType, numValue, true, unordered).
  165. typed_array.viewed_array_buffer()->template set_value<T>(byte_index_in_buffer.value(), num_value, true, ArrayBuffer::Order::Unordered);
  166. // 4. Return unused.
  167. return {};
  168. }
  169. template<typename T>
  170. class TypedArray : public TypedArrayBase {
  171. JS_OBJECT(TypedArray, TypedArrayBase);
  172. using UnderlyingBufferDataType = Conditional<IsSame<ClampedU8, T>, u8, T>;
  173. public:
  174. // 10.4.5.1 [[PreventExtensions]] ( ), https://tc39.es/ecma262/#sec-typedarray-preventextensions
  175. virtual ThrowCompletionOr<bool> internal_prevent_extensions() override
  176. {
  177. // 1. NOTE: The extensibility-related invariants specified in 6.1.7.3 do not allow this method to return true
  178. // when O can gain (or lose and then regain) properties, which might occur for properties with integer index
  179. // names when its underlying buffer is resized.
  180. // 2. If IsTypedArrayFixedLength(O) is false, return false.
  181. if (!is_typed_array_fixed_length(*this))
  182. return false;
  183. // 3. Return OrdinaryPreventExtensions(O).
  184. return Object::internal_prevent_extensions();
  185. }
  186. // 10.4.5.2 [[GetOwnProperty]] ( P ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-getownproperty-p
  187. virtual ThrowCompletionOr<Optional<PropertyDescriptor>> internal_get_own_property(PropertyKey const& property_key) const override
  188. {
  189. // NOTE: If the property name is a number type (An implementation-defined optimized
  190. // property key type), it can be treated as a string property that will transparently be
  191. // converted into a canonical numeric index.
  192. // 1. If P is a String, then
  193. // NOTE: This includes an implementation-defined optimization, see note above!
  194. if (property_key.is_string() || property_key.is_number()) {
  195. // a. Let numericIndex be CanonicalNumericIndexString(P).
  196. auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip);
  197. // b. If numericIndex is not undefined, then
  198. if (!numeric_index.is_undefined()) {
  199. // i. Let value be TypedArrayGetElement(O, numericIndex).
  200. auto value = MUST_OR_THROW_OOM(typed_array_get_element<T>(*this, numeric_index));
  201. // ii. If value is undefined, return undefined.
  202. if (value.is_undefined())
  203. return Optional<PropertyDescriptor> {};
  204. // iii. Return the PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
  205. return PropertyDescriptor {
  206. .value = value,
  207. .writable = true,
  208. .enumerable = true,
  209. .configurable = true,
  210. };
  211. }
  212. }
  213. // 2. Return OrdinaryGetOwnProperty(O, P).
  214. return Object::internal_get_own_property(property_key);
  215. }
  216. // 10.4.5.3 [[HasProperty]] ( P ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-hasproperty-p
  217. virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const& property_key) const override
  218. {
  219. // NOTE: If the property name is a number type (An implementation-defined optimized
  220. // property key type), it can be treated as a string property that will transparently be
  221. // converted into a canonical numeric index.
  222. // 1. If P is a String, then
  223. // NOTE: This includes an implementation-defined optimization, see note above!
  224. if (property_key.is_string() || property_key.is_number()) {
  225. // a. Let numericIndex be CanonicalNumericIndexString(P).
  226. auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip);
  227. // b. If numericIndex is not undefined, return IsValidIntegerIndex(O, numericIndex).
  228. if (!numeric_index.is_undefined())
  229. return is_valid_integer_index(*this, numeric_index);
  230. }
  231. // 2. Return ? OrdinaryHasProperty(O, P).
  232. return Object::internal_has_property(property_key);
  233. }
  234. // 10.4.5.4 [[DefineOwnProperty]] ( P, Desc ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-defineownproperty-p-desc
  235. virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const& property_key, PropertyDescriptor const& property_descriptor, Optional<PropertyDescriptor>* precomputed_get_own_property = nullptr) override
  236. {
  237. // NOTE: If the property name is a number type (An implementation-defined optimized
  238. // property key type), it can be treated as a string property that will transparently be
  239. // converted into a canonical numeric index.
  240. // 1. If P is a String, then
  241. // NOTE: This includes an implementation-defined optimization, see note above!
  242. if (property_key.is_string() || property_key.is_number()) {
  243. // a. Let numericIndex be CanonicalNumericIndexString(P).
  244. auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip);
  245. // b. If numericIndex is not undefined, then
  246. if (!numeric_index.is_undefined()) {
  247. // i. If IsValidIntegerIndex(O, numericIndex) is false, return false.
  248. if (!is_valid_integer_index(*this, numeric_index))
  249. return false;
  250. // ii. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, return false.
  251. if (property_descriptor.configurable.has_value() && !*property_descriptor.configurable)
  252. return false;
  253. // iii. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] is false, return false.
  254. if (property_descriptor.enumerable.has_value() && !*property_descriptor.enumerable)
  255. return false;
  256. // iv. If IsAccessorDescriptor(Desc) is true, return false.
  257. if (property_descriptor.is_accessor_descriptor())
  258. return false;
  259. // v. If Desc has a [[Writable]] field and if Desc.[[Writable]] is false, return false.
  260. if (property_descriptor.writable.has_value() && !*property_descriptor.writable)
  261. return false;
  262. // vi. If Desc has a [[Value]] field, perform ? TypedArraySetElement(O, numericIndex, Desc.[[Value]]).
  263. if (property_descriptor.value.has_value())
  264. TRY(typed_array_set_element<T>(*this, numeric_index, *property_descriptor.value));
  265. // vii. Return true.
  266. return true;
  267. }
  268. }
  269. // 2. Return ! OrdinaryDefineOwnProperty(O, P, Desc).
  270. return Object::internal_define_own_property(property_key, property_descriptor, precomputed_get_own_property);
  271. }
  272. // 10.4.5.5 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-typedarray-get
  273. virtual ThrowCompletionOr<Value> internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const override
  274. {
  275. VERIFY(!receiver.is_empty());
  276. // NOTE: If the property name is a number type (An implementation-defined optimized
  277. // property key type), it can be treated as a string property that will transparently be
  278. // converted into a canonical numeric index.
  279. // 1. If P is a String, then
  280. // NOTE: This includes an implementation-defined optimization, see note above!
  281. if (property_key.is_string() || property_key.is_number()) {
  282. // a. Let numericIndex be CanonicalNumericIndexString(P).
  283. auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip);
  284. // b. If numericIndex is not undefined, then
  285. if (!numeric_index.is_undefined()) {
  286. // i. Return TypedArrayGetElement(O, numericIndex).
  287. return typed_array_get_element<T>(*this, numeric_index);
  288. }
  289. }
  290. // 2. Return ? OrdinaryGet(O, P, Receiver).
  291. return Object::internal_get(property_key, receiver, cacheable_metadata, phase);
  292. }
  293. // 10.4.5.6 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver
  294. virtual ThrowCompletionOr<bool> internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*) override
  295. {
  296. VERIFY(!value.is_empty());
  297. VERIFY(!receiver.is_empty());
  298. // NOTE: If the property name is a number type (An implementation-defined optimized
  299. // property key type), it can be treated as a string property that will transparently be
  300. // converted into a canonical numeric index.
  301. // 1. If P is a String, then
  302. // NOTE: This includes an implementation-defined optimization, see note above!
  303. if (property_key.is_string() || property_key.is_number()) {
  304. // a. Let numericIndex be CanonicalNumericIndexString(P).
  305. auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip);
  306. // b. If numericIndex is not undefined, then
  307. if (!numeric_index.is_undefined()) {
  308. // i. If SameValue(O, Receiver) is true, then
  309. if (same_value(this, receiver)) {
  310. // 1. Perform ? TypedArraySetElement(O, numericIndex, V).
  311. TRY(typed_array_set_element<T>(*this, numeric_index, value));
  312. // 2. Return true.
  313. return true;
  314. }
  315. // ii. If IsValidIntegerIndex(O, numericIndex) is false, return true.
  316. if (!is_valid_integer_index(*this, numeric_index))
  317. return true;
  318. }
  319. }
  320. // 2. Return ? OrdinarySet(O, P, V, Receiver).
  321. return Object::internal_set(property_key, value, receiver);
  322. }
  323. // 10.4.5.7 [[Delete]] ( P ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-delete-p
  324. virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const& property_key) override
  325. {
  326. // NOTE: If the property name is a number type (An implementation-defined optimized
  327. // property key type), it can be treated as a string property that will transparently be
  328. // converted into a canonical numeric index.
  329. // 1. If P is a String, then
  330. // NOTE: This includes an implementation-defined optimization, see note above!
  331. if (property_key.is_string() || property_key.is_number()) {
  332. // a. Let numericIndex be CanonicalNumericIndexString(P).
  333. auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip);
  334. // b. If numericIndex is not undefined, then
  335. if (!numeric_index.is_undefined()) {
  336. // i. If IsValidIntegerIndex(O, numericIndex) is false, return true; else return false.
  337. if (!is_valid_integer_index(*this, numeric_index))
  338. return true;
  339. return false;
  340. }
  341. }
  342. // 2. Return ? OrdinaryDelete(O, P).
  343. return Object::internal_delete(property_key);
  344. }
  345. // 10.4.5.8 [[OwnPropertyKeys]] ( ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-ownpropertykeys
  346. virtual ThrowCompletionOr<GC::RootVector<Value>> internal_own_property_keys() const override
  347. {
  348. auto& vm = this->vm();
  349. // 1. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).
  350. auto typed_array_record = make_typed_array_with_buffer_witness_record(*this, ArrayBuffer::Order::SeqCst);
  351. // 2. Let keys be a new empty List.
  352. auto keys = GC::RootVector<Value> { heap() };
  353. // 3. If IsTypedArrayOutOfBounds(taRecord) is false, then
  354. if (!is_typed_array_out_of_bounds(typed_array_record)) {
  355. // a. Let length be TypedArrayLength(taRecord).
  356. auto length = typed_array_length(typed_array_record);
  357. // b. For each integer i such that 0 ≤ i < length, in ascending order, do
  358. for (size_t i = 0; i < length; ++i) {
  359. // i. Append ! ToString(𝔽(i)) to keys.
  360. keys.append(PrimitiveString::create(vm, String::number(i)));
  361. }
  362. }
  363. // 4. For each own property key P of O such that P is a String and P is not an integer index, in ascending chronological order of property creation, do
  364. for (auto& it : shape().property_table()) {
  365. if (it.key.is_string()) {
  366. // a. Append P to keys.
  367. keys.append(it.key.to_value(vm));
  368. }
  369. }
  370. // 5. For each own property key P of O such that P is a Symbol, in ascending chronological order of property creation, do
  371. for (auto& it : shape().property_table()) {
  372. if (it.key.is_symbol()) {
  373. // a. Append P to keys.
  374. keys.append(it.key.to_value(vm));
  375. }
  376. }
  377. // 6. Return keys.
  378. return { move(keys) };
  379. }
  380. ReadonlySpan<UnderlyingBufferDataType> data() const
  381. {
  382. auto typed_array_record = make_typed_array_with_buffer_witness_record(*this, ArrayBuffer::Order::SeqCst);
  383. if (is_typed_array_out_of_bounds(typed_array_record)) {
  384. // FIXME: Propagate this as an error?
  385. return {};
  386. }
  387. auto length = typed_array_length(typed_array_record);
  388. return { reinterpret_cast<UnderlyingBufferDataType const*>(m_viewed_array_buffer->buffer().data() + m_byte_offset), length };
  389. }
  390. Span<UnderlyingBufferDataType> data()
  391. {
  392. auto typed_array_record = make_typed_array_with_buffer_witness_record(*this, ArrayBuffer::Order::SeqCst);
  393. if (is_typed_array_out_of_bounds(typed_array_record)) {
  394. // FIXME: Propagate this as an error?
  395. return {};
  396. }
  397. auto length = typed_array_length(typed_array_record);
  398. return { reinterpret_cast<UnderlyingBufferDataType*>(m_viewed_array_buffer->buffer().data() + m_byte_offset), length };
  399. }
  400. bool is_unclamped_integer_element_type() const override
  401. {
  402. constexpr bool is_unclamped_integer = IsSame<T, i8> || IsSame<T, u8> || IsSame<T, i16> || IsSame<T, u16> || IsSame<T, i32> || IsSame<T, u32>;
  403. return is_unclamped_integer;
  404. }
  405. bool is_bigint_element_type() const override
  406. {
  407. constexpr bool is_bigint = IsSame<T, i64> || IsSame<T, u64>;
  408. return is_bigint;
  409. }
  410. Value get_value_from_buffer(size_t byte_index, ArrayBuffer::Order order, bool is_little_endian = true) const override { return viewed_array_buffer()->template get_value<T>(byte_index, true, order, is_little_endian); }
  411. void set_value_in_buffer(size_t byte_index, Value value, ArrayBuffer::Order order, bool is_little_endian = true) override { return viewed_array_buffer()->template set_value<T>(byte_index, value, true, order, is_little_endian); }
  412. Value get_modify_set_value_in_buffer(size_t byte_index, Value value, ReadWriteModifyFunction operation, bool is_little_endian = true) override { return viewed_array_buffer()->template get_modify_set_value<T>(byte_index, value, move(operation), is_little_endian); }
  413. protected:
  414. TypedArray(Object& prototype, IntrinsicConstructor intrinsic_constructor, u32 array_length, ArrayBuffer& array_buffer, Kind kind)
  415. : TypedArrayBase(prototype, intrinsic_constructor, kind, sizeof(UnderlyingBufferDataType))
  416. {
  417. VERIFY(!Checked<u32>::multiplication_would_overflow(array_length, sizeof(UnderlyingBufferDataType)));
  418. m_viewed_array_buffer = &array_buffer;
  419. if (array_length)
  420. VERIFY(!data().is_null());
  421. m_array_length = array_length;
  422. m_byte_length = m_viewed_array_buffer->byte_length();
  423. }
  424. };
  425. ThrowCompletionOr<TypedArrayBase*> typed_array_from(VM&, Value);
  426. ThrowCompletionOr<TypedArrayBase*> typed_array_create(VM&, FunctionObject& constructor, GC::RootVector<Value> arguments);
  427. ThrowCompletionOr<TypedArrayBase*> typed_array_create_same_type(VM&, TypedArrayBase const& exemplar, GC::RootVector<Value> arguments);
  428. ThrowCompletionOr<TypedArrayWithBufferWitness> validate_typed_array(VM&, Object const&, ArrayBuffer::Order);
  429. ThrowCompletionOr<double> compare_typed_array_elements(VM&, Value x, Value y, FunctionObject* comparefn);
  430. #define JS_DECLARE_TYPED_ARRAY(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  431. class ClassName : public TypedArray<Type> { \
  432. JS_OBJECT(ClassName, TypedArray); \
  433. GC_DECLARE_ALLOCATOR(ClassName); \
  434. \
  435. public: \
  436. virtual ~ClassName(); \
  437. static ThrowCompletionOr<GC::Ref<ClassName>> create(Realm&, u32 length, FunctionObject& new_target); \
  438. static ThrowCompletionOr<GC::Ref<ClassName>> create(Realm&, u32 length); \
  439. static GC::Ref<ClassName> create(Realm&, u32 length, ArrayBuffer& buffer); \
  440. virtual DeprecatedFlyString const& element_name() const override; \
  441. \
  442. protected: \
  443. ClassName(Object& prototype, u32 length, ArrayBuffer& array_buffer); \
  444. }; \
  445. class PrototypeName final : public Object { \
  446. JS_OBJECT(PrototypeName, Object); \
  447. GC_DECLARE_ALLOCATOR(PrototypeName); \
  448. \
  449. public: \
  450. virtual void initialize(Realm&) override; \
  451. virtual ~PrototypeName() override; \
  452. \
  453. private: \
  454. PrototypeName(Object& prototype); \
  455. }; \
  456. class ConstructorName final : public NativeFunction { \
  457. JS_OBJECT(ConstructorName, NativeFunction); \
  458. GC_DECLARE_ALLOCATOR(ConstructorName); \
  459. \
  460. public: \
  461. virtual void initialize(Realm&) override; \
  462. virtual ~ConstructorName() override; \
  463. \
  464. virtual ThrowCompletionOr<Value> call() override; \
  465. virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override; \
  466. \
  467. private: \
  468. explicit ConstructorName(Realm&, Object& prototype); \
  469. virtual bool has_constructor() const override \
  470. { \
  471. return true; \
  472. } \
  473. };
  474. #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  475. JS_DECLARE_TYPED_ARRAY(ClassName, snake_name, PrototypeName, ConstructorName, Type);
  476. JS_ENUMERATE_TYPED_ARRAYS
  477. #undef __JS_ENUMERATE
  478. }