TypedArray.h 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  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.8 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_valid_integer_index_slow_case(TypedArrayBase const&, CanonicalIndex property_index);
  85. // 10.4.5.14 IsValidIntegerIndex ( O, index ), https://tc39.es/ecma262/#sec-isvalidintegerindex
  86. inline bool is_valid_integer_index(TypedArrayBase const& typed_array, CanonicalIndex property_index)
  87. {
  88. // 1. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false.
  89. if (typed_array.viewed_array_buffer()->is_detached())
  90. return false;
  91. // 2. If IsIntegralNumber(index) is false, return false.
  92. // 3. If index is -0𝔽, return false.
  93. if (!property_index.is_index())
  94. return false;
  95. // OPTIMIZATION: For TypedArrays with non-resizable ArrayBuffers, we can avoid most of the work performed by
  96. // IsValidIntegerIndex. We just need to check whether the array itself is out-of-bounds and if
  97. // the provided index is within the array bounds.
  98. if (auto const& array_length = typed_array.array_length(); !array_length.is_auto()) {
  99. auto byte_length = array_buffer_byte_length(*typed_array.viewed_array_buffer(), ArrayBuffer::Unordered);
  100. auto byte_offset_end = typed_array.byte_offset() + array_length.length() * typed_array.element_size();
  101. return typed_array.byte_offset() <= byte_length
  102. && byte_offset_end <= byte_length
  103. && property_index.as_index() < array_length.length();
  104. }
  105. return is_valid_integer_index_slow_case(typed_array, property_index);
  106. }
  107. // 10.4.5.15 TypedArrayGetElement ( O, index ), https://tc39.es/ecma262/#sec-typedarraygetelement
  108. template<typename T>
  109. inline ThrowCompletionOr<Value> typed_array_get_element(TypedArrayBase const& typed_array, CanonicalIndex property_index)
  110. {
  111. // 1. If IsValidIntegerIndex(O, index) is false, return undefined.
  112. if (!is_valid_integer_index(typed_array, property_index))
  113. return js_undefined();
  114. // 2. Let offset be O.[[ByteOffset]].
  115. auto offset = typed_array.byte_offset();
  116. // 3. Let elementSize be TypedArrayElementSize(O).
  117. // 4. Let byteIndexInBuffer be (ℝ(index) × elementSize) + offset.
  118. Checked<size_t> byte_index_in_buffer = property_index.as_index();
  119. byte_index_in_buffer *= typed_array.element_size();
  120. byte_index_in_buffer += offset;
  121. // FIXME: Not exactly sure what we should do when overflow occurs.
  122. // Just return as if it's an invalid index for now.
  123. if (byte_index_in_buffer.has_overflow()) {
  124. dbgln("typed_array_get_element(): byte_index_in_buffer overflowed, returning as if it's an invalid index.");
  125. return js_undefined();
  126. }
  127. // 5. Let elementType be TypedArrayElementType(O).
  128. // 6. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, elementType, true, unordered).
  129. return typed_array.viewed_array_buffer()->template get_value<T>(byte_index_in_buffer.value(), true, ArrayBuffer::Order::Unordered);
  130. }
  131. // 10.4.5.16 TypedArraySetElement ( O, index, value ), https://tc39.es/ecma262/#sec-typedarraysetelement
  132. // NOTE: In error cases, the function will return as if it succeeded.
  133. template<typename T>
  134. inline ThrowCompletionOr<void> typed_array_set_element(TypedArrayBase& typed_array, CanonicalIndex property_index, Value value)
  135. {
  136. VERIFY(!value.is_empty());
  137. auto& vm = typed_array.vm();
  138. Value num_value;
  139. // 1. If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value).
  140. if (typed_array.content_type() == TypedArrayBase::ContentType::BigInt)
  141. num_value = TRY(value.to_bigint(vm));
  142. // 2. Otherwise, let numValue be ? ToNumber(value).
  143. else
  144. num_value = TRY(value.to_number(vm));
  145. // 3. If IsValidIntegerIndex(O, index) is true, then
  146. // NOTE: Inverted for flattened logic.
  147. if (!is_valid_integer_index(typed_array, property_index))
  148. return {};
  149. // a. Let offset be O.[[ByteOffset]].
  150. auto offset = typed_array.byte_offset();
  151. // b. Let elementSize be TypedArrayElementSize(O).
  152. // c. Let byteIndexInBuffer be (ℝ(index) × elementSize) + offset.
  153. Checked<size_t> byte_index_in_buffer = property_index.as_index();
  154. byte_index_in_buffer *= typed_array.element_size();
  155. byte_index_in_buffer += offset;
  156. // FIXME: Not exactly sure what we should do when overflow occurs.
  157. // Just return as if it succeeded for now.
  158. if (byte_index_in_buffer.has_overflow()) {
  159. dbgln("typed_array_set_element(): byte_index_in_buffer overflowed, returning as if succeeded.");
  160. return {};
  161. }
  162. // d. Let elementType be TypedArrayElementType(O).
  163. // e. Perform SetValueInBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, elementType, numValue, true, unordered).
  164. typed_array.viewed_array_buffer()->template set_value<T>(byte_index_in_buffer.value(), num_value, true, ArrayBuffer::Order::Unordered);
  165. // 4. Return unused.
  166. return {};
  167. }
  168. template<typename T>
  169. class TypedArray : public TypedArrayBase {
  170. JS_OBJECT(TypedArray, TypedArrayBase);
  171. using UnderlyingBufferDataType = Conditional<IsSame<ClampedU8, T>, u8, T>;
  172. public:
  173. // 10.4.5.1 [[GetOwnProperty]] ( P ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-getownproperty-p
  174. virtual ThrowCompletionOr<Optional<PropertyDescriptor>> internal_get_own_property(PropertyKey const& property_key) const override
  175. {
  176. // NOTE: If the property name is a number type (An implementation-defined optimized
  177. // property key type), it can be treated as a string property that will transparently be
  178. // converted into a canonical numeric index.
  179. // 1. If P is a String, then
  180. // NOTE: This includes an implementation-defined optimization, see note above!
  181. if (property_key.is_string() || property_key.is_number()) {
  182. // a. Let numericIndex be CanonicalNumericIndexString(P).
  183. auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip);
  184. // b. If numericIndex is not undefined, then
  185. if (!numeric_index.is_undefined()) {
  186. // i. Let value be TypedArrayGetElement(O, numericIndex).
  187. auto value = MUST_OR_THROW_OOM(typed_array_get_element<T>(*this, numeric_index));
  188. // ii. If value is undefined, return undefined.
  189. if (value.is_undefined())
  190. return Optional<PropertyDescriptor> {};
  191. // iii. Return the PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
  192. return PropertyDescriptor {
  193. .value = value,
  194. .writable = true,
  195. .enumerable = true,
  196. .configurable = true,
  197. };
  198. }
  199. }
  200. // 2. Return OrdinaryGetOwnProperty(O, P).
  201. return Object::internal_get_own_property(property_key);
  202. }
  203. // 10.4.5.2 [[HasProperty]] ( P ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-hasproperty-p
  204. virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const& property_key) const override
  205. {
  206. // NOTE: If the property name is a number type (An implementation-defined optimized
  207. // property key type), it can be treated as a string property that will transparently be
  208. // converted into a canonical numeric index.
  209. // 1. If P is a String, then
  210. // NOTE: This includes an implementation-defined optimization, see note above!
  211. if (property_key.is_string() || property_key.is_number()) {
  212. // a. Let numericIndex be CanonicalNumericIndexString(P).
  213. auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip);
  214. // b. If numericIndex is not undefined, return IsValidIntegerIndex(O, numericIndex).
  215. if (!numeric_index.is_undefined())
  216. return is_valid_integer_index(*this, numeric_index);
  217. }
  218. // 2. Return ? OrdinaryHasProperty(O, P).
  219. return Object::internal_has_property(property_key);
  220. }
  221. // 10.4.5.3 [[DefineOwnProperty]] ( P, Desc ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-defineownproperty-p-desc
  222. virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const& property_key, PropertyDescriptor const& property_descriptor, Optional<PropertyDescriptor>* precomputed_get_own_property = nullptr) override
  223. {
  224. // NOTE: If the property name is a number type (An implementation-defined optimized
  225. // property key type), it can be treated as a string property that will transparently be
  226. // converted into a canonical numeric index.
  227. // 1. If P is a String, then
  228. // NOTE: This includes an implementation-defined optimization, see note above!
  229. if (property_key.is_string() || property_key.is_number()) {
  230. // a. Let numericIndex be CanonicalNumericIndexString(P).
  231. auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip);
  232. // b. If numericIndex is not undefined, then
  233. if (!numeric_index.is_undefined()) {
  234. // i. If IsValidIntegerIndex(O, numericIndex) is false, return false.
  235. if (!is_valid_integer_index(*this, numeric_index))
  236. return false;
  237. // ii. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, return false.
  238. if (property_descriptor.configurable.has_value() && !*property_descriptor.configurable)
  239. return false;
  240. // iii. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] is false, return false.
  241. if (property_descriptor.enumerable.has_value() && !*property_descriptor.enumerable)
  242. return false;
  243. // iv. If IsAccessorDescriptor(Desc) is true, return false.
  244. if (property_descriptor.is_accessor_descriptor())
  245. return false;
  246. // v. If Desc has a [[Writable]] field and if Desc.[[Writable]] is false, return false.
  247. if (property_descriptor.writable.has_value() && !*property_descriptor.writable)
  248. return false;
  249. // vi. If Desc has a [[Value]] field, perform ? TypedArraySetElement(O, numericIndex, Desc.[[Value]]).
  250. if (property_descriptor.value.has_value())
  251. TRY(typed_array_set_element<T>(*this, numeric_index, *property_descriptor.value));
  252. // vii. Return true.
  253. return true;
  254. }
  255. }
  256. // 2. Return ! OrdinaryDefineOwnProperty(O, P, Desc).
  257. return Object::internal_define_own_property(property_key, property_descriptor, precomputed_get_own_property);
  258. }
  259. // 10.4.5.4 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-typedarray-get
  260. virtual ThrowCompletionOr<Value> internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const override
  261. {
  262. VERIFY(!receiver.is_empty());
  263. // NOTE: If the property name is a number type (An implementation-defined optimized
  264. // property key type), it can be treated as a string property that will transparently be
  265. // converted into a canonical numeric index.
  266. // 1. If P is a String, then
  267. // NOTE: This includes an implementation-defined optimization, see note above!
  268. if (property_key.is_string() || property_key.is_number()) {
  269. // a. Let numericIndex be CanonicalNumericIndexString(P).
  270. auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip);
  271. // b. If numericIndex is not undefined, then
  272. if (!numeric_index.is_undefined()) {
  273. // i. Return TypedArrayGetElement(O, numericIndex).
  274. return typed_array_get_element<T>(*this, numeric_index);
  275. }
  276. }
  277. // 2. Return ? OrdinaryGet(O, P, Receiver).
  278. return Object::internal_get(property_key, receiver, cacheable_metadata, phase);
  279. }
  280. // 10.4.5.5 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver
  281. virtual ThrowCompletionOr<bool> internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*) override
  282. {
  283. VERIFY(!value.is_empty());
  284. VERIFY(!receiver.is_empty());
  285. // NOTE: If the property name is a number type (An implementation-defined optimized
  286. // property key type), it can be treated as a string property that will transparently be
  287. // converted into a canonical numeric index.
  288. // 1. If P is a String, then
  289. // NOTE: This includes an implementation-defined optimization, see note above!
  290. if (property_key.is_string() || property_key.is_number()) {
  291. // a. Let numericIndex be CanonicalNumericIndexString(P).
  292. auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip);
  293. // b. If numericIndex is not undefined, then
  294. if (!numeric_index.is_undefined()) {
  295. // i. If SameValue(O, Receiver) is true, then
  296. if (same_value(this, receiver)) {
  297. // 1. Perform ? TypedArraySetElement(O, numericIndex, V).
  298. TRY(typed_array_set_element<T>(*this, numeric_index, value));
  299. // 2. Return true.
  300. return true;
  301. }
  302. // ii. If IsValidIntegerIndex(O, numericIndex) is false, return true.
  303. if (!is_valid_integer_index(*this, numeric_index))
  304. return true;
  305. }
  306. }
  307. // 2. Return ? OrdinarySet(O, P, V, Receiver).
  308. return Object::internal_set(property_key, value, receiver);
  309. }
  310. // 10.4.5.6 [[Delete]] ( P ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-delete-p
  311. virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const& property_key) override
  312. {
  313. // NOTE: If the property name is a number type (An implementation-defined optimized
  314. // property key type), it can be treated as a string property that will transparently be
  315. // converted into a canonical numeric index.
  316. // 1. If P is a String, then
  317. // NOTE: This includes an implementation-defined optimization, see note above!
  318. if (property_key.is_string() || property_key.is_number()) {
  319. // a. Let numericIndex be CanonicalNumericIndexString(P).
  320. auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip);
  321. // b. If numericIndex is not undefined, then
  322. if (!numeric_index.is_undefined()) {
  323. // i. If IsValidIntegerIndex(O, numericIndex) is false, return true; else return false.
  324. if (!is_valid_integer_index(*this, numeric_index))
  325. return true;
  326. return false;
  327. }
  328. }
  329. // 2. Return ? OrdinaryDelete(O, P).
  330. return Object::internal_delete(property_key);
  331. }
  332. // 10.4.5.7 [[OwnPropertyKeys]] ( ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-ownpropertykeys
  333. virtual ThrowCompletionOr<GC::MarkedVector<Value>> internal_own_property_keys() const override
  334. {
  335. auto& vm = this->vm();
  336. // 1. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).
  337. auto typed_array_record = make_typed_array_with_buffer_witness_record(*this, ArrayBuffer::Order::SeqCst);
  338. // 2. Let keys be a new empty List.
  339. auto keys = GC::MarkedVector<Value> { heap() };
  340. // 3. If IsTypedArrayOutOfBounds(taRecord) is false, then
  341. if (!is_typed_array_out_of_bounds(typed_array_record)) {
  342. // a. Let length be TypedArrayLength(taRecord).
  343. auto length = typed_array_length(typed_array_record);
  344. // b. For each integer i such that 0 ≤ i < length, in ascending order, do
  345. for (size_t i = 0; i < length; ++i) {
  346. // i. Append ! ToString(𝔽(i)) to keys.
  347. keys.append(PrimitiveString::create(vm, String::number(i)));
  348. }
  349. }
  350. // 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
  351. for (auto& it : shape().property_table()) {
  352. if (it.key.is_string()) {
  353. // a. Append P to keys.
  354. keys.append(it.key.to_value(vm));
  355. }
  356. }
  357. // 5. For each own property key P of O such that P is a Symbol, in ascending chronological order of property creation, do
  358. for (auto& it : shape().property_table()) {
  359. if (it.key.is_symbol()) {
  360. // a. Append P to keys.
  361. keys.append(it.key.to_value(vm));
  362. }
  363. }
  364. // 6. Return keys.
  365. return { move(keys) };
  366. }
  367. ReadonlySpan<UnderlyingBufferDataType> data() const
  368. {
  369. auto typed_array_record = make_typed_array_with_buffer_witness_record(*this, ArrayBuffer::Order::SeqCst);
  370. if (is_typed_array_out_of_bounds(typed_array_record)) {
  371. // FIXME: Propagate this as an error?
  372. return {};
  373. }
  374. auto length = typed_array_length(typed_array_record);
  375. return { reinterpret_cast<UnderlyingBufferDataType const*>(m_viewed_array_buffer->buffer().data() + m_byte_offset), length };
  376. }
  377. Span<UnderlyingBufferDataType> data()
  378. {
  379. auto typed_array_record = make_typed_array_with_buffer_witness_record(*this, ArrayBuffer::Order::SeqCst);
  380. if (is_typed_array_out_of_bounds(typed_array_record)) {
  381. // FIXME: Propagate this as an error?
  382. return {};
  383. }
  384. auto length = typed_array_length(typed_array_record);
  385. return { reinterpret_cast<UnderlyingBufferDataType*>(m_viewed_array_buffer->buffer().data() + m_byte_offset), length };
  386. }
  387. bool is_unclamped_integer_element_type() const override
  388. {
  389. constexpr bool is_unclamped_integer = IsSame<T, i8> || IsSame<T, u8> || IsSame<T, i16> || IsSame<T, u16> || IsSame<T, i32> || IsSame<T, u32>;
  390. return is_unclamped_integer;
  391. }
  392. bool is_bigint_element_type() const override
  393. {
  394. constexpr bool is_bigint = IsSame<T, i64> || IsSame<T, u64>;
  395. return is_bigint;
  396. }
  397. 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); }
  398. 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); }
  399. 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); }
  400. protected:
  401. TypedArray(Object& prototype, IntrinsicConstructor intrinsic_constructor, u32 array_length, ArrayBuffer& array_buffer, Kind kind)
  402. : TypedArrayBase(prototype, intrinsic_constructor, kind, sizeof(UnderlyingBufferDataType))
  403. {
  404. VERIFY(!Checked<u32>::multiplication_would_overflow(array_length, sizeof(UnderlyingBufferDataType)));
  405. m_viewed_array_buffer = &array_buffer;
  406. if (array_length)
  407. VERIFY(!data().is_null());
  408. m_array_length = array_length;
  409. m_byte_length = m_viewed_array_buffer->byte_length();
  410. }
  411. };
  412. ThrowCompletionOr<TypedArrayBase*> typed_array_from(VM&, Value);
  413. ThrowCompletionOr<TypedArrayBase*> typed_array_create(VM&, FunctionObject& constructor, GC::MarkedVector<Value> arguments);
  414. ThrowCompletionOr<TypedArrayBase*> typed_array_create_same_type(VM&, TypedArrayBase const& exemplar, GC::MarkedVector<Value> arguments);
  415. ThrowCompletionOr<TypedArrayWithBufferWitness> validate_typed_array(VM&, Object const&, ArrayBuffer::Order);
  416. ThrowCompletionOr<double> compare_typed_array_elements(VM&, Value x, Value y, FunctionObject* comparefn);
  417. #define JS_DECLARE_TYPED_ARRAY(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  418. class ClassName : public TypedArray<Type> { \
  419. JS_OBJECT(ClassName, TypedArray); \
  420. GC_DECLARE_ALLOCATOR(ClassName); \
  421. \
  422. public: \
  423. virtual ~ClassName(); \
  424. static ThrowCompletionOr<GC::Ref<ClassName>> create(Realm&, u32 length, FunctionObject& new_target); \
  425. static ThrowCompletionOr<GC::Ref<ClassName>> create(Realm&, u32 length); \
  426. static GC::Ref<ClassName> create(Realm&, u32 length, ArrayBuffer& buffer); \
  427. virtual DeprecatedFlyString const& element_name() const override; \
  428. \
  429. protected: \
  430. ClassName(Object& prototype, u32 length, ArrayBuffer& array_buffer); \
  431. }; \
  432. class PrototypeName final : public Object { \
  433. JS_OBJECT(PrototypeName, Object); \
  434. GC_DECLARE_ALLOCATOR(PrototypeName); \
  435. \
  436. public: \
  437. virtual void initialize(Realm&) override; \
  438. virtual ~PrototypeName() override; \
  439. \
  440. private: \
  441. PrototypeName(Object& prototype); \
  442. }; \
  443. class ConstructorName final : public NativeFunction { \
  444. JS_OBJECT(ConstructorName, NativeFunction); \
  445. GC_DECLARE_ALLOCATOR(ConstructorName); \
  446. \
  447. public: \
  448. virtual void initialize(Realm&) override; \
  449. virtual ~ConstructorName() override; \
  450. \
  451. virtual ThrowCompletionOr<Value> call() override; \
  452. virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override; \
  453. \
  454. private: \
  455. explicit ConstructorName(Realm&, Object& prototype); \
  456. virtual bool has_constructor() const override \
  457. { \
  458. return true; \
  459. } \
  460. };
  461. #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  462. JS_DECLARE_TYPED_ARRAY(ClassName, snake_name, PrototypeName, ConstructorName, Type);
  463. JS_ENUMERATE_TYPED_ARRAYS
  464. #undef __JS_ENUMERATE
  465. }