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