ArrayBufferPrototype.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /*
  2. * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
  3. * Copyright (c) 2021-2022, Jamie Mansfield <jmansfield@cadixdev.org>
  4. * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/Function.h>
  9. #include <LibJS/Runtime/AbstractOperations.h>
  10. #include <LibJS/Runtime/ArrayBufferConstructor.h>
  11. #include <LibJS/Runtime/ArrayBufferPrototype.h>
  12. #include <LibJS/Runtime/GlobalObject.h>
  13. namespace JS {
  14. GC_DEFINE_ALLOCATOR(ArrayBufferPrototype);
  15. ArrayBufferPrototype::ArrayBufferPrototype(Realm& realm)
  16. : PrototypeObject(realm.intrinsics().object_prototype())
  17. {
  18. }
  19. void ArrayBufferPrototype::initialize(Realm& realm)
  20. {
  21. auto& vm = this->vm();
  22. Base::initialize(realm);
  23. u8 attr = Attribute::Writable | Attribute::Configurable;
  24. define_native_accessor(realm, vm.names.byteLength, byte_length_getter, {}, Attribute::Configurable);
  25. define_native_accessor(realm, vm.names.detached, detached_getter, {}, Attribute::Configurable);
  26. define_native_accessor(realm, vm.names.maxByteLength, max_byte_length, {}, Attribute::Configurable);
  27. define_native_accessor(realm, vm.names.resizable, resizable, {}, Attribute::Configurable);
  28. define_native_function(realm, vm.names.resize, resize, 1, attr);
  29. define_native_function(realm, vm.names.slice, slice, 2, attr);
  30. define_native_function(realm, vm.names.transfer, transfer, 0, attr);
  31. define_native_function(realm, vm.names.transferToFixedLength, transfer_to_fixed_length, 0, attr);
  32. // 25.1.6.7 ArrayBuffer.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-arraybuffer.prototype-@@tostringtag
  33. define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, vm.names.ArrayBuffer.as_string()), Attribute::Configurable);
  34. }
  35. // 25.1.6.1 get ArrayBuffer.prototype.byteLength, https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.bytelength
  36. JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::byte_length_getter)
  37. {
  38. // 1. Let O be the this value.
  39. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  40. auto array_buffer_object = TRY(typed_this_value(vm));
  41. // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
  42. if (array_buffer_object->is_shared_array_buffer())
  43. return vm.throw_completion<TypeError>(ErrorType::SharedArrayBuffer);
  44. // NOTE: These steps are done in byte_length()
  45. // 4. If IsDetachedBuffer(O) is true, return +0𝔽.
  46. // 5. Let length be O.[[ArrayBufferByteLength]].
  47. // 6. Return 𝔽(length).
  48. return Value(array_buffer_object->byte_length());
  49. }
  50. // 25.1.6.3 get ArrayBuffer.prototype.detached, https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.detached
  51. JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::detached_getter)
  52. {
  53. // 1. Let O be the this value.
  54. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  55. auto array_buffer_object = TRY(typed_this_value(vm));
  56. // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
  57. if (array_buffer_object->is_shared_array_buffer())
  58. return vm.throw_completion<TypeError>(ErrorType::SharedArrayBuffer);
  59. // 4. Return IsDetachedBuffer(O).
  60. return Value(array_buffer_object->is_detached());
  61. }
  62. // 25.1.6.4 get ArrayBuffer.prototype.maxByteLength, https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.maxbytelength
  63. JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::max_byte_length)
  64. {
  65. // 1. Let O be the this value.
  66. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  67. auto array_buffer_object = TRY(typed_this_value(vm));
  68. // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
  69. if (array_buffer_object->is_shared_array_buffer())
  70. return vm.throw_completion<TypeError>(ErrorType::SharedArrayBuffer);
  71. // 4. If IsDetachedBuffer(O) is true, return +0𝔽.
  72. if (array_buffer_object->is_detached())
  73. return Value { 0 };
  74. size_t length = 0;
  75. // 5. If IsFixedLengthArrayBuffer(O) is true, then
  76. if (array_buffer_object->is_fixed_length()) {
  77. // a. Let length be O.[[ArrayBufferByteLength]].
  78. length = array_buffer_object->byte_length();
  79. }
  80. // 6. Else,
  81. else {
  82. // a. Let length be O.[[ArrayBufferMaxByteLength]].
  83. length = array_buffer_object->max_byte_length();
  84. }
  85. // 7. Return 𝔽(length).
  86. return Value { length };
  87. }
  88. // 25.1.6.5 get ArrayBuffer.prototype.resizable, https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.resizable
  89. JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::resizable)
  90. {
  91. // 1. Let O be the this value.
  92. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  93. auto array_buffer_object = TRY(typed_this_value(vm));
  94. // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
  95. if (array_buffer_object->is_shared_array_buffer())
  96. return vm.throw_completion<TypeError>(ErrorType::SharedArrayBuffer);
  97. // 4. If IsFixedLengthArrayBuffer(O) is false, return true; otherwise return false.
  98. return Value { !array_buffer_object->is_fixed_length() };
  99. }
  100. // 25.1.6.6 ArrayBuffer.prototype.resize ( newLength ), https://tc39.es/ecma262/#sec-arraybuffer.prototype.resize
  101. JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::resize)
  102. {
  103. auto new_length = vm.argument(0);
  104. // 1. Let O be the this value.
  105. auto array_buffer_object = TRY(typed_this_value(vm));
  106. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
  107. if (array_buffer_object->is_fixed_length())
  108. return vm.throw_completion<TypeError>(ErrorType::FixedArrayBuffer);
  109. // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
  110. if (array_buffer_object->is_shared_array_buffer())
  111. return vm.throw_completion<TypeError>(ErrorType::SharedArrayBuffer);
  112. // 4. Let newByteLength be ? ToIndex(newLength).
  113. auto new_byte_length = TRY(new_length.to_index(vm));
  114. // 5. If IsDetachedBuffer(O) is true, throw a TypeError exception.
  115. if (array_buffer_object->is_detached())
  116. return vm.throw_completion<TypeError>(ErrorType::DetachedArrayBuffer);
  117. // 6. If newByteLength > O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
  118. if (new_byte_length > array_buffer_object->max_byte_length())
  119. return vm.throw_completion<RangeError>(ErrorType::ByteLengthExceedsMaxByteLength, new_byte_length, array_buffer_object->max_byte_length());
  120. // 7. Let hostHandled be ? HostResizeArrayBuffer(O, newByteLength).
  121. auto host_handled = TRY(vm.host_resize_array_buffer(array_buffer_object, new_byte_length));
  122. // 8. If hostHandled is handled, return undefined.
  123. if (host_handled == HandledByHost::Handled)
  124. return js_undefined();
  125. // 9. Let oldBlock be O.[[ArrayBufferData]].
  126. auto const& old_block = array_buffer_object->buffer();
  127. // 10. Let newBlock be ? CreateByteDataBlock(newByteLength).
  128. auto new_block = TRY(create_byte_data_block(vm, new_byte_length));
  129. // 11. Let copyLength be min(newByteLength, O.[[ArrayBufferByteLength]]).
  130. auto copy_length = min(new_byte_length, array_buffer_object->byte_length());
  131. // 12. Perform CopyDataBlockBytes(newBlock, 0, oldBlock, 0, copyLength).
  132. copy_data_block_bytes(new_block.buffer(), 0, old_block, 0, copy_length);
  133. // 13. NOTE: Neither creation of the new Data Block nor copying from the old Data Block are observable. Implementations may implement this method as in-place growth or shrinkage.
  134. // 14. Set O.[[ArrayBufferData]] to newBlock.
  135. array_buffer_object->set_data_block(move(new_block));
  136. // 15. Set O.[[ArrayBufferByteLength]] to newByteLength.
  137. // 16. Return undefined.
  138. return js_undefined();
  139. }
  140. // 25.1.6.7 ArrayBuffer.prototype.slice ( start, end ), https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice
  141. JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::slice)
  142. {
  143. auto& realm = *vm.current_realm();
  144. auto start = vm.argument(0);
  145. auto end = vm.argument(1);
  146. // 1. Let O be the this value.
  147. // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
  148. auto array_buffer_object = TRY(typed_this_value(vm));
  149. // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
  150. if (array_buffer_object->is_shared_array_buffer())
  151. return vm.throw_completion<TypeError>(ErrorType::SharedArrayBuffer);
  152. // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
  153. if (array_buffer_object->is_detached())
  154. return vm.throw_completion<TypeError>(ErrorType::DetachedArrayBuffer);
  155. // 5. Let len be O.[[ArrayBufferByteLength]].
  156. auto length = array_buffer_object->byte_length();
  157. // 6. Let relativeStart be ? ToIntegerOrInfinity(start).
  158. auto relative_start = TRY(start.to_integer_or_infinity(vm));
  159. double first;
  160. // 7. If relativeStart is -∞, let first be 0.
  161. if (Value(relative_start).is_negative_infinity())
  162. first = 0;
  163. // 8. Else if relativeStart < 0, let first be max(len + relativeStart, 0).
  164. else if (relative_start < 0)
  165. first = max(length + relative_start, 0.0);
  166. // 9. Else, let first be min(relativeStart, len).
  167. else
  168. first = min(relative_start, (double)length);
  169. // 10. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).
  170. auto relative_end = end.is_undefined() ? length : TRY(end.to_integer_or_infinity(vm));
  171. double final;
  172. // 11. If relativeEnd is -∞, let final be 0.
  173. if (Value(relative_end).is_negative_infinity())
  174. final = 0;
  175. // 12. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).
  176. else if (relative_end < 0)
  177. final = max(length + relative_end, 0.0);
  178. // 13. Else, let final be min(relativeEnd, len).
  179. else
  180. final = min(relative_end, (double)length);
  181. // 14. Let newLen be max(final - first, 0).
  182. auto new_length = max(final - first, 0.0);
  183. // 15. Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%).
  184. auto* constructor = TRY(species_constructor(vm, array_buffer_object, realm.intrinsics().array_buffer_constructor()));
  185. // 16. Let new be ? Construct(ctor, « 𝔽(newLen) »).
  186. auto new_array_buffer = TRY(construct(vm, *constructor, Value(new_length)));
  187. // 17. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]).
  188. if (!is<ArrayBuffer>(new_array_buffer.ptr()))
  189. return vm.throw_completion<TypeError>(ErrorType::SpeciesConstructorDidNotCreate, "an ArrayBuffer");
  190. auto* new_array_buffer_object = static_cast<ArrayBuffer*>(new_array_buffer.ptr());
  191. // 18. If IsSharedArrayBuffer(new) is true, throw a TypeError exception.
  192. if (new_array_buffer_object->is_shared_array_buffer())
  193. return vm.throw_completion<TypeError>(ErrorType::SharedArrayBuffer);
  194. // 19. If IsDetachedBuffer(new) is true, throw a TypeError exception.
  195. if (new_array_buffer_object->is_detached())
  196. return vm.throw_completion<TypeError>(ErrorType::SpeciesConstructorReturned, "a detached ArrayBuffer");
  197. // 20. If SameValue(new, O) is true, throw a TypeError exception.
  198. if (same_value(new_array_buffer_object, array_buffer_object))
  199. return vm.throw_completion<TypeError>(ErrorType::SpeciesConstructorReturned, "same ArrayBuffer instance");
  200. // 21. If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception.
  201. if (new_array_buffer_object->byte_length() < new_length)
  202. return vm.throw_completion<TypeError>(ErrorType::SpeciesConstructorReturned, "an ArrayBuffer smaller than requested");
  203. // 22. NOTE: Side-effects of the above steps may have detached or resized O.
  204. // 23. If IsDetachedBuffer(O) is true, throw a TypeError exception.
  205. if (array_buffer_object->is_detached())
  206. return vm.throw_completion<TypeError>(ErrorType::DetachedArrayBuffer);
  207. // 24. Let fromBuf be O.[[ArrayBufferData]].
  208. auto& from_buf = array_buffer_object->buffer();
  209. // 25. Let toBuf be new.[[ArrayBufferData]].
  210. auto& to_buf = new_array_buffer_object->buffer();
  211. // 26. Let currentLen be O.[[ArrayBufferByteLength]].
  212. auto current_length = array_buffer_object->byte_length();
  213. // 27. If first < currentLen, then
  214. if (first < current_length) {
  215. // a. Let count be min(newLen, currentLen - first).
  216. auto count = min(new_length, current_length - first);
  217. // b. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, count).
  218. copy_data_block_bytes(to_buf, 0, from_buf, first, count);
  219. }
  220. // 28. Return new.
  221. return new_array_buffer_object;
  222. }
  223. // 25.1.6.8 ArrayBuffer.prototype.transfer ( [ newLength ] ), https://tc39.es/ecma262/#sec-arraybuffer.prototype.transfer
  224. JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::transfer)
  225. {
  226. // 1. Let O be the this value.
  227. auto array_buffer_object = TRY(typed_this_value(vm));
  228. // 2. Return ? ArrayBufferCopyAndDetach(O, newLength, PRESERVE-RESIZABILITY).
  229. auto new_length = vm.argument(0);
  230. return TRY(array_buffer_copy_and_detach(vm, array_buffer_object, new_length, PreserveResizability::PreserveResizability));
  231. }
  232. // 25.1.6.9 ArrayBuffer.prototype.transferToFixedLength ( [ newLength ] ), https://tc39.es/ecma262/#sec-arraybuffer.prototype.transfertofixedlength
  233. JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::transfer_to_fixed_length)
  234. {
  235. // 1. Let O be the this value.
  236. auto array_buffer_object = TRY(typed_this_value(vm));
  237. // 2. Return ? ArrayBufferCopyAndDetach(O, newLength, FIXED-LENGTH).
  238. auto new_length = vm.argument(0);
  239. return TRY(array_buffer_copy_and_detach(vm, array_buffer_object, new_length, PreserveResizability::FixedLength));
  240. }
  241. }