boa_engine/builtins/array_buffer/shared.rs
1use std::{
2 ptr,
3 sync::{Arc, atomic::Ordering},
4};
5
6use portable_atomic::{AtomicU8, AtomicUsize};
7
8use boa_gc::{Finalize, Trace};
9
10use crate::{
11 Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
12 builtins::{
13 Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,
14 array_buffer::{AlignedBox, AlignedVec},
15 },
16 context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
17 js_string,
18 object::internal_methods::get_prototype_from_constructor,
19 property::Attribute,
20 realm::Realm,
21 string::StaticJsStrings,
22};
23
24use super::{get_max_byte_len, utils::copy_shared_to_shared};
25
26/// The internal representation of a `SharedArrayBuffer` object.
27///
28/// This struct implements `Send` and `Sync`, meaning it can be shared between threads
29/// running different JS code at the same time.
30#[derive(Debug, Clone, Trace, Finalize, JsData)]
31pub struct SharedArrayBuffer {
32 // Shared buffers cannot be detached.
33 #[unsafe_ignore_trace]
34 data: Arc<Inner>,
35}
36
37#[derive(Debug)]
38struct Inner {
39 // Technically we should have an `[[ArrayBufferData]]` internal slot,
40 // `[[ArrayBufferByteLengthData]]` and `[[ArrayBufferMaxByteLength]]` slots for growable arrays
41 // or `[[ArrayBufferByteLength]]` for fixed arrays, but we can save some work
42 // by just using this representation instead.
43 //
44 // The maximum buffer length is represented by `buffer.len()`, and `current_len` has the current
45 // buffer length, or `None` if this is a fixed buffer; in this case, `buffer.len()` will be
46 // the true length of the buffer.
47 buffer: AlignedBox<[AtomicU8]>,
48 current_len: Option<AtomicUsize>,
49}
50
51impl Default for Inner {
52 fn default() -> Self {
53 Self {
54 buffer: AlignedVec::new(0).into_boxed_slice(),
55 current_len: None,
56 }
57 }
58}
59
60impl SharedArrayBuffer {
61 /// Creates a `SharedArrayBuffer` with an empty buffer.
62 #[must_use]
63 pub fn empty() -> Self {
64 Self {
65 data: Arc::default(),
66 }
67 }
68
69 /// Gets the length of this `SharedArrayBuffer`.
70 pub(crate) fn len(&self, ordering: Ordering) -> usize {
71 self.data
72 .current_len
73 .as_ref()
74 .map_or_else(|| self.data.buffer.len(), |len| len.load(ordering))
75 }
76
77 /// Gets the inner bytes of this `SharedArrayBuffer`.
78 pub(crate) fn bytes(&self, ordering: Ordering) -> &[AtomicU8] {
79 &self.data.buffer[..self.len(ordering)]
80 }
81
82 /// Gets the inner data of the buffer without accessing the current atomic length.
83 #[track_caller]
84 pub(crate) fn bytes_with_len(&self, len: usize) -> &[AtomicU8] {
85 &self.data.buffer[..len]
86 }
87
88 /// Gets a pointer to the internal shared buffer.
89 pub(crate) fn as_ptr(&self) -> *const AtomicU8 {
90 (*self.data.buffer).as_ptr()
91 }
92
93 pub(crate) fn is_fixed_len(&self) -> bool {
94 self.data.current_len.is_none()
95 }
96}
97
98impl IntrinsicObject for SharedArrayBuffer {
99 fn init(realm: &Realm) {
100 let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE;
101
102 let get_species = BuiltInBuilder::callable(realm, Self::get_species)
103 .name(js_string!("get [Symbol.species]"))
104 .build();
105
106 let get_byte_length = BuiltInBuilder::callable(realm, Self::get_byte_length)
107 .name(js_string!("get byteLength"))
108 .build();
109
110 let get_growable = BuiltInBuilder::callable(realm, Self::get_growable)
111 .name(js_string!("get growable"))
112 .build();
113
114 let get_max_byte_length = BuiltInBuilder::callable(realm, Self::get_max_byte_length)
115 .name(js_string!("get maxByteLength"))
116 .build();
117
118 BuiltInBuilder::from_standard_constructor::<Self>(realm)
119 .static_accessor(
120 JsSymbol::species(),
121 Some(get_species),
122 None,
123 Attribute::CONFIGURABLE,
124 )
125 .accessor(
126 js_string!("byteLength"),
127 Some(get_byte_length),
128 None,
129 flag_attributes,
130 )
131 .accessor(
132 js_string!("growable"),
133 Some(get_growable),
134 None,
135 flag_attributes,
136 )
137 .accessor(
138 js_string!("maxByteLength"),
139 Some(get_max_byte_length),
140 None,
141 flag_attributes,
142 )
143 .method(Self::slice, js_string!("slice"), 2)
144 .method(Self::grow, js_string!("grow"), 1)
145 .property(
146 JsSymbol::to_string_tag(),
147 Self::NAME,
148 Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
149 )
150 .build();
151 }
152
153 fn get(intrinsics: &Intrinsics) -> JsObject {
154 Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
155 }
156}
157
158impl BuiltInObject for SharedArrayBuffer {
159 const NAME: JsString = StaticJsStrings::SHARED_ARRAY_BUFFER;
160}
161
162impl BuiltInConstructor for SharedArrayBuffer {
163 const CONSTRUCTOR_ARGUMENTS: usize = 1;
164 const PROTOTYPE_STORAGE_SLOTS: usize = 9;
165 const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;
166
167 const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
168 StandardConstructors::shared_array_buffer;
169
170 /// `25.1.3.1 SharedArrayBuffer ( length [ , options ] )`
171 ///
172 /// More information:
173 /// - [ECMAScript reference][spec]
174 ///
175 /// [spec]: https://tc39.es/ecma262/#sec-sharedarraybuffer-constructor
176 fn constructor(
177 new_target: &JsValue,
178 args: &[JsValue],
179 context: &mut Context,
180 ) -> JsResult<JsValue> {
181 // 1. If NewTarget is undefined, throw a TypeError exception.
182 if new_target.is_undefined() {
183 return Err(JsNativeError::typ()
184 .with_message("ArrayBuffer.constructor called with undefined new target")
185 .into());
186 }
187
188 // 2. Let byteLength be ? ToIndex(length).
189 let byte_len = args.get_or_undefined(0).to_index(context)?;
190
191 // 3. Let requestedMaxByteLength be ? GetArrayBufferMaxByteLengthOption(options).
192 let max_byte_len = get_max_byte_len(args.get_or_undefined(1), context)?;
193
194 // 4. Return ? AllocateSharedArrayBuffer(NewTarget, byteLength, requestedMaxByteLength).
195 Ok(Self::allocate(new_target, byte_len, max_byte_len, context)?
196 .upcast()
197 .into())
198 }
199}
200
201impl SharedArrayBuffer {
202 /// `get SharedArrayBuffer [ @@species ]`
203 ///
204 /// More information:
205 /// - [ECMAScript reference][spec]
206 ///
207 /// [spec]: https://tc39.es/ecma262/#sec-sharedarraybuffer-@@species
208 #[allow(clippy::unnecessary_wraps)]
209 fn get_species(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
210 // 1. Return the this value.
211 Ok(this.clone())
212 }
213
214 /// `get SharedArrayBuffer.prototype.byteLength`
215 ///
216 /// More information:
217 /// - [ECMAScript reference][spec]
218 ///
219 /// [spec]: https://tc39.es/ecma262/#sec-get-sharedarraybuffer.prototype.bytelength
220 pub(crate) fn get_byte_length(
221 this: &JsValue,
222 _args: &[JsValue],
223 _: &mut Context,
224 ) -> JsResult<JsValue> {
225 // 1. Let O be the this value.
226 // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
227 // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.
228 let object = this.as_object();
229 let buf = object
230 .as_ref()
231 .and_then(JsObject::downcast_ref::<Self>)
232 .ok_or_else(|| {
233 JsNativeError::typ()
234 .with_message("SharedArrayBuffer.byteLength called with invalid value")
235 })?;
236
237 // 4. Let length be ArrayBufferByteLength(O, seq-cst).
238 let len = buf.bytes(Ordering::SeqCst).len() as u64;
239
240 // 5. Return 𝔽(length).
241 Ok(len.into())
242 }
243
244 /// [`get SharedArrayBuffer.prototype.growable`][spec].
245 ///
246 /// [spec]: https://tc39.es/ecma262/#sec-get-sharedarraybuffer.prototype.growable
247 pub(crate) fn get_growable(
248 this: &JsValue,
249 _args: &[JsValue],
250 _context: &mut Context,
251 ) -> JsResult<JsValue> {
252 // 1. Let O be the this value.
253 // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
254 // 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
255 let object = this.as_object();
256 let buf = object
257 .as_ref()
258 .and_then(JsObject::downcast_ref::<Self>)
259 .ok_or_else(|| {
260 JsNativeError::typ()
261 .with_message("get SharedArrayBuffer.growable called with invalid `this`")
262 })?;
263
264 // 4. If IsFixedLengthArrayBuffer(O) is false, return true; otherwise return false.
265 Ok(JsValue::from(!buf.is_fixed_len()))
266 }
267
268 /// [`get SharedArrayBuffer.prototype.maxByteLength`][spec].
269 ///
270 /// [spec]: https://tc39.es/ecma262/#sec-get-sharedarraybuffer.prototype.maxbytelength
271 pub(crate) fn get_max_byte_length(
272 this: &JsValue,
273 _args: &[JsValue],
274 _context: &mut Context,
275 ) -> JsResult<JsValue> {
276 // 1. Let O be the this value.
277 // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
278 // 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
279 let object = this.as_object();
280 let buf = object
281 .as_ref()
282 .and_then(JsObject::downcast_ref::<Self>)
283 .ok_or_else(|| {
284 JsNativeError::typ()
285 .with_message("get SharedArrayBuffer.maxByteLength called with invalid value")
286 })?;
287
288 // 4. If IsFixedLengthArrayBuffer(O) is true, then
289 // a. Let length be O.[[ArrayBufferByteLength]].
290 // 5. Else,
291 // a. Let length be O.[[ArrayBufferMaxByteLength]].
292 // 6. Return 𝔽(length).
293 Ok(buf.data.buffer.len().into())
294 }
295
296 /// [`SharedArrayBuffer.prototype.grow ( newLength )`][spec].
297 ///
298 /// [spec]: https://tc39.es/ecma262/sec-sharedarraybuffer.prototype.grow
299 pub(crate) fn grow(
300 this: &JsValue,
301 args: &[JsValue],
302 context: &mut Context,
303 ) -> JsResult<JsValue> {
304 // 1. Let O be the this value.
305 // 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
306 let Some(buf) = this
307 .as_object()
308 .and_then(|o| o.clone().downcast::<Self>().ok())
309 else {
310 return Err(JsNativeError::typ()
311 .with_message("SharedArrayBuffer.grow called with non-object value")
312 .into());
313 };
314
315 // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).
316 if buf.borrow().data().is_fixed_len() {
317 return Err(JsNativeError::typ()
318 .with_message("SharedArrayBuffer.grow: cannot grow a fixed-length buffer")
319 .into());
320 }
321
322 // 4. Let newByteLength be ? ToIndex(newLength).
323 let new_byte_len = args.get_or_undefined(0).to_index(context)?;
324
325 // TODO: 5. Let hostHandled be ? HostGrowSharedArrayBuffer(O, newByteLength).
326 // 6. If hostHandled is handled, return undefined.
327 // Used in engines to handle WASM buffers in a special way, but we don't
328 // have a WASM interpreter in place yet.
329
330 // 7. Let isLittleEndian be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
331 // 8. Let byteLengthBlock be O.[[ArrayBufferByteLengthData]].
332 // 9. Let currentByteLengthRawBytes be GetRawBytesFromSharedBlock(byteLengthBlock, 0, biguint64, true, seq-cst).
333 // 10. Let newByteLengthRawBytes be NumericToRawBytes(biguint64, ℤ(newByteLength), isLittleEndian).
334
335 let buf = buf.borrow();
336 let buf = &buf.data();
337
338 // d. If newByteLength < currentByteLength or newByteLength > O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.
339 // Extracting this condition outside the CAS since throwing early doesn't affect the correct
340 // behaviour of the loop.
341 if new_byte_len > buf.data.buffer.len() as u64 {
342 return Err(JsNativeError::range()
343 .with_message(
344 "SharedArrayBuffer.grow: new length cannot be bigger than `maxByteLength`",
345 )
346 .into());
347 }
348 let new_byte_len = new_byte_len as usize;
349
350 // If we used let-else above to avoid the expect, we would carry a borrow through the `to_index`
351 // call, which could mutably borrow. Another alternative would be to clone the whole
352 // `SharedArrayBuffer`, but it's better to avoid contention with the counter in the `Arc` pointer.
353 let atomic_len = buf
354 .data
355 .current_len
356 .as_ref()
357 .expect("already checked that the buffer is not fixed-length");
358
359 // 11. Repeat,
360 // a. NOTE: This is a compare-and-exchange loop to ensure that parallel, racing grows of the same buffer are
361 // totally ordered, are not lost, and do not silently do nothing. The loop exits if it was able to attempt
362 // to grow uncontended.
363 // b. Let currentByteLength be ℝ(RawBytesToNumeric(biguint64, currentByteLengthRawBytes, isLittleEndian)).
364 // c. If newByteLength = currentByteLength, return undefined.
365 // d. If newByteLength < currentByteLength or newByteLength > O.[[ArrayBufferMaxByteLength]], throw a
366 // RangeError exception.
367 // e. Let byteLengthDelta be newByteLength - currentByteLength.
368 // f. If it is impossible to create a new Shared Data Block value consisting of byteLengthDelta bytes, throw
369 // a RangeError exception.
370 // g. NOTE: No new Shared Data Block is constructed and used here. The observable behaviour of growable
371 // SharedArrayBuffers is specified by allocating a max-sized Shared Data Block at construction time, and
372 // this step captures the requirement that implementations that run out of memory must throw a RangeError.
373 // h. Let readByteLengthRawBytes be AtomicCompareExchangeInSharedBlock(byteLengthBlock, 0, 8,
374 // currentByteLengthRawBytes, newByteLengthRawBytes).
375 // i. If ByteListEqual(readByteLengthRawBytes, currentByteLengthRawBytes) is true, return undefined.
376 // j. Set currentByteLengthRawBytes to readByteLengthRawBytes.
377
378 // We require SEQ-CST operations because readers of the buffer also use SEQ-CST operations.
379 atomic_len
380 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev_byte_len| {
381 (prev_byte_len <= new_byte_len).then_some(new_byte_len)
382 })
383 .map_err(|_| {
384 JsNativeError::range()
385 .with_message("SharedArrayBuffer.grow: failed to grow buffer to new length")
386 })?;
387
388 Ok(JsValue::undefined())
389 }
390
391 /// `SharedArrayBuffer.prototype.slice ( start, end )`
392 ///
393 /// More information:
394 /// - [ECMAScript reference][spec]
395 ///
396 /// [spec]: https://tc39.es/ecma262/#sec-sharedarraybuffer.prototype.slice
397 fn slice(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
398 // 1. Let O be the this value.
399 // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).
400 // 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
401 let buf = this
402 .as_object()
403 .and_then(|o| o.clone().downcast::<Self>().ok())
404 .ok_or_else(|| {
405 JsNativeError::typ()
406 .with_message("SharedArrayBuffer.slice called with invalid `this` value")
407 })?;
408
409 // 4. Let len be ArrayBufferByteLength(O, seq-cst).
410 let len = buf.borrow().data().len(Ordering::SeqCst);
411
412 // 5. Let relativeStart be ? ToIntegerOrInfinity(start).
413 // 6. If relativeStart = -∞, let first be 0.
414 // 7. Else if relativeStart < 0, let first be max(len + relativeStart, 0).
415 // 8. Else, let first be min(relativeStart, len).
416 let first = Array::get_relative_start(context, args.get_or_undefined(0), len as u64)?;
417
418 // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).
419 // 10. If relativeEnd = -∞, let final be 0.
420 // 11. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).
421 // 12. Else, let final be min(relativeEnd, len).
422 let final_ = Array::get_relative_end(context, args.get_or_undefined(1), len as u64)?;
423
424 // 13. Let newLen be max(final - first, 0).
425 let new_len = final_.saturating_sub(first);
426
427 // 14. Let ctor be ? SpeciesConstructor(O, %SharedArrayBuffer%).
428 let ctor = buf
429 .clone()
430 .upcast()
431 .species_constructor(StandardConstructors::shared_array_buffer, context)?;
432
433 // 15. Let new be ? Construct(ctor, « 𝔽(newLen) »).
434 let new = ctor.construct(&[new_len.into()], Some(&ctor), context)?;
435
436 {
437 let buf = buf.borrow();
438 let buf = &buf.data();
439 // 16. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]).
440 // 17. If IsSharedArrayBuffer(new) is false, throw a TypeError exception.
441 let new = new.downcast_ref::<Self>().ok_or_else(|| {
442 JsNativeError::typ()
443 .with_message("SharedArrayBuffer constructor returned invalid object")
444 })?;
445
446 // 18. If new.[[ArrayBufferData]] is O.[[ArrayBufferData]], throw a TypeError exception.
447 if ptr::eq(buf.as_ptr(), new.as_ptr()) {
448 return Err(JsNativeError::typ()
449 .with_message("cannot reuse the same SharedArrayBuffer for a slice operation")
450 .into());
451 }
452
453 // 19. If ArrayBufferByteLength(new, seq-cst) < newLen, throw a TypeError exception.
454 if (new.len(Ordering::SeqCst) as u64) < new_len {
455 return Err(JsNativeError::typ()
456 .with_message("invalid size of constructed SharedArrayBuffer")
457 .into());
458 }
459
460 let first = first as usize;
461 let new_len = new_len as usize;
462
463 // 20. Let fromBuf be O.[[ArrayBufferData]].
464 let from_buf = &buf.bytes_with_len(len)[first..];
465
466 // 21. Let toBuf be new.[[ArrayBufferData]].
467 let to_buf = new;
468
469 // Sanity check to ensure there is enough space inside `from_buf` for
470 // `new_len` elements.
471 debug_assert!(from_buf.len() >= new_len);
472
473 // 22. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).
474 // SAFETY: `get_slice_range` will always return indices that are in-bounds.
475 // This also means that the newly created buffer will have at least `new_len` elements
476 // to write to.
477 unsafe { copy_shared_to_shared(from_buf.as_ptr(), to_buf.as_ptr(), new_len) }
478 }
479
480 // 23. Return new.
481 Ok(new.into())
482 }
483
484 /// `AllocateSharedArrayBuffer ( constructor, byteLength [ , maxByteLength ] )`
485 ///
486 /// More information:
487 /// - [ECMAScript reference][spec]
488 ///
489 /// [spec]: https://tc39.es/ecma262/#sec-allocatesharedarraybuffer
490 pub(crate) fn allocate(
491 constructor: &JsValue,
492 byte_len: u64,
493 max_byte_len: Option<u64>,
494 context: &mut Context,
495 ) -> JsResult<JsObject<SharedArrayBuffer>> {
496 // 1. Let slots be « [[ArrayBufferData]] ».
497 // 2. If maxByteLength is present and maxByteLength is not empty, let allocatingGrowableBuffer
498 // be true; otherwise let allocatingGrowableBuffer be false.
499 // 3. If allocatingGrowableBuffer is true, then
500 // a. If byteLength > maxByteLength, throw a RangeError exception.
501 // b. Append [[ArrayBufferByteLengthData]] and [[ArrayBufferMaxByteLength]] to slots.
502 // 4. Else,
503 // a. Append [[ArrayBufferByteLength]] to slots.
504 if let Some(max_byte_len) = max_byte_len
505 && byte_len > max_byte_len
506 {
507 return Err(JsNativeError::range()
508 .with_message("`length` cannot be bigger than `maxByteLength`")
509 .into());
510 }
511
512 // 5. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%SharedArrayBuffer.prototype%", slots).
513 let prototype = get_prototype_from_constructor(
514 constructor,
515 StandardConstructors::shared_array_buffer,
516 context,
517 )?;
518
519 // 6. If allocatingGrowableBuffer is true, let allocLength be maxByteLength;
520 // otherwise let allocLength be byteLength.
521 let alloc_len = max_byte_len.unwrap_or(byte_len);
522
523 // 7. Let block be ? CreateSharedByteDataBlock(allocLength).
524 // 8. Set obj.[[ArrayBufferData]] to block.
525 let block = create_shared_byte_data_block(alloc_len, context)?;
526
527 // 9. If allocatingGrowableBuffer is true, then
528 // `byte_len` must fit inside an `usize` thanks to the checks inside
529 // `create_shared_byte_data_block`.
530 // a. Assert: byteLength ≤ maxByteLength.
531 // b. Let byteLengthBlock be ? CreateSharedByteDataBlock(8).
532 // c. Perform SetValueInBuffer(byteLengthBlock, 0, biguint64, ℤ(byteLength), true, seq-cst).
533 // d. Set obj.[[ArrayBufferByteLengthData]] to byteLengthBlock.
534 // e. Set obj.[[ArrayBufferMaxByteLength]] to maxByteLength.
535 let current_len = max_byte_len.map(|_| AtomicUsize::new(byte_len as usize));
536
537 // 10. Else,
538 // a. Set obj.[[ArrayBufferByteLength]] to byteLength.
539 let obj = JsObject::new(
540 context.root_shape(),
541 prototype,
542 Self {
543 data: Arc::new(Inner {
544 buffer: block,
545 current_len,
546 }),
547 },
548 );
549
550 // 11. Return obj.
551 Ok(obj)
552 }
553}
554
555/// [`CreateSharedByteDataBlock ( size )`][spec] abstract operation.
556///
557/// Creates a new `Arc<Vec<AtomicU8>>` that can be used as a backing buffer for a [`SharedArrayBuffer`].
558///
559/// For more information, check the [spec][spec].
560///
561/// [spec]: https://tc39.es/ecma262/#sec-createsharedbytedatablock
562pub(crate) fn create_shared_byte_data_block(
563 size: u64,
564 context: &mut Context,
565) -> JsResult<AlignedBox<[AtomicU8]>> {
566 if size > context.host_hooks().max_buffer_size(context) {
567 return Err(JsNativeError::range()
568 .with_message(
569 "cannot allocate a buffer that exceeds the maximum buffer size".to_string(),
570 )
571 .into());
572 }
573
574 // 1. Let db be a new Shared Data Block value consisting of size bytes. If it is impossible to
575 // create such a Shared Data Block, throw a RangeError exception.
576 let size = size.try_into().map_err(|e| {
577 JsNativeError::range().with_message(format!("couldn't allocate the data block: {e}"))
578 })?;
579
580 if size == 0 {
581 // Must ensure we don't allocate a zero-sized buffer.
582 return Ok(AlignedVec::new(0).into_boxed_slice());
583 }
584
585 // 2. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
586 // 3. Let eventsRecord be the Agent Events Record of execution.[[EventsRecords]] whose
587 // [[AgentSignifier]] is AgentSignifier().
588 // 4. Let zero be « 0 ».
589 // 5. For each index i of db, do
590 // a. Append WriteSharedMemory { [[Order]]: init, [[NoTear]]: true, [[Block]]: db,
591 // [[ByteIndex]]: i, [[ElementSize]]: 1, [[Payload]]: zero } to eventsRecord.[[EventList]].
592 // 6. Return db.
593 let mut buf = AlignedVec::<u8>::new(0);
594 buf.try_reserve_exact(size).map_err(|e| {
595 let message = match e {
596 aligned_vec::TryReserveError::CapacityOverflow => {
597 format!("capacity overflow for size {size} while allocating data block")
598 }
599 aligned_vec::TryReserveError::AllocError { layout } => {
600 format!("invalid layout {layout:?} while allocating data block")
601 }
602 };
603 JsNativeError::range().with_message(message)
604 })?;
605 buf.resize(size, 0);
606 buf.shrink_to_fit();
607 let (data, align, len, _) = buf.into_raw_parts();
608
609 // 3. Return db.
610 // SAFETY: `[u8]` must be transparently castable to `[AtomicU8]`.
611 Ok(unsafe {
612 AlignedBox::from_raw_parts(align, ptr::slice_from_raw_parts_mut(data.cast(), len))
613 })
614}