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, NoGC};
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::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
47use crate::dom::bindings::error::{Error, Fallible};
48use crate::dom::bindings::trace::RootedTraceableBox;
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) enum ArrayBufferViewOrArrayBufferRef<'a> {
81 ArrayBufferView(&'a RootedTypedArray<ArrayBufferViewU8>),
82 ArrayBuffer(&'a RootedTypedArray<ArrayBufferU8>),
83}
84
85impl<'a> From<&'a RootedTypedArray<ArrayBufferViewU8>> for ArrayBufferViewOrArrayBufferRef<'a> {
86 fn from(view: &'a RootedTypedArray<ArrayBufferViewU8>) -> Self {
87 ArrayBufferViewOrArrayBufferRef::ArrayBufferView(view)
88 }
89}
90
91impl<'a> From<&'a RootedTypedArray<ArrayBufferU8>> for ArrayBufferViewOrArrayBufferRef<'a> {
92 fn from(buffer: &'a RootedTypedArray<ArrayBufferU8>) -> Self {
93 ArrayBufferViewOrArrayBufferRef::ArrayBuffer(buffer)
94 }
95}
96
97impl<'a> From<&'a ArrayBufferViewOrArrayBuffer> for ArrayBufferViewOrArrayBufferRef<'a> {
98 fn from(view_or_buffer: &'a ArrayBufferViewOrArrayBuffer) -> Self {
99 match view_or_buffer {
100 ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => {
101 ArrayBufferViewOrArrayBufferRef::ArrayBufferView(view)
102 },
103 ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => {
104 ArrayBufferViewOrArrayBufferRef::ArrayBuffer(buffer)
105 },
106 }
107 }
108}
109
110pub(crate) fn get_buffer_source_copy(source: ArrayBufferViewOrArrayBufferRef<'_>) -> Vec<u8> {
131 match source {
132 ArrayBufferViewOrArrayBufferRef::ArrayBufferView(view) => view.to_vec(),
133 ArrayBufferViewOrArrayBufferRef::ArrayBuffer(buffer) => buffer.to_vec(),
134 }
135 .unwrap_or(vec![])
136}
137
138pub(crate) fn get_buffer_source_slice<'a>(
143 source: &'a ArrayBufferViewOrArrayBuffer,
144 no_gc: &'a NoGC,
145) -> &'a [u8] {
146 match source {
147 ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.as_slice_safe(no_gc),
148 ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.as_slice_safe(no_gc),
149 }
150 .unwrap_or(&[])
151}
152
153pub(crate) fn create_heap_buffer_source_with_length<T>(
154 cx: &mut JSContext,
155 len: u32,
156) -> Fallible<RootedTraceableBox<HeapBufferSource<T>>>
157where
158 T: TypedArrayElement + TypedArrayElementCreator + 'static,
159 T::Element: Clone + Copy,
160{
161 rooted!(&in(cx) let mut array = ptr::null_mut::<JSObject>());
162 let typed_array_result =
163 create_buffer_source_with_length::<T>(cx, len as usize, array.handle_mut());
164 if typed_array_result.is_err() {
165 return Err(Error::JSFailed);
166 }
167
168 Ok(RootedTraceableBox::new(HeapBufferSource::<T>::new(
169 array.handle(),
170 )))
171}
172
173#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
174pub(crate) struct HeapBufferSource<T> {
175 buffer_source: BufferSource,
176 phantom: PhantomData<T>,
177}
178
179impl<T> Eq for HeapBufferSource<T> where T: TypedArrayElement {}
180
181impl<T> PartialEq for HeapBufferSource<T>
182where
183 T: TypedArrayElement,
184{
185 fn eq(&self, other: &Self) -> bool {
186 match &self.buffer_source {
187 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => match &other
188 .buffer_source
189 {
190 BufferSource::ArrayBufferView(from_heap) | BufferSource::ArrayBuffer(from_heap) => {
191 std::ptr::eq(heap.get(), from_heap.get())
192 },
193 },
194 }
195 }
196}
197
198impl<T> Clone for HeapBufferSource<T>
199where
200 T: TypedArrayElement,
201{
202 fn clone(&self) -> Self {
203 HeapBufferSource {
204 buffer_source: self.buffer_source.clone(),
205 phantom: PhantomData,
206 }
207 }
208}
209
210impl<T> HeapBufferSource<T>
211where
212 T: TypedArrayElement,
213{
214 pub(crate) fn new(object: Handle<*mut JSObject>) -> HeapBufferSource<T> {
216 let object = object.get();
217 assert!(!object.is_null());
218
219 HeapBufferSource {
220 buffer_source: if unsafe { IsArrayBufferObject(object) } {
221 BufferSource::ArrayBuffer(Heap::boxed(object))
222 } else {
223 assert!(unsafe { JS_IsArrayBufferViewObject(object) });
224 BufferSource::ArrayBufferView(Heap::boxed(object))
225 },
226 phantom: PhantomData,
227 }
228 }
229
230 pub(crate) fn from_view(
231 cx: &mut JSContext,
232 chunk: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
233 ) -> RootedTraceableBox<HeapBufferSource<T>> {
234 rooted!(&in(cx) let object = unsafe { *chunk.underlying_object() });
235 RootedTraceableBox::new(HeapBufferSource::<T>::new(object.handle()))
236 }
237
238 pub(crate) fn default() -> Self {
239 HeapBufferSource {
240 buffer_source: BufferSource::ArrayBufferView(Heap::boxed(std::ptr::null_mut())),
241 phantom: PhantomData,
242 }
243 }
244
245 pub(crate) fn is_initialized(&self) -> bool {
246 match &self.buffer_source {
247 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
248 !buffer.get().is_null()
249 },
250 }
251 }
252
253 pub(crate) fn get_typed_array(&self) -> Result<RootedTypedArray<T>, ()> {
254 TypedArray::from(match &self.buffer_source {
255 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
256 buffer.get()
257 },
258 })
259 .map(RootedTraceableBox::new)
260 }
261
262 pub(crate) fn get_buffer_view_value(
263 &self,
264 cx: &mut JSContext,
265 mut handle_mut: SafeMutableHandleValue,
266 ) {
267 match &self.buffer_source {
268 BufferSource::ArrayBufferView(buffer) => {
269 rooted!(&in(cx) let value = ObjectValue(buffer.get()));
270 handle_mut.set(*value);
271 },
272 BufferSource::ArrayBuffer(_) => {
273 unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
274 },
275 }
276 }
277
278 pub(crate) fn get_array_buffer_view_buffer(
279 &self,
280 cx: &mut JSContext,
281 ) -> RootedTraceableBox<HeapBufferSource<ArrayBufferU8>> {
282 match &self.buffer_source {
283 BufferSource::ArrayBufferView(buffer) => unsafe {
284 let mut is_shared = false;
285 rooted!(&in(cx) let view_buffer =
286 JS_GetArrayBufferViewBuffer(cx, Handle::from_raw(buffer.handle()), &mut is_shared));
287
288 RootedTraceableBox::new(HeapBufferSource::<ArrayBufferU8>::new(
289 view_buffer.handle(),
290 ))
291 },
292 BufferSource::ArrayBuffer(_) => {
293 unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
294 },
295 }
296 }
297
298 pub(crate) fn detach_buffer(&self, cx: &mut JSContext) -> bool {
300 assert!(self.is_initialized());
301 match &self.buffer_source {
302 BufferSource::ArrayBufferView(buffer) => {
303 let mut is_shared = false;
304 unsafe {
305 assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
307 rooted!(&in(cx) let view_buffer =
308 JS_GetArrayBufferViewBuffer(cx, Handle::from_raw(buffer.handle()), &mut is_shared));
309 debug_assert!(!is_shared);
311 DetachArrayBuffer(cx, view_buffer.handle())
313 }
314 },
315 BufferSource::ArrayBuffer(buffer) => unsafe {
316 DetachArrayBuffer(cx, Handle::from_raw(buffer.handle()))
317 },
318 }
319 }
320
321 pub(crate) fn typed_array_to_option(&self) -> Option<RootedTypedArray<T>> {
322 if self.is_initialized() {
323 self.get_typed_array().ok()
324 } else {
325 warn!("Buffer not initialized.");
326 None
327 }
328 }
329
330 pub(crate) fn is_detached_buffer(&self, cx: &mut JSContext) -> bool {
331 assert!(self.is_initialized());
332 match &self.buffer_source {
333 BufferSource::ArrayBufferView(buffer) => {
334 let mut is_shared = false;
335 unsafe {
336 assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
337 rooted!(&in(cx) let view_buffer =
338 JS_GetArrayBufferViewBuffer(cx, Handle::from_raw(buffer.handle()), &mut is_shared));
339 debug_assert!(!is_shared);
340 IsDetachedArrayBufferObject(*view_buffer.handle())
341 }
342 },
343 BufferSource::ArrayBuffer(buffer) => unsafe {
344 IsDetachedArrayBufferObject(*buffer.handle())
345 },
346 }
347 }
348
349 pub(crate) fn viewed_buffer_array_byte_length(&self, cx: &mut JSContext) -> usize {
350 assert!(self.is_initialized());
351 match &self.buffer_source {
352 BufferSource::ArrayBufferView(buffer) => {
353 let mut is_shared = false;
354 unsafe {
355 assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
356 rooted!(&in(cx) let view_buffer =
357 JS_GetArrayBufferViewBuffer(cx, Handle::from_raw(buffer.handle()), &mut is_shared));
358 debug_assert!(!is_shared);
359 GetArrayBufferByteLength(*view_buffer.handle())
360 }
361 },
362 BufferSource::ArrayBuffer(buffer) => unsafe {
363 GetArrayBufferByteLength(*buffer.handle())
364 },
365 }
366 }
367
368 pub(crate) fn byte_length(&self) -> usize {
369 match &self.buffer_source {
370 BufferSource::ArrayBufferView(buffer) => unsafe {
371 JS_GetArrayBufferViewByteLength(*buffer.handle())
372 },
373 BufferSource::ArrayBuffer(buffer) => unsafe {
374 GetArrayBufferByteLength(*buffer.handle())
375 },
376 }
377 }
378
379 pub(crate) fn get_byte_offset(&self) -> usize {
380 match &self.buffer_source {
381 BufferSource::ArrayBufferView(buffer) => unsafe {
382 JS_GetArrayBufferViewByteOffset(*buffer.handle())
383 },
384 BufferSource::ArrayBuffer(_) => {
385 unreachable!("BufferSource::ArrayBuffer does not have a byte offset.")
386 },
387 }
388 }
389
390 pub(crate) fn get_typed_array_length(&self) -> usize {
391 match &self.buffer_source {
392 BufferSource::ArrayBufferView(buffer) => unsafe {
393 JS_GetTypedArrayLength(*buffer.handle())
394 },
395 BufferSource::ArrayBuffer(_) => {
396 unreachable!("BufferSource::ArrayBuffer does not have a length.")
397 },
398 }
399 }
400
401 pub(crate) fn has_typed_array_name(&self) -> bool {
403 match &self.buffer_source {
404 BufferSource::ArrayBufferView(buffer) => unsafe {
405 JS_IsTypedArrayObject(*buffer.handle())
406 },
407 BufferSource::ArrayBuffer(_) => false,
408 }
409 }
410
411 pub(crate) fn get_array_buffer_view_type(&self) -> Type {
412 match &self.buffer_source {
413 BufferSource::ArrayBufferView(buffer) => unsafe {
414 JS_GetArrayBufferViewType(*buffer.handle())
415 },
416 BufferSource::ArrayBuffer(_) => unreachable!("ArrayBuffer does not have a view type."),
417 }
418 }
419
420 pub(crate) fn is_array_buffer_object(&self) -> bool {
421 match &self.buffer_source {
422 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
423 IsArrayBufferObject(*heap.handle())
424 },
425 }
426 }
427
428 pub(crate) fn clone_array_buffer(
430 &self,
431 cx: &mut JSContext,
432 byte_offset: usize,
433 byte_length: usize,
434 ) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferU8>>> {
435 let result = match &self.buffer_source {
436 BufferSource::ArrayBufferView(buffer) => {
437 let mut is_shared = false;
438 rooted!(&in(cx) let view_buffer =
439 unsafe { JS_GetArrayBufferViewBuffer(cx, Handle::from_raw(buffer.handle()), &mut is_shared) });
440 debug_assert!(!is_shared);
441
442 unsafe { ArrayBufferClone(cx, view_buffer.handle(), byte_offset, byte_length) }
443 },
444 BufferSource::ArrayBuffer(buffer) => unsafe {
445 ArrayBufferClone(
446 cx,
447 Handle::from_raw(buffer.handle()),
448 byte_offset,
449 byte_length,
450 )
451 },
452 };
453
454 rooted!(&in(cx) let result = result);
455
456 if result.is_null() {
457 rooted!(&in(cx) let mut _ex = UndefinedValue());
460 unsafe {
461 if JS_GetPendingException(cx, _ex.handle_mut()) {
463 JS_ClearPendingException(cx);
464 }
465 }
466
467 Err(Error::Type(c"can't clone array buffer".to_owned()))
468 } else {
469 Ok(RootedTraceableBox::new(
470 HeapBufferSource::<ArrayBufferU8>::new(result.handle()),
471 ))
472 }
473 }
474 #[expect(unsafe_code)]
476 pub(crate) fn clone_as_uint8_array(
477 &self,
478 cx: &mut JSContext,
479 ) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferViewU8>>> {
480 match &self.buffer_source {
481 BufferSource::ArrayBufferView(buffer) => {
482 assert!(unsafe { JS_IsArrayBufferViewObject(*buffer.handle()) });
485
486 assert!(!self.is_detached_buffer(cx));
488
489 let byte_offset = self.get_byte_offset();
492 let byte_length = self.byte_length();
493
494 let buffer = self.clone_array_buffer(cx, byte_offset, byte_length)?;
495
496 construct_typed_array(cx, &Type::Uint8, &buffer, 0, byte_length as i64)
499 },
500 BufferSource::ArrayBuffer(_buffer) => {
501 unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
502 },
503 }
504 }
505
506 pub(crate) fn is_undefined(&self) -> bool {
507 match &self.buffer_source {
508 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
509 buffer.get().is_null()
510 },
511 }
512 }
513}
514
515impl<T> HeapBufferSource<T>
516where
517 T: TypedArrayElement + TypedArrayElementCreator + 'static,
518 T::Element: Clone + Copy,
519{
520 pub(crate) fn acquire_data(&self, cx: &mut JSContext) -> Result<Vec<T::Element>, ()> {
521 assert!(self.is_initialized());
522
523 typedarray!(&in(cx) let array: TypedArray = match &self.buffer_source {
524 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
525 => {
526 buffer.get()
527 },
528 });
529 let data = if let Ok(array) =
530 array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
531 {
532 if let Some(data) = array.to_vec() {
533 let _ = self.detach_buffer(cx);
534 Ok(data)
535 } else {
536 Err(())
537 }
538 } else {
539 Err(())
540 };
541
542 match &self.buffer_source {
543 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
544 buffer.set(ptr::null_mut());
545 },
546 }
547 data
548 }
549
550 pub(crate) fn copy_data_to(
551 &self,
552 cx: &mut JSContext,
553 dest: &mut [T::Element],
554 source_start: usize,
555 source_end: usize,
556 ) -> Result<(), ()> {
557 assert!(self.is_initialized());
558 typedarray!(&in(cx) let array: TypedArray = match &self.buffer_source {
559 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
560 => {
561 buffer.get()
562 },
563 });
564 let Ok(array) =
565 array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
566 else {
567 return Err(());
568 };
569 let slice = (*array).as_slice_safe(cx.no_gc()).unwrap_or(&[]);
570 dest.copy_from_slice(&slice[source_start..source_end]);
571 Ok(())
572 }
573
574 pub(crate) fn copy_data_from(
575 &self,
576 cx: &mut JSContext,
577 source: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
578 dest_start: usize,
579 length: usize,
580 ) -> Result<(), ()> {
581 assert!(self.is_initialized());
582 typedarray!(&in(cx) let mut array: TypedArray = match &self.buffer_source {
583 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer)
584 => {
585 buffer.get()
586 },
587 });
588 let Ok(mut array) =
589 array as Result<CustomAutoRooterGuard<'_, TypedArray<T, *mut JSObject>>, &mut ()>
590 else {
591 return Err(());
592 };
593 let slice = (*array).as_mut_slice_safe(cx.no_gc()).unwrap_or(&mut []);
594 let (_, dest) = slice.split_at_mut(dest_start);
595 let source = source.as_slice_safe(cx.no_gc()).unwrap_or(&[]);
596 dest[0..length].copy_from_slice(&source[0..length]);
597 Ok(())
598 }
599
600 pub(crate) fn set_data(&self, cx: &mut JSContext, data: &[T::Element]) -> Result<(), ()> {
601 rooted!(&in(cx) let mut array = ptr::null_mut::<JSObject>());
602 let _ = create_buffer_source::<T>(cx, data, array.handle_mut())?;
603
604 match &self.buffer_source {
605 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
606 buffer.set(*array);
607 },
608 }
609 Ok(())
610 }
611
612 pub(crate) fn can_copy_data_block_bytes(
615 &self,
616 cx: &mut JSContext,
617 to_index: usize,
618 from_buffer: &HeapBufferSource<ArrayBufferU8>,
619 from_index: usize,
620 bytes_to_copy: usize,
621 ) -> bool {
622 assert!(self.is_array_buffer_object());
625
626 assert!(from_buffer.is_array_buffer_object());
629
630 match &self.buffer_source {
632 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => {
633 match &from_buffer.buffer_source {
634 BufferSource::ArrayBufferView(from_heap) |
635 BufferSource::ArrayBuffer(from_heap) => {
636 if std::ptr::eq(heap.get(), from_heap.get()) {
637 return false;
638 }
639 },
640 }
641 },
642 }
643
644 if self.is_detached_buffer(cx) {
646 return false;
647 }
648
649 if from_buffer.is_detached_buffer(cx) {
651 return false;
652 }
653
654 if to_index + bytes_to_copy > self.byte_length() {
656 return false;
657 }
658
659 if from_index + bytes_to_copy > from_buffer.byte_length() {
661 return false;
662 }
663
664 true
666 }
667
668 pub(crate) fn copy_data_block_bytes(
669 &self,
670 cx: &mut JSContext,
671 dest_start: usize,
672 from_buffer: &HeapBufferSource<ArrayBufferU8>,
673 from_byte_offset: usize,
674 bytes_to_copy: usize,
675 ) -> bool {
676 match &self.buffer_source {
677 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
678 match &from_buffer.buffer_source {
679 BufferSource::ArrayBufferView(from_heap) |
680 BufferSource::ArrayBuffer(from_heap) => ArrayBufferCopyData(
681 cx,
682 Handle::from_raw(heap.handle()),
683 dest_start,
684 Handle::from_raw(from_heap.handle()),
685 from_byte_offset,
686 bytes_to_copy,
687 ),
688 }
689 },
690 }
691 }
692
693 pub(crate) fn can_transfer_array_buffer(&self, cx: &mut JSContext) -> bool {
695 assert!(self.is_array_buffer_object());
698
699 if self.is_detached_buffer(cx) {
701 return false;
702 }
703
704 let mut is_defined = false;
707 match &self.buffer_source {
708 BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
709 if !HasDefinedArrayBufferDetachKey(
710 cx,
711 Handle::from_raw(heap.handle()),
712 &mut is_defined,
713 ) {
714 return false;
715 }
716 },
717 }
718
719 !is_defined
720 }
721
722 pub(crate) fn transfer_array_buffer(
724 &self,
725 cx: &mut JSContext,
726 ) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferU8>>> {
727 assert!(self.is_array_buffer_object());
728
729 assert!(!self.is_detached_buffer(cx));
731
732 let buffer_length = self.byte_length();
735
736 let buffer_data = match &self.buffer_source {
739 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => unsafe {
740 StealArrayBufferContents(cx, Handle::from_raw(buffer.handle()))
741 },
742 };
743
744 if !self.detach_buffer(cx) {
748 rooted!(&in(cx) let mut rval = UndefinedValue());
749 unsafe {
750 assert!(JS_GetPendingException(cx, rval.handle_mut()));
751 JS_ClearPendingException(cx)
752 };
753
754 Err(Error::Type(c"can't transfer array buffer".to_owned()))
755 } else {
756 rooted!(&in(cx) let result = unsafe {
760 NewArrayBufferWithContents(cx, buffer_length, buffer_data)
761 });
762 if result.is_null() {
763 return Err(Error::JSFailed);
764 }
765 Ok(RootedTraceableBox::new(
766 HeapBufferSource::<ArrayBufferU8>::new(result.handle()),
767 ))
768 }
769 }
770}
771
772unsafe impl<T> crate::dom::bindings::trace::JSTraceable for HeapBufferSource<T> {
773 #[inline]
774 unsafe fn trace(&self, tracer: *mut js::jsapi::JSTracer) {
775 match &self.buffer_source {
776 BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => {
777 unsafe { buffer.trace(tracer) };
778 },
779 }
780 }
781}
782
783pub(crate) fn create_buffer_source<T>(
785 cx: &mut JSContext,
786 data: &[T::Element],
787 mut dest: MutableHandleObject,
788) -> Result<RootedTypedArray<T>, ()>
789where
790 T: TypedArrayElement + TypedArrayElementCreator,
791{
792 let res = unsafe {
793 TypedArray::<T, *mut JSObject>::create(
794 cx.raw_cx(),
795 CreateWith::Slice(data),
796 dest.reborrow(),
797 )
798 };
799
800 if res.is_err() {
801 Err(())
802 } else {
803 TypedArray::from(dest.get()).map(RootedTraceableBox::new)
804 }
805}
806
807fn create_buffer_source_with_length<T>(
808 cx: &mut JSContext,
809 len: usize,
810 mut dest: MutableHandleObject,
811) -> Result<RootedTypedArray<T>, ()>
812where
813 T: TypedArrayElement + TypedArrayElementCreator,
814{
815 let res = unsafe {
816 TypedArray::<T, *mut JSObject>::create(
817 cx.raw_cx(),
818 CreateWith::Length(len),
819 dest.reborrow(),
820 )
821 };
822
823 if res.is_err() {
824 Err(())
825 } else {
826 TypedArray::from(dest.get()).map(RootedTraceableBox::new)
827 }
828}
829
830pub(crate) fn byte_size(byte_type: Type) -> u64 {
831 match byte_type {
832 Type::Int8 | Type::Uint8 | Type::Uint8Clamped => 1,
833 Type::Int16 | Type::Uint16 | Type::Float16 => 2,
834 Type::Int32 | Type::Uint32 | Type::Float32 => 4,
835 Type::Int64 | Type::Float64 | Type::BigInt64 | Type::BigUint64 => 8,
836 Type::Simd128 => 16,
837 _ => unreachable!("invalid scalar type"),
838 }
839}
840
841#[derive(Clone, Eq, JSTraceable, MallocSizeOf, PartialEq)]
842pub(crate) enum Constructor {
843 DataView,
844 Name(
845 #[ignore_malloc_size_of = "mozjs"]
846 #[no_trace]
847 Type,
848 ),
849}
850
851pub(crate) fn create_buffer_source_with_constructor(
852 cx: &mut JSContext,
853 constructor: &Constructor,
854 buffer_source: &HeapBufferSource<ArrayBufferU8>,
855 byte_offset: usize,
856 byte_length: usize,
857) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferViewU8>>> {
858 match &buffer_source.buffer_source {
859 BufferSource::ArrayBuffer(heap) => match constructor {
860 Constructor::DataView => {
861 rooted!(&in(cx) let view = unsafe {
862 JS_NewDataView(
863 cx,
864 Handle::from_raw(heap.handle()),
865 byte_offset,
866 byte_length,
867 )
868 });
869 if view.is_null() {
870 return Err(Error::JSFailed);
871 }
872 Ok(RootedTraceableBox::new(HeapBufferSource::new(
873 view.handle(),
874 )))
875 },
876 Constructor::Name(name_type) => construct_typed_array(
877 cx,
878 name_type,
879 buffer_source,
880 byte_offset,
881 byte_length as i64,
882 ),
883 },
884 BufferSource::ArrayBufferView(_) => {
885 unreachable!("Can not create a new ArrayBufferView from an existing ArrayBufferView");
886 },
887 }
888}
889
890fn construct_typed_array(
892 cx: &mut JSContext,
893 name_type: &Type,
894 buffer_source: &HeapBufferSource<ArrayBufferU8>,
895 byte_offset: usize,
896 byte_length: i64,
897) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferViewU8>>> {
898 match &buffer_source.buffer_source {
899 BufferSource::ArrayBuffer(heap) => {
900 rooted!(&in(cx) let array_view = unsafe {
901 match name_type {
902 Type::Int8 => JS_NewInt8ArrayWithBuffer(
903 cx,
904 Handle::from_raw(heap.handle()),
905 byte_offset,
906 byte_length,
907 ),
908 Type::Uint8 => JS_NewUint8ArrayWithBuffer(
909 cx,
910 Handle::from_raw(heap.handle()),
911 byte_offset,
912 byte_length,
913 ),
914 Type::Uint16 => JS_NewUint16ArrayWithBuffer(
915 cx,
916 Handle::from_raw(heap.handle()),
917 byte_offset,
918 byte_length,
919 ),
920 Type::Int16 => JS_NewInt16ArrayWithBuffer(
921 cx,
922 Handle::from_raw(heap.handle()),
923 byte_offset,
924 byte_length,
925 ),
926 Type::Int32 => JS_NewInt32ArrayWithBuffer(
927 cx,
928 Handle::from_raw(heap.handle()),
929 byte_offset,
930 byte_length,
931 ),
932 Type::Uint32 => JS_NewUint32ArrayWithBuffer(
933 cx,
934 Handle::from_raw(heap.handle()),
935 byte_offset,
936 byte_length,
937 ),
938 Type::Float32 => JS_NewFloat32ArrayWithBuffer(
939 cx,
940 Handle::from_raw(heap.handle()),
941 byte_offset,
942 byte_length,
943 ),
944 Type::Float64 => JS_NewFloat64ArrayWithBuffer(
945 cx,
946 Handle::from_raw(heap.handle()),
947 byte_offset,
948 byte_length,
949 ),
950 Type::Uint8Clamped => JS_NewUint8ClampedArrayWithBuffer(
951 cx,
952 Handle::from_raw(heap.handle()),
953 byte_offset,
954 byte_length,
955 ),
956 Type::BigInt64 => JS_NewBigInt64ArrayWithBuffer(
957 cx,
958 Handle::from_raw(heap.handle()),
959 byte_offset,
960 byte_length,
961 ),
962 Type::BigUint64 => JS_NewBigUint64ArrayWithBuffer(
963 cx,
964 Handle::from_raw(heap.handle()),
965 byte_offset,
966 byte_length,
967 ),
968 Type::Float16 => JS_NewFloat16ArrayWithBuffer(
969 cx,
970 Handle::from_raw(heap.handle()),
971 byte_offset,
972 byte_length,
973 ),
974 Type::Int64 | Type::Simd128 | Type::MaxTypedArrayViewType => {
975 unreachable!("Invalid TypedArray type")
976 },
977 }
978 });
979 if array_view.is_null() {
980 return Err(Error::JSFailed);
981 }
982
983 Ok(RootedTraceableBox::new(HeapBufferSource::new(
984 array_view.handle(),
985 )))
986 },
987 BufferSource::ArrayBufferView(_) => {
988 unreachable!("Can not create a new ArrayBufferView from an existing ArrayBufferView");
989 },
990 }
991}
992
993pub(crate) fn create_array_buffer_with_size(
994 cx: &mut JSContext,
995 size: usize,
996) -> Fallible<RootedTraceableBox<HeapBufferSource<ArrayBufferU8>>> {
997 rooted!(&in(cx) let result = unsafe { NewArrayBuffer(cx, size) });
998 if result.is_null() {
999 rooted!(&in(cx) let mut rval = UndefinedValue());
1000 unsafe {
1001 assert!(JS_GetPendingException(cx, rval.handle_mut()));
1002 JS_ClearPendingException(cx)
1003 };
1004
1005 Err(Error::Type(c"can't create array buffer".to_owned()))
1006 } else {
1007 Ok(RootedTraceableBox::new(
1008 HeapBufferSource::<ArrayBufferU8>::new(result.handle()),
1009 ))
1010 }
1011}
1012
1013#[cfg(feature = "webgpu")]
1014#[derive(JSTraceable, MallocSizeOf)]
1015#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1016pub(crate) struct DataBlock {
1017 #[conditional_malloc_size_of]
1018 data: Arc<Box<[u8]>>,
1019 data_views: Vec<DataView>,
1021}
1022
1023#[cfg(feature = "webgpu")]
1026fn range_overlap<T: std::cmp::PartialOrd>(range1: &Range<T>, range2: &Range<T>) -> bool {
1027 range1.start < range2.end && range2.start < range1.end
1028}
1029
1030#[cfg(feature = "webgpu")]
1031impl DataBlock {
1032 pub(crate) fn new_zeroed(size: usize) -> Self {
1033 let data = vec![0; size];
1034 Self {
1035 data: Arc::new(data.into_boxed_slice()),
1036 data_views: Vec::new(),
1037 }
1038 }
1039
1040 pub(crate) fn load(&mut self, src: &[u8]) {
1042 Arc::get_mut(&mut self.data).unwrap().clone_from_slice(src)
1044 }
1045
1046 pub(crate) fn data(&mut self) -> &mut [u8] {
1048 Arc::get_mut(&mut self.data).unwrap()
1050 }
1051
1052 #[cfg_attr(
1053 crown,
1054 expect(
1055 crown::unrooted_must_root,
1056 reason = "Underlying content is rooted when GC can happen"
1057 )
1058 )]
1059 pub(crate) fn clear_views(&mut self, cx: &mut JSContext) {
1060 while let Some(DataView { buffer, .. }) = self.data_views.pop() {
1062 rooted!(&in(cx) let b = unsafe { buffer.underlying_object().get() });
1063 assert!(unsafe { DetachArrayBuffer(cx, b.handle()) })
1064 }
1065 }
1066
1067 pub(crate) fn view(
1069 &mut self,
1070 cx: &mut JSContext,
1071 range: Range<usize>,
1072 ) -> Result<&DataView, ()> {
1073 if self
1074 .data_views
1075 .iter()
1076 .any(|view| range_overlap(&view.range, &range))
1077 {
1078 return Err(());
1079 }
1080 let range_len = range
1081 .end
1082 .checked_sub(range.start)
1083 .expect("range end must be >= range start");
1084 assert!(range.end <= self.data.len());
1085
1086 unsafe extern "C" fn free_func(_contents: *mut c_void, free_user_data: *mut c_void) {
1089 let raw: *const Box<[u8]> = free_user_data.cast();
1090 drop(unsafe { Arc::from_raw(raw) });
1094 }
1095 let raw: *const Box<[u8]> = Arc::into_raw(Arc::clone(&self.data));
1096 let data_ptr = unsafe { (**raw).as_ptr().add(range.start) };
1100 rooted!(&in(cx) let object = unsafe {
1101 NewExternalArrayBuffer(
1102 cx,
1103 range_len,
1104 data_ptr.cast_mut().cast(),
1107 Some(free_func),
1108 raw as _,
1109 )
1110 });
1111 self.data_views.push(DataView {
1112 range,
1113 buffer: HeapArrayBuffer::from(*object).unwrap(),
1114 });
1115 Ok(self.data_views.last().unwrap())
1116 }
1117}
1118
1119#[cfg(feature = "webgpu")]
1122#[derive(JSTraceable, MallocSizeOf)]
1123#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
1124pub(crate) struct DataView {
1125 #[no_trace]
1126 range: Range<usize>,
1127 #[ignore_malloc_size_of = "defined in mozjs"]
1128 buffer: HeapArrayBuffer,
1129}
1130
1131#[cfg(feature = "webgpu")]
1132impl DataView {
1133 pub(crate) fn array_buffer(&self) -> RootedTraceableBox<HeapArrayBuffer> {
1134 RootedTraceableBox::new(unsafe {
1135 HeapArrayBuffer::from(self.buffer.underlying_object().get()).unwrap()
1136 })
1137 }
1138}