TypedArray.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/Checked.h>
  8. #include <AK/TypeCasts.h>
  9. #include <LibJS/Runtime/AbstractOperations.h>
  10. #include <LibJS/Runtime/ArrayBuffer.h>
  11. #include <LibJS/Runtime/ArrayBufferConstructor.h>
  12. #include <LibJS/Runtime/Completion.h>
  13. #include <LibJS/Runtime/GlobalObject.h>
  14. #include <LibJS/Runtime/Iterator.h>
  15. #include <LibJS/Runtime/TypedArray.h>
  16. #include <LibJS/Runtime/TypedArrayConstructor.h>
  17. #include <LibJS/Runtime/Uint8Array.h>
  18. #include <LibJS/Runtime/ValueInlines.h>
  19. namespace JS {
  20. ThrowCompletionOr<TypedArrayBase*> typed_array_from(VM& vm, Value typed_array_value)
  21. {
  22. auto this_object = TRY(typed_array_value.to_object(vm));
  23. if (!this_object->is_typed_array())
  24. return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "TypedArray");
  25. return static_cast<TypedArrayBase*>(this_object.ptr());
  26. }
  27. // 22.2.5.1.3 InitializeTypedArrayFromArrayBuffer, https://tc39.es/ecma262/#sec-initializetypedarrayfromarraybuffer
  28. static ThrowCompletionOr<void> initialize_typed_array_from_array_buffer(VM& vm, TypedArrayBase& typed_array, ArrayBuffer& array_buffer, Value byte_offset, Value length)
  29. {
  30. // 1. Let elementSize be TypedArrayElementSize(O).
  31. auto element_size = typed_array.element_size();
  32. // 2. Let offset be ? ToIndex(byteOffset).
  33. auto offset = TRY(byte_offset.to_index(vm));
  34. // 3. If offset modulo elementSize ≠ 0, throw a RangeError exception.
  35. if (offset % element_size != 0)
  36. return vm.throw_completion<RangeError>(ErrorType::TypedArrayInvalidByteOffset, typed_array.class_name(), element_size, offset);
  37. // 4. Let bufferIsFixedLength be IsFixedLengthArrayBuffer(buffer).
  38. auto buffer_is_fixed_length = array_buffer.is_fixed_length();
  39. size_t new_length { 0 };
  40. // 5. If length is not undefined, then
  41. if (!length.is_undefined()) {
  42. // a. Let newLength be ? ToIndex(length).
  43. new_length = TRY(length.to_index(vm));
  44. }
  45. // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
  46. if (array_buffer.is_detached())
  47. return vm.throw_completion<TypeError>(ErrorType::DetachedArrayBuffer);
  48. // 7. Let bufferByteLength be ArrayBufferByteLength(buffer, seq-cst).
  49. auto buffer_byte_length = array_buffer_byte_length(array_buffer, ArrayBuffer::Order::SeqCst);
  50. // 8. If length is undefined and bufferIsFixedLength is false, then
  51. if (length.is_undefined() && !buffer_is_fixed_length) {
  52. // a. If offset > bufferByteLength, throw a RangeError exception.
  53. if (offset > buffer_byte_length)
  54. return vm.throw_completion<RangeError>(ErrorType::TypedArrayOutOfRangeByteOffset, offset, buffer_byte_length);
  55. // b. Set O.[[ByteLength]] to auto.
  56. typed_array.set_byte_length(ByteLength::auto_());
  57. // c. Set O.[[ArrayLength]] to auto.
  58. typed_array.set_array_length(ByteLength::auto_());
  59. }
  60. // 9. Else,
  61. else {
  62. Checked<u32> new_byte_length;
  63. // a. If length is undefined, then
  64. if (length.is_undefined()) {
  65. // i. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception.
  66. if (modulo(buffer_byte_length, element_size) != 0)
  67. return vm.throw_completion<RangeError>(ErrorType::TypedArrayInvalidBufferLength, typed_array.class_name(), element_size, buffer_byte_length);
  68. // ii. Let newByteLength be bufferByteLength - offset.
  69. new_byte_length = buffer_byte_length;
  70. new_byte_length -= offset;
  71. // iii. If newByteLength < 0, throw a RangeError exception.
  72. if (new_byte_length.has_overflow())
  73. return vm.throw_completion<RangeError>(ErrorType::TypedArrayOutOfRangeByteOffset, offset, buffer_byte_length);
  74. }
  75. // b. Else,
  76. else {
  77. // i. Let newByteLength be newLength × elementSize.
  78. new_byte_length = new_length;
  79. new_byte_length *= element_size;
  80. // ii. If offset + newByteLength > bufferByteLength, throw a RangeError exception.
  81. Checked<u32> new_byte_end = offset;
  82. new_byte_end += new_byte_length;
  83. if (new_byte_end.has_overflow())
  84. return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "typed array");
  85. if (new_byte_end.value() > buffer_byte_length)
  86. return vm.throw_completion<RangeError>(ErrorType::TypedArrayOutOfRangeByteOffsetOrLength, offset, new_byte_end.value(), buffer_byte_length);
  87. }
  88. // c. Set O.[[ByteLength]] to newByteLength.
  89. typed_array.set_byte_length(new_byte_length.value());
  90. // d. Set O.[[ArrayLength]] to newByteLength / elementSize.
  91. typed_array.set_array_length(new_byte_length.value() / element_size);
  92. }
  93. // 10. Set O.[[ViewedArrayBuffer]] to buffer.
  94. typed_array.set_viewed_array_buffer(&array_buffer);
  95. // 11. Set O.[[ByteOffset]] to offset.
  96. typed_array.set_byte_offset(offset);
  97. // 12. Return unused.
  98. return {};
  99. }
  100. // 23.2.5.1.2 InitializeTypedArrayFromTypedArray ( O, srcArray ), https://tc39.es/ecma262/#sec-initializetypedarrayfromtypedarray
  101. template<typename T>
  102. static ThrowCompletionOr<void> initialize_typed_array_from_typed_array(VM& vm, TypedArray<T>& typed_array, TypedArrayBase& source_array)
  103. {
  104. auto& realm = *vm.current_realm();
  105. // 1. Let srcData be srcArray.[[ViewedArrayBuffer]].
  106. auto* source_data = source_array.viewed_array_buffer();
  107. VERIFY(source_data);
  108. // 2. Let elementType be TypedArrayElementType(O).
  109. auto const& element_type = typed_array.element_name();
  110. // 3. Let elementSize be TypedArrayElementSize(O).
  111. auto element_size = typed_array.element_size();
  112. // 4. Let srcType be TypedArrayElementType(srcArray).
  113. auto const& source_type = source_array.element_name();
  114. // 5. Let srcElementSize be TypedArrayElementSize(srcArray).
  115. auto source_element_size = source_array.element_size();
  116. // 6. Let srcByteOffset be srcArray.[[ByteOffset]].
  117. auto source_byte_offset = source_array.byte_offset();
  118. // 7. Let srcRecord be MakeTypedArrayWithBufferWitnessRecord(srcArray, seq-cst).
  119. auto source_record = make_typed_array_with_buffer_witness_record(source_array, ArrayBuffer::Order::SeqCst);
  120. // 8. If IsTypedArrayOutOfBounds(srcRecord) is true, throw a TypeError exception.
  121. if (is_typed_array_out_of_bounds(source_record))
  122. return vm.throw_completion<TypeError>(ErrorType::BufferOutOfBounds, "TypedArray"sv);
  123. // 9. Let elementLength be TypedArrayLength(srcRecord).
  124. auto element_length = typed_array_length(source_record);
  125. // 10. Let byteLength be elementSize × elementLength.
  126. Checked<size_t> byte_length = element_size;
  127. byte_length *= element_length;
  128. if (byte_length.has_overflow())
  129. return vm.template throw_completion<RangeError>(ErrorType::InvalidLength, "typed array");
  130. ArrayBuffer* data = nullptr;
  131. // 11. If elementType is srcType, then
  132. if (element_type == source_type) {
  133. // a. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength).
  134. data = TRY(clone_array_buffer(vm, *source_data, source_byte_offset, byte_length.value()));
  135. }
  136. // 12. Else,
  137. else {
  138. // a. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength).
  139. data = TRY(allocate_array_buffer(vm, realm.intrinsics().array_buffer_constructor(), byte_length.value()));
  140. // b. If srcArray.[[ContentType]] is not O.[[ContentType]], throw a TypeError exception.
  141. if (source_array.content_type() != typed_array.content_type())
  142. return vm.template throw_completion<TypeError>(ErrorType::TypedArrayContentTypeMismatch, typed_array.class_name(), source_array.class_name());
  143. // c. Let srcByteIndex be srcByteOffset.
  144. u64 source_byte_index = source_byte_offset;
  145. // d. Let targetByteIndex be 0.
  146. u64 target_byte_index = 0;
  147. // e. Let count be elementLength.
  148. // f. Repeat, while count > 0,
  149. for (u32 i = 0; i < element_length; ++i) {
  150. // i. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, unordered).
  151. auto value = source_array.get_value_from_buffer(source_byte_index, ArrayBuffer::Order::Unordered);
  152. // ii. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, unordered).
  153. data->template set_value<T>(target_byte_index, value, true, ArrayBuffer::Order::Unordered);
  154. // iii. Set srcByteIndex to srcByteIndex + srcElementSize.
  155. source_byte_index += source_element_size;
  156. // iv. Set targetByteIndex to targetByteIndex + elementSize.
  157. target_byte_index += element_size;
  158. // v. Set count to count - 1.
  159. }
  160. }
  161. // 13. Set O.[[ViewedArrayBuffer]] to data.
  162. typed_array.set_viewed_array_buffer(data);
  163. // 14. Set O.[[ByteLength]] to byteLength.
  164. typed_array.set_byte_length(byte_length.value());
  165. // 15. Set O.[[ByteOffset]] to 0.
  166. typed_array.set_byte_offset(0);
  167. // 16. Set O.[[ArrayLength]] to elementLength.
  168. typed_array.set_array_length(element_length);
  169. // 17. Return unused.
  170. return {};
  171. }
  172. // 23.2.5.1.6 AllocateTypedArrayBuffer ( O, length ), https://tc39.es/ecma262/#sec-allocatetypedarraybuffer
  173. template<typename T>
  174. static ThrowCompletionOr<void> allocate_typed_array_buffer(VM& vm, TypedArray<T>& typed_array, size_t length)
  175. {
  176. auto& realm = *vm.current_realm();
  177. // Enforce 2GB "Excessive Length" limit
  178. if (length > NumericLimits<i32>::max() / sizeof(T))
  179. return vm.template throw_completion<RangeError>(ErrorType::InvalidLength, "typed array");
  180. // 1. Assert: O.[[ViewedArrayBuffer]] is undefined.
  181. // 2. Let elementSize be TypedArrayElementSize(O).
  182. auto element_size = typed_array.element_size();
  183. if (Checked<size_t>::multiplication_would_overflow(element_size, length))
  184. return vm.template throw_completion<RangeError>(ErrorType::InvalidLength, "typed array");
  185. // 3. Let byteLength be elementSize × length.
  186. auto byte_length = element_size * length;
  187. // 4. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength).
  188. auto* data = TRY(allocate_array_buffer(vm, realm.intrinsics().array_buffer_constructor(), byte_length));
  189. // 5. Set O.[[ViewedArrayBuffer]] to data.
  190. typed_array.set_viewed_array_buffer(data);
  191. // 6. Set O.[[ByteLength]] to byteLength.
  192. typed_array.set_byte_length(byte_length);
  193. // 7. Set O.[[ByteOffset]] to 0.
  194. typed_array.set_byte_offset(0);
  195. // 8. Set O.[[ArrayLength]] to length.
  196. typed_array.set_array_length(length);
  197. // 9. Return unused.
  198. return {};
  199. }
  200. // 23.2.5.1.5 InitializeTypedArrayFromArrayLike, https://tc39.es/ecma262/#sec-initializetypedarrayfromarraylike
  201. template<typename T>
  202. static ThrowCompletionOr<void> initialize_typed_array_from_array_like(VM& vm, TypedArray<T>& typed_array, Object const& array_like)
  203. {
  204. // 1. Let len be ? LengthOfArrayLike(arrayLike).
  205. auto length = TRY(length_of_array_like(vm, array_like));
  206. // 2. Perform ? AllocateTypedArrayBuffer(O, len).
  207. TRY(allocate_typed_array_buffer(vm, typed_array, length));
  208. // 3. Let k be 0.
  209. // 4. Repeat, while k < len,
  210. for (size_t k = 0; k < length; k++) {
  211. // a. Let Pk be ! ToString(𝔽(k)).
  212. // b. Let kValue be ? Get(arrayLike, Pk).
  213. auto k_value = TRY(array_like.get(k));
  214. // c. Perform ? Set(O, Pk, kValue, true).
  215. TRY(typed_array.set(k, k_value, Object::ShouldThrowExceptions::Yes));
  216. // d. Set k to k + 1.
  217. }
  218. // 5. Return unused.
  219. return {};
  220. }
  221. // 23.2.5.1.4 InitializeTypedArrayFromList, https://tc39.es/ecma262/#sec-initializetypedarrayfromlist
  222. template<typename T>
  223. static ThrowCompletionOr<void> initialize_typed_array_from_list(VM& vm, TypedArray<T>& typed_array, GC::RootVector<Value> const& list)
  224. {
  225. // 1. Let len be the number of elements in values.
  226. auto length = list.size();
  227. // 2. Perform ? AllocateTypedArrayBuffer(O, len).
  228. TRY(allocate_typed_array_buffer(vm, typed_array, length));
  229. // 3. Let k be 0.
  230. // 4. Repeat, while k < len,
  231. for (size_t k = 0; k < length; k++) {
  232. // a. Let Pk be ! ToString(𝔽(k)).
  233. // b. Let kValue be the first element of values and remove that element from values.
  234. auto value = list[k];
  235. // c. Perform ? Set(O, Pk, kValue, true).
  236. TRY(typed_array.set(k, value, Object::ShouldThrowExceptions::Yes));
  237. // d. Set k to k + 1.
  238. }
  239. // 5. Assert: values is now an empty List.
  240. // 6. Return unused.
  241. return {};
  242. }
  243. // 23.2.4.2 TypedArrayCreate ( constructor, argumentList ), https://tc39.es/ecma262/#typedarray-create
  244. ThrowCompletionOr<TypedArrayBase*> typed_array_create(VM& vm, FunctionObject& constructor, GC::RootVector<Value> arguments)
  245. {
  246. Optional<double> first_argument;
  247. if (arguments.size() == 1 && arguments[0].is_number())
  248. first_argument = arguments[0].as_double();
  249. // 1. Let newTypedArray be ? Construct(constructor, argumentList).
  250. auto new_typed_array = TRY(construct(vm, constructor, arguments.span()));
  251. // 2. Let taRecord be ? ValidateTypedArray(newTypedArray, seq-cst).
  252. auto typed_array_record = TRY(validate_typed_array(vm, *new_typed_array, ArrayBuffer::Order::SeqCst));
  253. // 3. If the number of elements in argumentList is 1 and argumentList[0] is a Number, then
  254. if (first_argument.has_value()) {
  255. // a. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.
  256. if (is_typed_array_out_of_bounds(typed_array_record))
  257. return vm.throw_completion<TypeError>(ErrorType::BufferOutOfBounds, "TypedArray"sv);
  258. // b. Let length be TypedArrayLength(taRecord).
  259. auto length = typed_array_length(typed_array_record);
  260. // c. If length < ℝ(argumentList[0]), throw a TypeError exception.
  261. if (length < *first_argument)
  262. return vm.throw_completion<TypeError>(ErrorType::InvalidLength, "typed array");
  263. }
  264. // 4. Return newTypedArray.
  265. return static_cast<TypedArrayBase*>(new_typed_array.ptr());
  266. }
  267. // 23.2.4.3 TypedArrayCreateSameType ( exemplar, argumentList ), https://tc39.es/ecma262/#sec-typedarray-create-same-type
  268. ThrowCompletionOr<TypedArrayBase*> typed_array_create_same_type(VM& vm, TypedArrayBase const& exemplar, GC::RootVector<Value> arguments)
  269. {
  270. auto& realm = *vm.current_realm();
  271. // 1. Let constructor be the intrinsic object associated with the constructor name exemplar.[[TypedArrayName]] in Table 68.
  272. auto constructor = (realm.intrinsics().*exemplar.intrinsic_constructor())();
  273. // 2. Let result be ? TypedArrayCreate(constructor, argumentList).
  274. auto* result = TRY(typed_array_create(vm, constructor, move(arguments)));
  275. // 3. Assert: result has [[TypedArrayName]] and [[ContentType]] internal slots.
  276. // 4. Assert: result.[[ContentType]] is exemplar.[[ContentType]].
  277. // 5. Return result.
  278. return result;
  279. }
  280. // 23.2.4.4 ValidateTypedArray ( O ), https://tc39.es/ecma262/#sec-validatetypedarray
  281. ThrowCompletionOr<TypedArrayWithBufferWitness> validate_typed_array(VM& vm, Object const& object, ArrayBuffer::Order order)
  282. {
  283. // 1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
  284. if (!object.is_typed_array())
  285. return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "TypedArray");
  286. // 2. Assert: O has a [[ViewedArrayBuffer]] internal slot.
  287. auto const& typed_array = static_cast<TypedArrayBase const&>(object);
  288. // 3. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, order).
  289. auto typed_array_record = make_typed_array_with_buffer_witness_record(typed_array, order);
  290. // 4. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.
  291. if (is_typed_array_out_of_bounds(typed_array_record))
  292. return vm.throw_completion<TypeError>(ErrorType::BufferOutOfBounds, "TypedArray"sv);
  293. // 5. Return taRecord.
  294. return typed_array_record;
  295. }
  296. // 23.2.4.7 CompareTypedArrayElements ( x, y, comparefn ), https://tc39.es/ecma262/#sec-typedarray-create-same-type
  297. ThrowCompletionOr<double> compare_typed_array_elements(VM& vm, Value x, Value y, FunctionObject* comparefn)
  298. {
  299. // 1. Assert: x is a Number and y is a Number, or x is a BigInt and y is a BigInt.
  300. VERIFY(((x.is_number() && y.is_number()) || (x.is_bigint() && y.is_bigint())));
  301. // 2. If comparefn is not undefined, then
  302. if (comparefn != nullptr) {
  303. // a. Let v be ? ToNumber(? Call(comparefn, undefined, « x, y »)).
  304. auto value = TRY(call(vm, comparefn, js_undefined(), x, y));
  305. auto value_number = TRY(value.to_number(vm));
  306. // b. If v is NaN, return +0𝔽.
  307. if (value_number.is_nan())
  308. return 0;
  309. // c. Return v.
  310. return value_number.as_double();
  311. }
  312. // 3. If x and y are both NaN, return +0𝔽.
  313. if (x.is_nan() && y.is_nan())
  314. return 0;
  315. // 4. If x is NaN, return 1𝔽.
  316. if (x.is_nan())
  317. return 1;
  318. // 5. If y is NaN, return -1𝔽.
  319. if (y.is_nan())
  320. return -1;
  321. // 6. If x < y, return -1𝔽.
  322. if (x.is_bigint()
  323. ? (x.as_bigint().big_integer() < y.as_bigint().big_integer())
  324. : (x.as_double() < y.as_double()))
  325. return -1;
  326. // 7. If x > y, return 1𝔽.
  327. if (x.is_bigint()
  328. ? (x.as_bigint().big_integer() > y.as_bigint().big_integer())
  329. : (x.as_double() > y.as_double()))
  330. return 1;
  331. // 8. If x is -0𝔽 and y is +0𝔽, return -1𝔽.
  332. if (x.is_negative_zero() && y.is_positive_zero())
  333. return -1;
  334. // 9. If x is +0𝔽 and y is -0𝔽, return 1𝔽.
  335. if (x.is_positive_zero() && y.is_negative_zero())
  336. return 1;
  337. // 10. Return +0𝔽.
  338. return 0;
  339. }
  340. void TypedArrayBase::visit_edges(Visitor& visitor)
  341. {
  342. Base::visit_edges(visitor);
  343. visitor.visit(m_viewed_array_buffer);
  344. }
  345. #define JS_DEFINE_TYPED_ARRAY(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
  346. GC_DEFINE_ALLOCATOR(ClassName); \
  347. GC_DEFINE_ALLOCATOR(PrototypeName); \
  348. GC_DEFINE_ALLOCATOR(ConstructorName); \
  349. ThrowCompletionOr<GC::Ref<ClassName>> ClassName::create(Realm& realm, u32 length, FunctionObject& new_target) \
  350. { \
  351. auto* prototype = TRY(get_prototype_from_constructor(realm.vm(), new_target, &Intrinsics::snake_name##_prototype)); \
  352. auto array_buffer = TRY(ArrayBuffer::create(realm, length * sizeof(UnderlyingBufferDataType))); \
  353. return realm.create<ClassName>(*prototype, length, *array_buffer); \
  354. } \
  355. \
  356. ThrowCompletionOr<GC::Ref<ClassName>> ClassName::create(Realm& realm, u32 length) \
  357. { \
  358. auto array_buffer = TRY(ArrayBuffer::create(realm, length * sizeof(UnderlyingBufferDataType))); \
  359. return create(realm, length, *array_buffer); \
  360. } \
  361. \
  362. GC::Ref<ClassName> ClassName::create(Realm& realm, u32 length, ArrayBuffer& array_buffer) \
  363. { \
  364. return realm.create<ClassName>(realm.intrinsics().snake_name##_prototype(), length, array_buffer); \
  365. } \
  366. \
  367. ClassName::ClassName(Object& prototype, u32 length, ArrayBuffer& array_buffer) \
  368. : TypedArray(prototype, \
  369. bit_cast<TypedArrayBase::IntrinsicConstructor>(&Intrinsics::snake_name##_constructor), \
  370. length, array_buffer, Kind::ClassName) \
  371. { \
  372. if constexpr (#ClassName##sv.is_one_of("BigInt64Array", "BigUint64Array")) \
  373. m_content_type = ContentType::BigInt; \
  374. else \
  375. m_content_type = ContentType::Number; \
  376. } \
  377. \
  378. ClassName::~ClassName() \
  379. { \
  380. } \
  381. \
  382. DeprecatedFlyString const& ClassName::element_name() const \
  383. { \
  384. return vm().names.ClassName.as_string(); \
  385. } \
  386. \
  387. PrototypeName::PrototypeName(Object& prototype) \
  388. : Object(ConstructWithPrototypeTag::Tag, prototype, MayInterfereWithIndexedPropertyAccess::Yes) \
  389. { \
  390. } \
  391. \
  392. PrototypeName::~PrototypeName() \
  393. { \
  394. } \
  395. \
  396. void PrototypeName::initialize(Realm& realm) \
  397. { \
  398. auto& vm = this->vm(); \
  399. Base::initialize(realm); \
  400. define_direct_property(vm.names.BYTES_PER_ELEMENT, Value((i32)sizeof(Type)), 0); \
  401. \
  402. if constexpr (IsSame<PrototypeName, Uint8ArrayPrototype>) \
  403. Uint8ArrayPrototypeHelpers::initialize(realm, *this); \
  404. } \
  405. \
  406. ConstructorName::ConstructorName(Realm& realm, Object& prototype) \
  407. : NativeFunction(realm.vm().names.ClassName.as_string(), prototype) \
  408. { \
  409. } \
  410. \
  411. ConstructorName::~ConstructorName() \
  412. { \
  413. } \
  414. \
  415. void ConstructorName::initialize(Realm& realm) \
  416. { \
  417. auto& vm = this->vm(); \
  418. Base::initialize(realm); \
  419. \
  420. /* 23.2.6.2 TypedArray.prototype, https://tc39.es/ecma262/#sec-typedarray.prototype */ \
  421. define_direct_property(vm.names.prototype, realm.intrinsics().snake_name##_prototype(), 0); \
  422. \
  423. /* 23.2.6.1 TypedArray.BYTES_PER_ELEMENT, https://tc39.es/ecma262/#sec-typedarray.bytes_per_element */ \
  424. define_direct_property(vm.names.BYTES_PER_ELEMENT, Value((i32)sizeof(Type)), 0); \
  425. \
  426. define_direct_property(vm.names.length, Value(3), Attribute::Configurable); \
  427. \
  428. if constexpr (IsSame<ConstructorName, Uint8ArrayConstructor>) \
  429. Uint8ArrayConstructorHelpers::initialize(realm, *this); \
  430. } \
  431. \
  432. /* 23.2.5.1 TypedArray ( ...args ), https://tc39.es/ecma262/#sec-typedarray */ \
  433. ThrowCompletionOr<Value> ConstructorName::call() \
  434. { \
  435. auto& vm = this->vm(); \
  436. return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, vm.names.ClassName); \
  437. } \
  438. \
  439. /* 23.2.5.1 TypedArray ( ...args ), https://tc39.es/ecma262/#sec-typedarray */ \
  440. ThrowCompletionOr<GC::Ref<Object>> ConstructorName::construct(FunctionObject& new_target) \
  441. { \
  442. auto& vm = this->vm(); \
  443. auto& realm = *vm.current_realm(); \
  444. \
  445. if (vm.argument_count() == 0) \
  446. return TRY(ClassName::create(realm, 0, new_target)); \
  447. \
  448. auto first_argument = vm.argument(0); \
  449. if (first_argument.is_object()) { \
  450. auto typed_array = TRY(ClassName::create(realm, 0, new_target)); \
  451. if (first_argument.as_object().is_typed_array()) { \
  452. auto& arg_typed_array = static_cast<TypedArrayBase&>(first_argument.as_object()); \
  453. TRY(initialize_typed_array_from_typed_array(vm, *typed_array, arg_typed_array)); \
  454. } else if (is<ArrayBuffer>(first_argument.as_object())) { \
  455. auto& array_buffer = static_cast<ArrayBuffer&>(first_argument.as_object()); \
  456. TRY(initialize_typed_array_from_array_buffer(vm, *typed_array, array_buffer, \
  457. vm.argument(1), vm.argument(2))); \
  458. } else { \
  459. auto iterator = TRY(first_argument.get_method(vm, vm.well_known_symbol_iterator())); \
  460. if (iterator) { \
  461. auto values = TRY(iterator_to_list(vm, TRY(get_iterator_from_method(vm, first_argument, *iterator)))); \
  462. TRY(initialize_typed_array_from_list(vm, *typed_array, values)); \
  463. } else { \
  464. TRY(initialize_typed_array_from_array_like(vm, *typed_array, first_argument.as_object())); \
  465. } \
  466. } \
  467. return typed_array; \
  468. } \
  469. \
  470. auto array_length_or_error = first_argument.to_index(vm); \
  471. if (array_length_or_error.is_error()) { \
  472. auto error = array_length_or_error.release_error(); \
  473. if (error.value()->is_object() && is<RangeError>(error.value()->as_object())) { \
  474. /* Re-throw more specific RangeError */ \
  475. return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "typed array"); \
  476. } \
  477. return error; \
  478. } \
  479. auto array_length = array_length_or_error.release_value(); \
  480. if (array_length > NumericLimits<i32>::max() / sizeof(Type)) \
  481. return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "typed array"); \
  482. /* FIXME: What is the best/correct behavior here? */ \
  483. if (Checked<u32>::multiplication_would_overflow(array_length, sizeof(Type))) \
  484. return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "typed array"); \
  485. return TRY(ClassName::create(realm, array_length, new_target)); \
  486. }
  487. #undef __JS_ENUMERATE
  488. #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
  489. JS_DEFINE_TYPED_ARRAY(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType);
  490. JS_ENUMERATE_TYPED_ARRAYS
  491. #undef __JS_ENUMERATE
  492. // 10.4.5.10 MakeTypedArrayWithBufferWitnessRecord ( obj, order ), https://tc39.es/ecma262/#sec-maketypedarraywithbufferwitnessrecord
  493. TypedArrayWithBufferWitness make_typed_array_with_buffer_witness_record(TypedArrayBase const& typed_array, ArrayBuffer::Order order)
  494. {
  495. // 1. Let buffer be obj.[[ViewedArrayBuffer]].
  496. auto* buffer = typed_array.viewed_array_buffer();
  497. ByteLength byte_length { 0 };
  498. // 2. If IsDetachedBuffer(buffer) is true, then
  499. if (buffer->is_detached()) {
  500. // a. Let byteLength be detached.
  501. byte_length = ByteLength::detached();
  502. }
  503. // 3. Else,
  504. else {
  505. // a. Let byteLength be ArrayBufferByteLength(buffer, order).
  506. byte_length = array_buffer_byte_length(*buffer, order);
  507. }
  508. // 4. Return the TypedArray With Buffer Witness Record { [[Object]]: obj, [[CachedBufferByteLength]]: byteLength }.
  509. return { .object = typed_array, .cached_buffer_byte_length = move(byte_length) };
  510. }
  511. // 10.4.5.12 TypedArrayByteLength ( taRecord ), https://tc39.es/ecma262/#sec-typedarraybytelength
  512. u32 typed_array_byte_length(TypedArrayWithBufferWitness const& typed_array_record)
  513. {
  514. // 1. If IsTypedArrayOutOfBounds(taRecord) is true, return 0.
  515. if (is_typed_array_out_of_bounds(typed_array_record))
  516. return 0;
  517. // 2. Let length be TypedArrayLength(taRecord).
  518. auto length = typed_array_length(typed_array_record);
  519. // 3. If length = 0, return 0.
  520. if (length == 0)
  521. return 0;
  522. // 4. Let O be taRecord.[[Object]].
  523. auto object = typed_array_record.object;
  524. // 5. If O.[[ByteLength]] is not auto, return O.[[ByteLength]].
  525. if (!object->byte_length().is_auto())
  526. return object->byte_length().length();
  527. // 6. Let elementSize be TypedArrayElementSize(O).
  528. auto element_size = object->element_size();
  529. // 7. Return length × elementSize.
  530. return length * element_size;
  531. }
  532. // 10.4.5.13 TypedArrayLength ( taRecord ), https://tc39.es/ecma262/#sec-typedarraylength
  533. u32 typed_array_length(TypedArrayWithBufferWitness const& typed_array_record)
  534. {
  535. // 1. Assert: IsTypedArrayOutOfBounds(taRecord) is false.
  536. VERIFY(!is_typed_array_out_of_bounds(typed_array_record));
  537. // 2. Let O be taRecord.[[Object]].
  538. auto object = typed_array_record.object;
  539. // 3. If O.[[ArrayLength]] is not auto, return O.[[ArrayLength]].
  540. if (!object->array_length().is_auto())
  541. return object->array_length().length();
  542. // 4. Assert: IsFixedLengthArrayBuffer(O.[[ViewedArrayBuffer]]) is false.
  543. VERIFY(!object->viewed_array_buffer()->is_fixed_length());
  544. // 5. Let byteOffset be O.[[ByteOffset]].
  545. auto byte_offset = object->byte_offset();
  546. // 6. Let elementSize be TypedArrayElementSize(O).
  547. auto element_size = object->element_size();
  548. // 7. Let byteLength be taRecord.[[CachedBufferByteLength]].
  549. auto const& byte_length = typed_array_record.cached_buffer_byte_length;
  550. // 8. Assert: byteLength is not detached.
  551. VERIFY(!byte_length.is_detached());
  552. // 9. Return floor((byteLength - byteOffset) / elementSize).
  553. return (byte_length.length() - byte_offset) / element_size;
  554. }
  555. // 10.4.5.14 IsTypedArrayOutOfBounds ( taRecord ), https://tc39.es/ecma262/#sec-istypedarrayoutofbounds
  556. bool is_typed_array_out_of_bounds(TypedArrayWithBufferWitness const& typed_array_record)
  557. {
  558. // 1. Let O be taRecord.[[Object]].
  559. auto object = typed_array_record.object;
  560. // 2. Let bufferByteLength be taRecord.[[CachedBufferByteLength]].
  561. auto const& buffer_byte_length = typed_array_record.cached_buffer_byte_length;
  562. // 3. Assert: IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true if and only if bufferByteLength is detached.
  563. VERIFY(object->viewed_array_buffer()->is_detached() == buffer_byte_length.is_detached());
  564. // 4. If bufferByteLength is detached, return true.
  565. if (buffer_byte_length.is_detached())
  566. return true;
  567. // 5. Let byteOffsetStart be O.[[ByteOffset]].
  568. auto byte_offset_start = object->byte_offset();
  569. u32 byte_offset_end = 0;
  570. // 6. If O.[[ArrayLength]] is auto, then
  571. if (object->array_length().is_auto()) {
  572. // a. Let byteOffsetEnd be bufferByteLength.
  573. byte_offset_end = buffer_byte_length.length();
  574. }
  575. // 7. Else,
  576. else {
  577. // a. Let elementSize be TypedArrayElementSize(O).
  578. auto element_size = object->element_size();
  579. // b. Let byteOffsetEnd be byteOffsetStart + O.[[ArrayLength]] × elementSize.
  580. byte_offset_end = byte_offset_start + object->array_length().length() * element_size;
  581. }
  582. // 8. If byteOffsetStart > bufferByteLength or byteOffsetEnd > bufferByteLength, return true.
  583. if ((byte_offset_start > buffer_byte_length.length()) || (byte_offset_end > buffer_byte_length.length()))
  584. return true;
  585. // 9. NOTE: 0-length TypedArrays are not considered out-of-bounds.
  586. // 10. Return false.
  587. return false;
  588. }
  589. // 10.4.5.15 IsTypedArrayFixedLength ( O ), https://tc39.es/ecma262/#sec-istypedarrayfixedlength
  590. bool is_typed_array_fixed_length(TypedArrayBase const& typed_array)
  591. {
  592. // 1. If O.[[ArrayLength]] is AUTO, return false.
  593. if (typed_array.array_length().is_auto())
  594. return false;
  595. // 2. Let buffer be O.[[ViewedArrayBuffer]].
  596. auto const* buffer = typed_array.viewed_array_buffer();
  597. // 3. If IsFixedLengthArrayBuffer(buffer) is false and IsSharedArrayBuffer(buffer) is false, return false.
  598. if (!buffer->is_fixed_length() && !buffer->is_shared_array_buffer())
  599. return false;
  600. // 4. Return true.
  601. return true;
  602. }
  603. // 10.4.5.16 IsValidIntegerIndex ( O, index ), https://tc39.es/ecma262/#sec-isvalidintegerindex
  604. bool is_valid_integer_index_slow_case(TypedArrayBase const& typed_array, CanonicalIndex property_index)
  605. {
  606. // 4. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, unordered).
  607. auto typed_array_record = make_typed_array_with_buffer_witness_record(typed_array, ArrayBuffer::Unordered);
  608. // 5. NOTE: Bounds checking is not a synchronizing operation when O's backing buffer is a growable SharedArrayBuffer.
  609. // 6. If IsTypedArrayOutOfBounds(taRecord) is true, return false.
  610. if (is_typed_array_out_of_bounds(typed_array_record))
  611. return false;
  612. // 7. Let length be TypedArrayLength(taRecord).
  613. auto length = typed_array_length(typed_array_record);
  614. // 8. If ℝ(index) < 0 or ℝ(index) ≥ length, return false.
  615. if (property_index.as_index() >= length)
  616. return false;
  617. // 9. Return true.
  618. return true;
  619. }
  620. }