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
16use js::context::JSContext;
17use js::jsapi::{
18 GetArrayBufferByteLength, Heap, IsArrayBufferObject, IsDetachedArrayBufferObject,
19 JS_GetArrayBufferViewByteLength, JS_GetArrayBufferViewByteOffset, JS_GetArrayBufferViewType,
20 JS_GetTypedArrayLength, JS_IsArrayBufferViewObject, JS_IsTypedArrayObject, JSObject, Type,
21};
22use js::jsval::{ObjectValue, UndefinedValue};
23#[cfg(feature = "webgpu")]
24use js::rust::wrappers2::NewExternalArrayBuffer;
25use js::rust::wrappers2::{
26 ArrayBufferClone, ArrayBufferCopyData, DetachArrayBuffer, HasDefinedArrayBufferDetachKey,
27 JS_ClearPendingException, JS_GetArrayBufferViewBuffer, JS_GetPendingException,
28 JS_NewBigInt64ArrayWithBuffer, JS_NewBigUint64ArrayWithBuffer, JS_NewDataView,
29 JS_NewFloat16ArrayWithBuffer, JS_NewFloat32ArrayWithBuffer, JS_NewFloat64ArrayWithBuffer,
30 JS_NewInt8ArrayWithBuffer, JS_NewInt16ArrayWithBuffer, JS_NewInt32ArrayWithBuffer,
31 JS_NewUint8ArrayWithBuffer, JS_NewUint8ClampedArrayWithBuffer, JS_NewUint16ArrayWithBuffer,
32 JS_NewUint32ArrayWithBuffer, NewArrayBuffer, NewArrayBufferWithContents,
33 StealArrayBufferContents,
34};
35use js::rust::{
36 CustomAutoRooterGuard, Handle, MutableHandleObject,
37 MutableHandleValue as SafeMutableHandleValue,
38};
39#[cfg(feature = "webgpu")]
40use js::typedarray::HeapArrayBuffer;
41use js::typedarray::{
42 ArrayBufferU8, ArrayBufferViewU8, CreateWith, TypedArray, TypedArrayElement,
43 TypedArrayElementCreator,
44};
45
46use crate::dom::bindings::error::{Error, Fallible};
47use crate::dom::bindings::trace::RootedTraceableBox;
48#[cfg(feature = "webgpu")]
49use crate::dom::globalscope::GlobalScope;
50
51pub(crate) type RootedTypedArray<T> = RootedTraceableBox<TypedArray<T, Box<Heap<*mut JSObject>>>>;
52
53#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
60pub(crate) enum BufferSource {
61 ArrayBufferView(Box<Heap<*mut JSObject>>),
64
65 ArrayBuffer(Box<Heap<*mut JSObject>>),
68}
69
70impl Clone for BufferSource {
71 fn clone(&self) -> Self {
72 match self {
73 BufferSource::ArrayBufferView(heap) => {
74 BufferSource::ArrayBufferView(Heap::boxed(heap.get()))
75 },
76 BufferSource::ArrayBuffer(heap) => BufferSource::ArrayBuffer(Heap::boxed(heap.get())),
77 }
78 }
79}
80
81pub(crate) fn create_heap_buffer_source_with_length<T>(
82 cx: &mut JSContext,
83 len: u32,
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());
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: &mut 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: &mut 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, Handle::from_raw(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: &mut 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, Handle::from_raw(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: &mut 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, Handle::from_raw(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: &mut 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, Handle::from_raw(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 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, Handle::from_raw(buffer.handle()), &mut is_shared) });
360 debug_assert!(!is_shared);
361
362 unsafe { ArrayBufferClone(cx, view_buffer.handle(), byte_offset, byte_length) }
363 },
364 BufferSource::ArrayBuffer(buffer) => unsafe {
365 ArrayBufferClone(
366 cx,
367 Handle::from_raw(buffer.handle()),
368 byte_offset,
369 byte_length,
370 )
371 },
372 };
373
374 if result.is_null() {
375 rooted!(&in(cx) let mut _ex = UndefinedValue());
378 unsafe {
379 if JS_GetPendingException(cx, _ex.handle_mut()) {
381 JS_ClearPendingException(cx);
382 }
383 }
384
385 Err(Error::Type(c"can't clone array buffer".to_owned()))
386 } else {
387 Ok(RootedTraceableBox::new(
388 HeapBufferSource::<ArrayBufferU8>::new(BufferSource::ArrayBuffer(Heap::boxed(
389 result,
390 ))),
391 ))
392 }
393 }
394 #[expect(unsafe_code)]
396 pub(crate) fn clone_as_uint8_array(
397 &self,
398 cx: &mut JSContext,
399 ) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferViewU8>>> {
400 match &self.buffer_source {
401 BufferSource::ArrayBufferView(buffer) => {
402 assert!(unsafe { JS_IsArrayBufferViewObject(*buffer.handle()) });
405
406 assert!(!self.is_detached_buffer(cx));
408
409 let byte_offset = self.get_byte_offset();
412 let byte_length = self.byte_length();
413
414 let buffer = self.clone_array_buffer(cx, byte_offset, byte_length)?;
415
416 construct_typed_array(cx, &Type::Uint8, &buffer, 0, byte_length as i64)
419 },
420 BufferSource::ArrayBuffer(_buffer) => {
421 unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
422 },
423 }
424 }
425
426 pub(crate) fn is_undefined(&self) -> bool {
427 match &self.buffer_source {
428 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
429 buffer.get().is_null()
430 },
431 }
432 }
433}
434
435impl<T> HeapBufferSource<T>
436where
437 T: TypedArrayElement + TypedArrayElementCreator + 'static,
438 T::Element: Clone + Copy,
439{
440 pub(crate) fn acquire_data(&self, cx: &mut JSContext) -> Result<Vec<T::Element>, ()> {
441 assert!(self.is_initialized());
442
443 typedarray!(&in(cx) let array: TypedArray = match &self.buffer_source {
444 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
445 => {
446 buffer.get()
447 },
448 });
449 let data = if let Ok(array) =
450 array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
451 {
452 let data = array.to_vec();
453 let _ = self.detach_buffer(cx);
454 Ok(data)
455 } else {
456 Err(())
457 };
458
459 match &self.buffer_source {
460 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
461 buffer.set(ptr::null_mut());
462 },
463 }
464 data
465 }
466
467 pub(crate) fn copy_data_to(
468 &self,
469 cx: &mut JSContext,
470 dest: &mut [T::Element],
471 source_start: usize,
472 length: usize,
473 ) -> Result<(), ()> {
474 assert!(self.is_initialized());
475 typedarray!(&in(cx) let array: TypedArray = match &self.buffer_source {
476 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
477 => {
478 buffer.get()
479 },
480 });
481 let Ok(array) =
482 array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
483 else {
484 return Err(());
485 };
486 unsafe {
487 let slice = (*array).as_slice();
488 dest.copy_from_slice(&slice[source_start..length]);
489 }
490 Ok(())
491 }
492
493 pub(crate) fn copy_data_from(
494 &self,
495 cx: &mut JSContext,
496 source: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
497 dest_start: usize,
498 length: usize,
499 ) -> Result<(), ()> {
500 assert!(self.is_initialized());
501 typedarray!(&in(cx) let mut array: TypedArray = match &self.buffer_source {
502 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
503 => {
504 buffer.get()
505 },
506 });
507 let Ok(mut array) =
508 array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
509 else {
510 return Err(());
511 };
512 unsafe {
513 let slice = (*array).as_mut_slice();
514 let (_, dest) = slice.split_at_mut(dest_start);
515 dest[0..length].copy_from_slice(&source.as_slice()[0..length])
516 }
517 Ok(())
518 }
519
520 pub(crate) fn set_data(&self, cx: &mut JSContext, data: &[T::Element]) -> Result<(), ()> {
521 rooted!(&in(cx) let mut array = ptr::null_mut::<JSObject>());
522 let _ = create_buffer_source::<T>(cx, data, array.handle_mut())?;
523
524 match &self.buffer_source {
525 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
526 buffer.set(*array);
527 },
528 }
529 Ok(())
530 }
531
532 pub(crate) fn can_copy_data_block_bytes(
535 &self,
536 cx: &mut JSContext,
537 to_index: usize,
538 from_buffer: &HeapBufferSource<ArrayBufferU8>,
539 from_index: usize,
540 bytes_to_copy: usize,
541 ) -> bool {
542 assert!(self.is_array_buffer_object());
545
546 assert!(from_buffer.is_array_buffer_object());
549
550 match &self.buffer_source {
552 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => {
553 match &from_buffer.buffer_source {
554 BufferSource::ArrayBufferView(from_heap) |
555 BufferSource::ArrayBuffer(from_heap) => {
556 if std::ptr::eq(heap.get(), from_heap.get()) {
557 return false;
558 }
559 },
560 }
561 },
562 }
563
564 if self.is_detached_buffer(cx) {
566 return false;
567 }
568
569 if from_buffer.is_detached_buffer(cx) {
571 return false;
572 }
573
574 if to_index + bytes_to_copy > self.byte_length() {
576 return false;
577 }
578
579 if from_index + bytes_to_copy > from_buffer.byte_length() {
581 return false;
582 }
583
584 true
586 }
587
588 pub(crate) fn copy_data_block_bytes(
589 &self,
590 cx: &mut JSContext,
591 dest_start: usize,
592 from_buffer: &HeapBufferSource<ArrayBufferU8>,
593 from_byte_offset: usize,
594 bytes_to_copy: usize,
595 ) -> bool {
596 match &self.buffer_source {
597 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
598 match &from_buffer.buffer_source {
599 BufferSource::ArrayBufferView(from_heap) |
600 BufferSource::ArrayBuffer(from_heap) => ArrayBufferCopyData(
601 cx,
602 Handle::from_raw(heap.handle()),
603 dest_start,
604 Handle::from_raw(from_heap.handle()),
605 from_byte_offset,
606 bytes_to_copy,
607 ),
608 }
609 },
610 }
611 }
612
613 pub(crate) fn can_transfer_array_buffer(&self, cx: &mut JSContext) -> bool {
615 assert!(self.is_array_buffer_object());
618
619 if self.is_detached_buffer(cx) {
621 return false;
622 }
623
624 let mut is_defined = false;
627 match &self.buffer_source {
628 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
629 if !HasDefinedArrayBufferDetachKey(
630 cx,
631 Handle::from_raw(heap.handle()),
632 &mut is_defined,
633 ) {
634 return false;
635 }
636 },
637 }
638
639 !is_defined
640 }
641
642 pub(crate) fn transfer_array_buffer(
644 &self,
645 cx: &mut JSContext,
646 ) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferU8>>> {
647 assert!(self.is_array_buffer_object());
648
649 assert!(!self.is_detached_buffer(cx));
651
652 let buffer_length = self.byte_length();
655
656 let buffer_data = match &self.buffer_source {
659 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => unsafe {
660 StealArrayBufferContents(cx, Handle::from_raw(buffer.handle()))
661 },
662 };
663
664 if !self.detach_buffer(cx) {
668 rooted!(&in(cx) let mut rval = UndefinedValue());
669 unsafe {
670 assert!(JS_GetPendingException(cx, rval.handle_mut()));
671 JS_ClearPendingException(cx)
672 };
673
674 Err(Error::Type(c"can't transfer array buffer".to_owned()))
675 } else {
676 Ok(RootedTraceableBox::new(
680 HeapBufferSource::<ArrayBufferU8>::new(BufferSource::ArrayBuffer(Heap::boxed(
681 unsafe { NewArrayBufferWithContents(cx, buffer_length, buffer_data) },
682 ))),
683 ))
684 }
685 }
686}
687
688unsafe impl<T> crate::dom::bindings::trace::JSTraceable for HeapBufferSource<T> {
689 #[inline]
690 unsafe fn trace(&self, tracer: *mut js::jsapi::JSTracer) {
691 match &self.buffer_source {
692 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
693 unsafe { buffer.trace(tracer) };
694 },
695 }
696 }
697}
698
699pub(crate) fn create_buffer_source<T>(
701 cx: &mut JSContext,
702 data: &[T::Element],
703 mut dest: MutableHandleObject,
704) -> Result<RootedTypedArray<T>, ()>
705where
706 T: TypedArrayElement + TypedArrayElementCreator,
707{
708 let res = unsafe {
709 TypedArray::<T, *mut JSObject>::create(
710 cx.raw_cx(),
711 CreateWith::Slice(data),
712 dest.reborrow(),
713 )
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: &mut JSContext,
725 len: usize,
726 mut dest: MutableHandleObject,
727) -> Result<RootedTypedArray<T>, ()>
728where
729 T: TypedArrayElement + TypedArrayElementCreator,
730{
731 let res = unsafe {
732 TypedArray::<T, *mut JSObject>::create(
733 cx.raw_cx(),
734 CreateWith::Length(len),
735 dest.reborrow(),
736 )
737 };
738
739 if res.is_err() {
740 Err(())
741 } else {
742 TypedArray::from(dest.get()).map(RootedTraceableBox::new)
743 }
744}
745
746pub(crate) fn byte_size(byte_type: Type) -> u64 {
747 match byte_type {
748 Type::Int8 | Type::Uint8 | Type::Uint8Clamped => 1,
749 Type::Int16 | Type::Uint16 | Type::Float16 => 2,
750 Type::Int32 | Type::Uint32 | Type::Float32 => 4,
751 Type::Int64 | Type::Float64 | Type::BigInt64 | Type::BigUint64 => 8,
752 Type::Simd128 => 16,
753 _ => unreachable!("invalid scalar type"),
754 }
755}
756
757#[derive(Clone, Eq, JSTraceable, MallocSizeOf, PartialEq)]
758pub(crate) enum Constructor {
759 DataView,
760 Name(
761 #[ignore_malloc_size_of = "mozjs"]
762 #[no_trace]
763 Type,
764 ),
765}
766
767pub(crate) fn create_buffer_source_with_constructor(
768 cx: &mut JSContext,
769 constructor: &Constructor,
770 buffer_source: &HeapBufferSource<ArrayBufferU8>,
771 byte_offset: usize,
772 byte_length: usize,
773) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferViewU8>>> {
774 match &buffer_source.buffer_source {
775 BufferSource::ArrayBuffer(heap) => match constructor {
776 Constructor::DataView => Ok(RootedTraceableBox::new(HeapBufferSource::new(
777 BufferSource::ArrayBufferView(Heap::boxed(unsafe {
778 JS_NewDataView(
779 cx,
780 Handle::from_raw(heap.handle()),
781 byte_offset,
782 byte_length,
783 )
784 })),
785 ))),
786 Constructor::Name(name_type) => construct_typed_array(
787 cx,
788 name_type,
789 buffer_source,
790 byte_offset,
791 byte_length as i64,
792 ),
793 },
794 BufferSource::ArrayBufferView(_) => {
795 unreachable!("Can not create a new ArrayBufferView from an existing ArrayBufferView");
796 },
797 }
798}
799
800fn construct_typed_array(
802 cx: &mut JSContext,
803 name_type: &Type,
804 buffer_source: &HeapBufferSource<ArrayBufferU8>,
805 byte_offset: usize,
806 byte_length: i64,
807) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferViewU8>>> {
808 match &buffer_source.buffer_source {
809 BufferSource::ArrayBuffer(heap) => {
810 let array_view = unsafe {
811 match name_type {
812 Type::Int8 => JS_NewInt8ArrayWithBuffer(
813 cx,
814 Handle::from_raw(heap.handle()),
815 byte_offset,
816 byte_length,
817 ),
818 Type::Uint8 => JS_NewUint8ArrayWithBuffer(
819 cx,
820 Handle::from_raw(heap.handle()),
821 byte_offset,
822 byte_length,
823 ),
824 Type::Uint16 => JS_NewUint16ArrayWithBuffer(
825 cx,
826 Handle::from_raw(heap.handle()),
827 byte_offset,
828 byte_length,
829 ),
830 Type::Int16 => JS_NewInt16ArrayWithBuffer(
831 cx,
832 Handle::from_raw(heap.handle()),
833 byte_offset,
834 byte_length,
835 ),
836 Type::Int32 => JS_NewInt32ArrayWithBuffer(
837 cx,
838 Handle::from_raw(heap.handle()),
839 byte_offset,
840 byte_length,
841 ),
842 Type::Uint32 => JS_NewUint32ArrayWithBuffer(
843 cx,
844 Handle::from_raw(heap.handle()),
845 byte_offset,
846 byte_length,
847 ),
848 Type::Float32 => JS_NewFloat32ArrayWithBuffer(
849 cx,
850 Handle::from_raw(heap.handle()),
851 byte_offset,
852 byte_length,
853 ),
854 Type::Float64 => JS_NewFloat64ArrayWithBuffer(
855 cx,
856 Handle::from_raw(heap.handle()),
857 byte_offset,
858 byte_length,
859 ),
860 Type::Uint8Clamped => JS_NewUint8ClampedArrayWithBuffer(
861 cx,
862 Handle::from_raw(heap.handle()),
863 byte_offset,
864 byte_length,
865 ),
866 Type::BigInt64 => JS_NewBigInt64ArrayWithBuffer(
867 cx,
868 Handle::from_raw(heap.handle()),
869 byte_offset,
870 byte_length,
871 ),
872 Type::BigUint64 => JS_NewBigUint64ArrayWithBuffer(
873 cx,
874 Handle::from_raw(heap.handle()),
875 byte_offset,
876 byte_length,
877 ),
878 Type::Float16 => JS_NewFloat16ArrayWithBuffer(
879 cx,
880 Handle::from_raw(heap.handle()),
881 byte_offset,
882 byte_length,
883 ),
884 Type::Int64 | Type::Simd128 | Type::MaxTypedArrayViewType => {
885 unreachable!("Invalid TypedArray type")
886 },
887 }
888 };
889
890 Ok(RootedTraceableBox::new(HeapBufferSource::new(
891 BufferSource::ArrayBufferView(Heap::boxed(array_view)),
892 )))
893 },
894 BufferSource::ArrayBufferView(_) => {
895 unreachable!("Can not create a new ArrayBufferView from an existing ArrayBufferView");
896 },
897 }
898}
899
900pub(crate) fn create_array_buffer_with_size(
901 cx: &mut JSContext,
902 size: usize,
903) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferU8>>> {
904 let result = unsafe { NewArrayBuffer(cx, size) };
905 if result.is_null() {
906 rooted!(&in(cx) let mut rval = UndefinedValue());
907 unsafe {
908 assert!(JS_GetPendingException(cx, rval.handle_mut()));
909 JS_ClearPendingException(cx)
910 };
911
912 Err(Error::Type(c"can't create array buffer".to_owned()))
913 } else {
914 Ok(RootedTraceableBox::new(
915 HeapBufferSource::<ArrayBufferU8>::new(BufferSource::ArrayBuffer(Heap::boxed(result))),
916 ))
917 }
918}
919
920#[cfg(feature = "webgpu")]
921#[derive(JSTraceable, MallocSizeOf)]
922#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
923pub(crate) struct DataBlock {
924 #[conditional_malloc_size_of]
925 data: Arc<Box<[u8]>>,
926 data_views: Vec<DataView>,
928}
929
930#[cfg(feature = "webgpu")]
933fn range_overlap<T: std::cmp::PartialOrd>(range1: &Range<T>, range2: &Range<T>) -> bool {
934 range1.start < range2.end && range2.start < range1.end
935}
936
937#[cfg(feature = "webgpu")]
938impl DataBlock {
939 pub(crate) fn new_zeroed(size: usize) -> Self {
940 let data = vec![0; size];
941 Self {
942 data: Arc::new(data.into_boxed_slice()),
943 data_views: Vec::new(),
944 }
945 }
946
947 pub(crate) fn load(&mut self, src: &[u8]) {
949 Arc::get_mut(&mut self.data).unwrap().clone_from_slice(src)
951 }
952
953 pub(crate) fn data(&mut self) -> &mut [u8] {
955 Arc::get_mut(&mut self.data).unwrap()
957 }
958
959 pub(crate) fn clear_views(&mut self) {
960 self.data_views.clear()
961 }
962
963 pub(crate) fn view(
965 &mut self,
966 cx: &mut JSContext,
967 range: Range<usize>,
968 ) -> Result<&DataView, ()> {
969 if self
970 .data_views
971 .iter()
972 .any(|view| range_overlap(&view.range, &range))
973 {
974 return Err(());
975 }
976 let range_len = range
977 .end
978 .checked_sub(range.start)
979 .expect("range end must be >= range start");
980 assert!(range.end <= self.data.len());
981
982 unsafe extern "C" fn free_func(_contents: *mut c_void, free_user_data: *mut c_void) {
985 let raw: *const Box<[u8]> = free_user_data.cast();
986 drop(unsafe { Arc::from_raw(raw) });
990 }
991 let raw: *const Box<[u8]> = Arc::into_raw(Arc::clone(&self.data));
992 let data_ptr = unsafe { (**raw).as_ptr().add(range.start) };
996 rooted!(&in(cx) let object = unsafe {
997 NewExternalArrayBuffer(
998 cx,
999 range_len,
1000 data_ptr.cast_mut().cast(),
1003 Some(free_func),
1004 raw as _,
1005 )
1006 });
1007 self.data_views.push(DataView {
1008 range,
1009 buffer: HeapArrayBuffer::from(*object).unwrap(),
1010 });
1011 Ok(self.data_views.last().unwrap())
1012 }
1013}
1014
1015#[cfg(feature = "webgpu")]
1016#[derive(JSTraceable, MallocSizeOf)]
1017#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1018pub(crate) struct DataView {
1019 #[no_trace]
1020 range: Range<usize>,
1021 #[ignore_malloc_size_of = "defined in mozjs"]
1022 buffer: HeapArrayBuffer,
1023}
1024
1025#[cfg(feature = "webgpu")]
1026impl DataView {
1027 pub(crate) fn array_buffer(&self) -> RootedTraceableBox<HeapArrayBuffer> {
1028 RootedTraceableBox::new(unsafe {
1029 HeapArrayBuffer::from(self.buffer.underlying_object().get()).unwrap()
1030 })
1031 }
1032}
1033
1034#[cfg(feature = "webgpu")]
1035impl Drop for DataView {
1036 #[expect(unsafe_code)]
1037 fn drop(&mut self) {
1038 let cx = GlobalScope::get_cx();
1039 assert!(unsafe {
1040 js::jsapi::DetachArrayBuffer(*cx, self.buffer.underlying_object().handle())
1041 })
1042 }
1043}