1#![expect(unsafe_code)]
6
7#[cfg(feature = "webgpu")]
8use std::ffi::c_void;
9use std::marker::PhantomData;
10#[cfg(feature = "webgpu")]
11use std::ops::Range;
12use std::ptr;
13#[cfg(feature = "webgpu")]
14use std::sync::Arc;
15
16#[cfg(feature = "webgpu")]
17use js::jsapi::NewExternalArrayBuffer;
18use js::jsapi::{
19 ArrayBufferClone, ArrayBufferCopyData, GetArrayBufferByteLength,
20 HasDefinedArrayBufferDetachKey, Heap, IsArrayBufferObject, IsDetachedArrayBufferObject,
21 JS_ClearPendingException, JS_GetArrayBufferViewBuffer, JS_GetArrayBufferViewByteLength,
22 JS_GetArrayBufferViewByteOffset, JS_GetArrayBufferViewType, JS_GetPendingException,
23 JS_GetTypedArrayLength, JS_IsArrayBufferViewObject, JS_IsTypedArrayObject,
24 JS_NewBigInt64ArrayWithBuffer, JS_NewBigUint64ArrayWithBuffer, JS_NewDataView,
25 JS_NewFloat16ArrayWithBuffer, JS_NewFloat32ArrayWithBuffer, JS_NewFloat64ArrayWithBuffer,
26 JS_NewInt8ArrayWithBuffer, JS_NewInt16ArrayWithBuffer, JS_NewInt32ArrayWithBuffer,
27 JS_NewUint8ArrayWithBuffer, JS_NewUint8ClampedArrayWithBuffer, JS_NewUint16ArrayWithBuffer,
28 JS_NewUint32ArrayWithBuffer, JSObject, NewArrayBuffer, NewArrayBufferWithContents,
29 StealArrayBufferContents, Type,
30};
31use js::jsval::{ObjectValue, UndefinedValue};
32use js::rust::wrappers::DetachArrayBuffer;
33use js::rust::{
34 CustomAutoRooterGuard, Handle, MutableHandleObject,
35 MutableHandleValue as SafeMutableHandleValue,
36};
37#[cfg(feature = "webgpu")]
38use js::typedarray::HeapArrayBuffer;
39use js::typedarray::{
40 ArrayBufferU8, ArrayBufferViewU8, CreateWith, TypedArray, TypedArrayElement,
41 TypedArrayElementCreator,
42};
43
44use crate::dom::bindings::error::{Error, Fallible};
45use crate::dom::bindings::trace::RootedTraceableBox;
46#[cfg(feature = "webgpu")]
47use crate::dom::globalscope::GlobalScope;
48use crate::script_runtime::{CanGc, JSContext};
49
50pub(crate) type RootedTypedArray<T> = RootedTraceableBox<TypedArray<T, Box<Heap<*mut JSObject>>>>;
51
52#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
59pub(crate) enum BufferSource {
60 ArrayBufferView(Box<Heap<*mut JSObject>>),
63
64 ArrayBuffer(Box<Heap<*mut JSObject>>),
67}
68
69impl Clone for BufferSource {
70 fn clone(&self) -> Self {
71 match self {
72 BufferSource::ArrayBufferView(heap) => {
73 BufferSource::ArrayBufferView(Heap::boxed(heap.get()))
74 },
75 BufferSource::ArrayBuffer(heap) => BufferSource::ArrayBuffer(Heap::boxed(heap.get())),
76 }
77 }
78}
79
80pub(crate) fn create_heap_buffer_source_with_length<T>(
81 cx: JSContext,
82 len: u32,
83 can_gc: CanGc,
84) -> Fallible<RootedTraceableBox<HeapBufferSource<T>>>
85where
86 T: TypedArrayElement + TypedArrayElementCreator + 'static,
87 T::Element: Clone + Copy,
88{
89 rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
90 let typed_array_result =
91 create_buffer_source_with_length::<T>(cx, len as usize, array.handle_mut(), can_gc);
92 if typed_array_result.is_err() {
93 return Err(Error::JSFailed);
94 }
95
96 Ok(RootedTraceableBox::new(HeapBufferSource::<T>::new(
97 BufferSource::ArrayBufferView(Heap::boxed(*array.handle())),
98 )))
99}
100
101#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
102pub(crate) struct HeapBufferSource<T> {
103 buffer_source: BufferSource,
104 phantom: PhantomData<T>,
105}
106
107impl<T> Eq for HeapBufferSource<T> where T: TypedArrayElement {}
108
109impl<T> PartialEq for HeapBufferSource<T>
110where
111 T: TypedArrayElement,
112{
113 fn eq(&self, other: &Self) -> bool {
114 match &self.buffer_source {
115 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => match &other
116 .buffer_source
117 {
118 BufferSource::ArrayBufferView(from_heap) | BufferSource::ArrayBuffer(from_heap) => {
119 std::ptr::eq(heap.get(), from_heap.get())
120 },
121 },
122 }
123 }
124}
125
126impl<T> Clone for HeapBufferSource<T>
127where
128 T: TypedArrayElement,
129{
130 fn clone(&self) -> Self {
131 HeapBufferSource {
132 buffer_source: self.buffer_source.clone(),
133 phantom: PhantomData,
134 }
135 }
136}
137
138impl<T> HeapBufferSource<T>
139where
140 T: TypedArrayElement,
141{
142 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
143 pub(crate) fn new(buffer_source: BufferSource) -> HeapBufferSource<T> {
144 HeapBufferSource {
145 buffer_source,
146 phantom: PhantomData,
147 }
148 }
149
150 pub(crate) fn from_view(
151 chunk: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
152 ) -> RootedTraceableBox<HeapBufferSource<T>> {
153 RootedTraceableBox::new(HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(
154 Heap::boxed(unsafe { *chunk.underlying_object() }),
155 )))
156 }
157
158 pub(crate) fn default() -> Self {
159 HeapBufferSource {
160 buffer_source: BufferSource::ArrayBufferView(Heap::boxed(std::ptr::null_mut())),
161 phantom: PhantomData,
162 }
163 }
164
165 pub(crate) fn is_initialized(&self) -> bool {
166 match &self.buffer_source {
167 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
168 !buffer.get().is_null()
169 },
170 }
171 }
172
173 pub(crate) fn get_typed_array(&self) -> Result<RootedTypedArray<T>, ()> {
174 TypedArray::from(match &self.buffer_source {
175 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
176 buffer.get()
177 },
178 })
179 .map(RootedTraceableBox::new)
180 }
181
182 pub(crate) fn get_buffer_view_value(
183 &self,
184 cx: JSContext,
185 mut handle_mut: SafeMutableHandleValue,
186 ) {
187 match &self.buffer_source {
188 BufferSource::ArrayBufferView(buffer) => {
189 rooted!(in(*cx) let value = ObjectValue(buffer.get()));
190 handle_mut.set(*value);
191 },
192 BufferSource::ArrayBuffer(_) => {
193 unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
194 },
195 }
196 }
197
198 pub(crate) fn get_array_buffer_view_buffer(
199 &self,
200 cx: JSContext,
201 ) -> RootedTraceableBox<HeapBufferSource<ArrayBufferU8>> {
202 match &self.buffer_source {
203 BufferSource::ArrayBufferView(buffer) => unsafe {
204 let mut is_shared = false;
205 rooted!(in (*cx) let view_buffer =
206 JS_GetArrayBufferViewBuffer(*cx, buffer.handle(), &mut is_shared));
207
208 RootedTraceableBox::new(HeapBufferSource::<ArrayBufferU8>::new(
209 BufferSource::ArrayBuffer(Heap::boxed(*view_buffer.handle())),
210 ))
211 },
212 BufferSource::ArrayBuffer(_) => {
213 unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
214 },
215 }
216 }
217
218 pub(crate) fn detach_buffer(&self, cx: JSContext) -> bool {
220 assert!(self.is_initialized());
221 match &self.buffer_source {
222 BufferSource::ArrayBufferView(buffer) => {
223 let mut is_shared = false;
224 unsafe {
225 assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
227 rooted!(in (*cx) let view_buffer =
228 JS_GetArrayBufferViewBuffer(*cx, buffer.handle(), &mut is_shared));
229 debug_assert!(!is_shared);
231 DetachArrayBuffer(*cx, view_buffer.handle())
233 }
234 },
235 BufferSource::ArrayBuffer(buffer) => unsafe {
236 DetachArrayBuffer(*cx, Handle::from_raw(buffer.handle()))
237 },
238 }
239 }
240
241 pub(crate) fn typed_array_to_option(&self) -> Option<RootedTypedArray<T>> {
242 if self.is_initialized() {
243 self.get_typed_array().ok()
244 } else {
245 warn!("Buffer not initialized.");
246 None
247 }
248 }
249
250 pub(crate) fn is_detached_buffer(&self, cx: JSContext) -> bool {
251 assert!(self.is_initialized());
252 match &self.buffer_source {
253 BufferSource::ArrayBufferView(buffer) => {
254 let mut is_shared = false;
255 unsafe {
256 assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
257 rooted!(in (*cx) let view_buffer =
258 JS_GetArrayBufferViewBuffer(*cx, buffer.handle(), &mut is_shared));
259 debug_assert!(!is_shared);
260 IsDetachedArrayBufferObject(*view_buffer.handle())
261 }
262 },
263 BufferSource::ArrayBuffer(buffer) => unsafe {
264 IsDetachedArrayBufferObject(*buffer.handle())
265 },
266 }
267 }
268
269 pub(crate) fn viewed_buffer_array_byte_length(&self, cx: JSContext) -> usize {
270 assert!(self.is_initialized());
271 match &self.buffer_source {
272 BufferSource::ArrayBufferView(buffer) => {
273 let mut is_shared = false;
274 unsafe {
275 assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
276 rooted!(in (*cx) let view_buffer =
277 JS_GetArrayBufferViewBuffer(*cx, buffer.handle(), &mut is_shared));
278 debug_assert!(!is_shared);
279 GetArrayBufferByteLength(*view_buffer.handle())
280 }
281 },
282 BufferSource::ArrayBuffer(buffer) => unsafe {
283 GetArrayBufferByteLength(*buffer.handle())
284 },
285 }
286 }
287
288 pub(crate) fn byte_length(&self) -> usize {
289 match &self.buffer_source {
290 BufferSource::ArrayBufferView(buffer) => unsafe {
291 JS_GetArrayBufferViewByteLength(*buffer.handle())
292 },
293 BufferSource::ArrayBuffer(buffer) => unsafe {
294 GetArrayBufferByteLength(*buffer.handle())
295 },
296 }
297 }
298
299 pub(crate) fn get_byte_offset(&self) -> usize {
300 match &self.buffer_source {
301 BufferSource::ArrayBufferView(buffer) => unsafe {
302 JS_GetArrayBufferViewByteOffset(*buffer.handle())
303 },
304 BufferSource::ArrayBuffer(_) => {
305 unreachable!("BufferSource::ArrayBuffer does not have a byte offset.")
306 },
307 }
308 }
309
310 pub(crate) fn get_typed_array_length(&self) -> usize {
311 match &self.buffer_source {
312 BufferSource::ArrayBufferView(buffer) => unsafe {
313 JS_GetTypedArrayLength(*buffer.handle())
314 },
315 BufferSource::ArrayBuffer(_) => {
316 unreachable!("BufferSource::ArrayBuffer does not have a length.")
317 },
318 }
319 }
320
321 pub(crate) fn has_typed_array_name(&self) -> bool {
323 match &self.buffer_source {
324 BufferSource::ArrayBufferView(buffer) => unsafe {
325 JS_IsTypedArrayObject(*buffer.handle())
326 },
327 BufferSource::ArrayBuffer(_) => false,
328 }
329 }
330
331 pub(crate) fn get_array_buffer_view_type(&self) -> Type {
332 match &self.buffer_source {
333 BufferSource::ArrayBufferView(buffer) => unsafe {
334 JS_GetArrayBufferViewType(*buffer.handle())
335 },
336 BufferSource::ArrayBuffer(_) => unreachable!("ArrayBuffer does not have a view type."),
337 }
338 }
339
340 pub(crate) fn is_array_buffer_object(&self) -> bool {
341 match &self.buffer_source {
342 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
343 IsArrayBufferObject(*heap.handle())
344 },
345 }
346 }
347
348 pub(crate) fn clone_array_buffer(
350 &self,
351 cx: &mut js::context::JSContext,
352 byte_offset: usize,
353 byte_length: usize,
354 ) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferU8>>> {
355 let result = match &self.buffer_source {
356 BufferSource::ArrayBufferView(buffer) => {
357 let mut is_shared = false;
358 rooted!(&in(cx) let view_buffer =
359 unsafe { JS_GetArrayBufferViewBuffer(cx.raw_cx(), buffer.handle(), &mut is_shared) });
360 debug_assert!(!is_shared);
361
362 unsafe {
363 ArrayBufferClone(
364 cx.raw_cx(),
365 view_buffer.handle().into(),
366 byte_offset,
367 byte_length,
368 )
369 }
370 },
371 BufferSource::ArrayBuffer(buffer) => unsafe {
372 ArrayBufferClone(cx.raw_cx(), buffer.handle(), byte_offset, byte_length)
373 },
374 };
375
376 if result.is_null() {
377 rooted!(&in(cx) let mut _ex = UndefinedValue());
380 unsafe {
381 if JS_GetPendingException(cx.raw_cx(), _ex.handle_mut().into()) {
383 JS_ClearPendingException(cx.raw_cx());
384 }
385 }
386
387 Err(Error::Type(c"can't clone array buffer".to_owned()))
388 } else {
389 Ok(RootedTraceableBox::new(
390 HeapBufferSource::<ArrayBufferU8>::new(BufferSource::ArrayBuffer(Heap::boxed(
391 result,
392 ))),
393 ))
394 }
395 }
396 #[expect(unsafe_code)]
398 pub(crate) fn clone_as_uint8_array(
399 &self,
400 cx: &mut js::context::JSContext,
401 ) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferViewU8>>> {
402 match &self.buffer_source {
403 BufferSource::ArrayBufferView(buffer) => {
404 assert!(unsafe { JS_IsArrayBufferViewObject(*buffer.handle()) });
407
408 assert!(!self.is_detached_buffer(cx.into()));
410
411 let byte_offset = self.get_byte_offset();
414 let byte_length = self.byte_length();
415
416 let buffer = self.clone_array_buffer(cx, byte_offset, byte_length)?;
417
418 construct_typed_array(cx, &Type::Uint8, &buffer, 0, byte_length as i64)
421 },
422 BufferSource::ArrayBuffer(_buffer) => {
423 unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
424 },
425 }
426 }
427
428 pub(crate) fn is_undefined(&self) -> bool {
429 match &self.buffer_source {
430 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
431 buffer.get().is_null()
432 },
433 }
434 }
435}
436
437impl<T> HeapBufferSource<T>
438where
439 T: TypedArrayElement + TypedArrayElementCreator + 'static,
440 T::Element: Clone + Copy,
441{
442 pub(crate) fn acquire_data(&self, cx: JSContext) -> Result<Vec<T::Element>, ()> {
443 assert!(self.is_initialized());
444
445 typedarray!(in(*cx) let array: TypedArray = match &self.buffer_source {
446 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
447 => {
448 buffer.get()
449 },
450 });
451 let data = if let Ok(array) =
452 array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
453 {
454 let data = array.to_vec();
455 let _ = self.detach_buffer(cx);
456 Ok(data)
457 } else {
458 Err(())
459 };
460
461 match &self.buffer_source {
462 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
463 buffer.set(ptr::null_mut());
464 },
465 }
466 data
467 }
468
469 pub(crate) fn copy_data_to(
470 &self,
471 cx: JSContext,
472 dest: &mut [T::Element],
473 source_start: usize,
474 length: usize,
475 ) -> Result<(), ()> {
476 assert!(self.is_initialized());
477 typedarray!(in(*cx) let array: TypedArray = match &self.buffer_source {
478 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
479 => {
480 buffer.get()
481 },
482 });
483 let Ok(array) =
484 array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
485 else {
486 return Err(());
487 };
488 unsafe {
489 let slice = (*array).as_slice();
490 dest.copy_from_slice(&slice[source_start..length]);
491 }
492 Ok(())
493 }
494
495 pub(crate) fn copy_data_from(
496 &self,
497 cx: JSContext,
498 source: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
499 dest_start: usize,
500 length: usize,
501 ) -> Result<(), ()> {
502 assert!(self.is_initialized());
503 typedarray!(in(*cx) let mut array: TypedArray = match &self.buffer_source {
504 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
505 => {
506 buffer.get()
507 },
508 });
509 let Ok(mut array) =
510 array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
511 else {
512 return Err(());
513 };
514 unsafe {
515 let slice = (*array).as_mut_slice();
516 let (_, dest) = slice.split_at_mut(dest_start);
517 dest[0..length].copy_from_slice(&source.as_slice()[0..length])
518 }
519 Ok(())
520 }
521
522 pub(crate) fn set_data(
523 &self,
524 cx: JSContext,
525 data: &[T::Element],
526 can_gc: CanGc,
527 ) -> Result<(), ()> {
528 rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
529 let _ = create_buffer_source::<T>(cx, data, array.handle_mut(), can_gc)?;
530
531 match &self.buffer_source {
532 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
533 buffer.set(*array);
534 },
535 }
536 Ok(())
537 }
538
539 pub(crate) fn can_copy_data_block_bytes(
542 &self,
543 cx: JSContext,
544 to_index: usize,
545 from_buffer: &HeapBufferSource<ArrayBufferU8>,
546 from_index: usize,
547 bytes_to_copy: usize,
548 ) -> bool {
549 assert!(self.is_array_buffer_object());
552
553 assert!(from_buffer.is_array_buffer_object());
556
557 match &self.buffer_source {
559 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => {
560 match &from_buffer.buffer_source {
561 BufferSource::ArrayBufferView(from_heap) |
562 BufferSource::ArrayBuffer(from_heap) => {
563 if std::ptr::eq(heap.get(), from_heap.get()) {
564 return false;
565 }
566 },
567 }
568 },
569 }
570
571 if self.is_detached_buffer(cx) {
573 return false;
574 }
575
576 if from_buffer.is_detached_buffer(cx) {
578 return false;
579 }
580
581 if to_index + bytes_to_copy > self.byte_length() {
583 return false;
584 }
585
586 if from_index + bytes_to_copy > from_buffer.byte_length() {
588 return false;
589 }
590
591 true
593 }
594
595 pub(crate) fn copy_data_block_bytes(
596 &self,
597 cx: JSContext,
598 dest_start: usize,
599 from_buffer: &HeapBufferSource<ArrayBufferU8>,
600 from_byte_offset: usize,
601 bytes_to_copy: usize,
602 ) -> bool {
603 match &self.buffer_source {
604 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
605 match &from_buffer.buffer_source {
606 BufferSource::ArrayBufferView(from_heap) |
607 BufferSource::ArrayBuffer(from_heap) => ArrayBufferCopyData(
608 *cx,
609 heap.handle(),
610 dest_start,
611 from_heap.handle(),
612 from_byte_offset,
613 bytes_to_copy,
614 ),
615 }
616 },
617 }
618 }
619
620 pub(crate) fn can_transfer_array_buffer(&self, cx: JSContext) -> bool {
622 assert!(self.is_array_buffer_object());
625
626 if self.is_detached_buffer(cx) {
628 return false;
629 }
630
631 let mut is_defined = false;
634 match &self.buffer_source {
635 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
636 if !HasDefinedArrayBufferDetachKey(*cx, heap.handle(), &mut is_defined) {
637 return false;
638 }
639 },
640 }
641
642 !is_defined
643 }
644
645 pub(crate) fn transfer_array_buffer(
647 &self,
648 cx: JSContext,
649 ) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferU8>>> {
650 assert!(self.is_array_buffer_object());
651
652 assert!(!self.is_detached_buffer(cx));
654
655 let buffer_length = self.byte_length();
658
659 let buffer_data = match &self.buffer_source {
662 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => unsafe {
663 StealArrayBufferContents(*cx, buffer.handle())
664 },
665 };
666
667 if !self.detach_buffer(cx) {
671 rooted!(in(*cx) let mut rval = UndefinedValue());
672 unsafe {
673 assert!(JS_GetPendingException(*cx, rval.handle_mut().into()));
674 JS_ClearPendingException(*cx)
675 };
676
677 Err(Error::Type(c"can't transfer array buffer".to_owned()))
678 } else {
679 Ok(RootedTraceableBox::new(
683 HeapBufferSource::<ArrayBufferU8>::new(BufferSource::ArrayBuffer(Heap::boxed(
684 unsafe { NewArrayBufferWithContents(*cx, buffer_length, buffer_data) },
685 ))),
686 ))
687 }
688 }
689}
690
691unsafe impl<T> crate::dom::bindings::trace::JSTraceable for HeapBufferSource<T> {
692 #[inline]
693 unsafe fn trace(&self, tracer: *mut js::jsapi::JSTracer) {
694 match &self.buffer_source {
695 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
696 unsafe { buffer.trace(tracer) };
697 },
698 }
699 }
700}
701
702pub(crate) fn create_buffer_source<T>(
704 cx: JSContext,
705 data: &[T::Element],
706 mut dest: MutableHandleObject,
707 _can_gc: CanGc,
708) -> Result<RootedTypedArray<T>, ()>
709where
710 T: TypedArrayElement + TypedArrayElementCreator,
711{
712 let res = unsafe {
713 TypedArray::<T, *mut JSObject>::create(*cx, CreateWith::Slice(data), dest.reborrow())
714 };
715
716 if res.is_err() {
717 Err(())
718 } else {
719 TypedArray::from(dest.get()).map(RootedTraceableBox::new)
720 }
721}
722
723fn create_buffer_source_with_length<T>(
724 cx: JSContext,
725 len: usize,
726 mut dest: MutableHandleObject,
727 _can_gc: CanGc,
728) -> Result<RootedTypedArray<T>, ()>
729where
730 T: TypedArrayElement + TypedArrayElementCreator,
731{
732 let res = unsafe {
733 TypedArray::<T, *mut JSObject>::create(*cx, CreateWith::Length(len), dest.reborrow())
734 };
735
736 if res.is_err() {
737 Err(())
738 } else {
739 TypedArray::from(dest.get()).map(RootedTraceableBox::new)
740 }
741}
742
743pub(crate) fn byte_size(byte_type: Type) -> u64 {
744 match byte_type {
745 Type::Int8 | Type::Uint8 | Type::Uint8Clamped => 1,
746 Type::Int16 | Type::Uint16 | Type::Float16 => 2,
747 Type::Int32 | Type::Uint32 | Type::Float32 => 4,
748 Type::Int64 | Type::Float64 | Type::BigInt64 | Type::BigUint64 => 8,
749 Type::Simd128 => 16,
750 _ => unreachable!("invalid scalar type"),
751 }
752}
753
754#[derive(Clone, Eq, JSTraceable, MallocSizeOf, PartialEq)]
755pub(crate) enum Constructor {
756 DataView,
757 Name(
758 #[ignore_malloc_size_of = "mozjs"]
759 #[no_trace]
760 Type,
761 ),
762}
763
764pub(crate) fn create_buffer_source_with_constructor(
765 cx: &mut js::context::JSContext,
766 constructor: &Constructor,
767 buffer_source: &HeapBufferSource<ArrayBufferU8>,
768 byte_offset: usize,
769 byte_length: usize,
770) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferViewU8>>> {
771 match &buffer_source.buffer_source {
772 BufferSource::ArrayBuffer(heap) => match constructor {
773 Constructor::DataView => Ok(RootedTraceableBox::new(HeapBufferSource::new(
774 BufferSource::ArrayBufferView(Heap::boxed(unsafe {
775 JS_NewDataView(cx.raw_cx(), heap.handle(), byte_offset, byte_length)
776 })),
777 ))),
778 Constructor::Name(name_type) => construct_typed_array(
779 cx,
780 name_type,
781 buffer_source,
782 byte_offset,
783 byte_length as i64,
784 ),
785 },
786 BufferSource::ArrayBufferView(_) => {
787 unreachable!("Can not create a new ArrayBufferView from an existing ArrayBufferView");
788 },
789 }
790}
791
792fn construct_typed_array(
794 cx: &mut js::context::JSContext,
795 name_type: &Type,
796 buffer_source: &HeapBufferSource<ArrayBufferU8>,
797 byte_offset: usize,
798 byte_length: i64,
799) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferViewU8>>> {
800 match &buffer_source.buffer_source {
801 BufferSource::ArrayBuffer(heap) => {
802 let array_view = unsafe {
803 match name_type {
804 Type::Int8 => JS_NewInt8ArrayWithBuffer(
805 cx.raw_cx(),
806 heap.handle(),
807 byte_offset,
808 byte_length,
809 ),
810 Type::Uint8 => JS_NewUint8ArrayWithBuffer(
811 cx.raw_cx(),
812 heap.handle(),
813 byte_offset,
814 byte_length,
815 ),
816 Type::Uint16 => JS_NewUint16ArrayWithBuffer(
817 cx.raw_cx(),
818 heap.handle(),
819 byte_offset,
820 byte_length,
821 ),
822 Type::Int16 => JS_NewInt16ArrayWithBuffer(
823 cx.raw_cx(),
824 heap.handle(),
825 byte_offset,
826 byte_length,
827 ),
828 Type::Int32 => JS_NewInt32ArrayWithBuffer(
829 cx.raw_cx(),
830 heap.handle(),
831 byte_offset,
832 byte_length,
833 ),
834 Type::Uint32 => JS_NewUint32ArrayWithBuffer(
835 cx.raw_cx(),
836 heap.handle(),
837 byte_offset,
838 byte_length,
839 ),
840 Type::Float32 => JS_NewFloat32ArrayWithBuffer(
841 cx.raw_cx(),
842 heap.handle(),
843 byte_offset,
844 byte_length,
845 ),
846 Type::Float64 => JS_NewFloat64ArrayWithBuffer(
847 cx.raw_cx(),
848 heap.handle(),
849 byte_offset,
850 byte_length,
851 ),
852 Type::Uint8Clamped => JS_NewUint8ClampedArrayWithBuffer(
853 cx.raw_cx(),
854 heap.handle(),
855 byte_offset,
856 byte_length,
857 ),
858 Type::BigInt64 => JS_NewBigInt64ArrayWithBuffer(
859 cx.raw_cx(),
860 heap.handle(),
861 byte_offset,
862 byte_length,
863 ),
864 Type::BigUint64 => JS_NewBigUint64ArrayWithBuffer(
865 cx.raw_cx(),
866 heap.handle(),
867 byte_offset,
868 byte_length,
869 ),
870 Type::Float16 => JS_NewFloat16ArrayWithBuffer(
871 cx.raw_cx(),
872 heap.handle(),
873 byte_offset,
874 byte_length,
875 ),
876 Type::Int64 | Type::Simd128 | Type::MaxTypedArrayViewType => {
877 unreachable!("Invalid TypedArray type")
878 },
879 }
880 };
881
882 Ok(RootedTraceableBox::new(HeapBufferSource::new(
883 BufferSource::ArrayBufferView(Heap::boxed(array_view)),
884 )))
885 },
886 BufferSource::ArrayBufferView(_) => {
887 unreachable!("Can not create a new ArrayBufferView from an existing ArrayBufferView");
888 },
889 }
890}
891
892pub(crate) fn create_array_buffer_with_size(
893 cx: &mut js::context::JSContext,
894 size: usize,
895) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferU8>>> {
896 let result = unsafe { NewArrayBuffer(cx.raw_cx(), size) };
897 if result.is_null() {
898 rooted!(&in(cx) let mut rval = UndefinedValue());
899 unsafe {
900 assert!(JS_GetPendingException(
901 cx.raw_cx(),
902 rval.handle_mut().into()
903 ));
904 JS_ClearPendingException(cx.raw_cx())
905 };
906
907 Err(Error::Type(c"can't create array buffer".to_owned()))
908 } else {
909 Ok(RootedTraceableBox::new(
910 HeapBufferSource::<ArrayBufferU8>::new(BufferSource::ArrayBuffer(Heap::boxed(result))),
911 ))
912 }
913}
914
915#[cfg(feature = "webgpu")]
916#[derive(JSTraceable, MallocSizeOf)]
917pub(crate) struct DataBlock {
918 #[conditional_malloc_size_of]
919 data: Arc<Box<[u8]>>,
920 data_views: Vec<DataView>,
922}
923
924#[cfg(feature = "webgpu")]
927fn range_overlap<T: std::cmp::PartialOrd>(range1: &Range<T>, range2: &Range<T>) -> bool {
928 range1.start < range2.end && range2.start < range1.end
929}
930
931#[cfg(feature = "webgpu")]
932impl DataBlock {
933 pub(crate) fn new_zeroed(size: usize) -> Self {
934 let data = vec![0; size];
935 Self {
936 data: Arc::new(data.into_boxed_slice()),
937 data_views: Vec::new(),
938 }
939 }
940
941 pub(crate) fn load(&mut self, src: &[u8]) {
943 Arc::get_mut(&mut self.data).unwrap().clone_from_slice(src)
945 }
946
947 pub(crate) fn data(&mut self) -> &mut [u8] {
949 Arc::get_mut(&mut self.data).unwrap()
951 }
952
953 pub(crate) fn clear_views(&mut self) {
954 self.data_views.clear()
955 }
956
957 pub(crate) fn view(&mut self, range: Range<usize>, _can_gc: CanGc) -> Result<&DataView, ()> {
959 if self
960 .data_views
961 .iter()
962 .any(|view| range_overlap(&view.range, &range))
963 {
964 return Err(());
965 }
966 let cx = GlobalScope::get_cx();
967 unsafe extern "C" fn free_func(_contents: *mut c_void, free_user_data: *mut c_void) {
970 #[expect(clippy::from_raw_with_void_ptr)]
974 drop(unsafe { Arc::from_raw(free_user_data as *const _) });
975 }
976 let raw: *mut Box<[u8]> = Arc::into_raw(Arc::clone(&self.data)) as _;
977 rooted!(in(*cx) let object = unsafe {
978 NewExternalArrayBuffer(
979 *cx,
980 range.end - range.start,
981 (&mut (*raw))[range.clone()].as_mut_ptr() as _,
983 Some(free_func),
984 raw as _,
985 )
986 });
987 self.data_views.push(DataView {
988 range,
989 buffer: HeapArrayBuffer::from(*object).unwrap(),
990 });
991 Ok(self.data_views.last().unwrap())
992 }
993}
994
995#[cfg(feature = "webgpu")]
996#[derive(JSTraceable, MallocSizeOf)]
997#[cfg_attr(crown, expect(crown::unrooted_must_root))]
998pub(crate) struct DataView {
999 #[no_trace]
1000 range: Range<usize>,
1001 #[ignore_malloc_size_of = "defined in mozjs"]
1002 buffer: HeapArrayBuffer,
1003}
1004
1005#[cfg(feature = "webgpu")]
1006impl DataView {
1007 pub(crate) fn array_buffer(&self) -> RootedTraceableBox<HeapArrayBuffer> {
1008 RootedTraceableBox::new(unsafe {
1009 HeapArrayBuffer::from(self.buffer.underlying_object().get()).unwrap()
1010 })
1011 }
1012}
1013
1014#[cfg(feature = "webgpu")]
1015impl Drop for DataView {
1016 #[expect(unsafe_code)]
1017 fn drop(&mut self) {
1018 let cx = GlobalScope::get_cx();
1019 assert!(unsafe {
1020 js::jsapi::DetachArrayBuffer(*cx, self.buffer.underlying_object().handle())
1021 })
1022 }
1023}