1use crate::{
3 Context, JsExpect, JsNativeError, JsResult, JsValue,
4 builtins::{DataView, array_buffer::BufferObject},
5 object::{JsArrayBuffer, JsObject},
6 value::TryFromJs,
7};
8
9use boa_gc::{Finalize, Trace};
10use std::ops::Deref;
11
12#[derive(Debug, Clone, Trace, Finalize)]
33#[boa_gc(unsafe_no_drop)]
34pub struct JsDataView {
35 inner: JsObject<DataView>,
36}
37
38impl From<JsDataView> for JsObject<DataView> {
39 #[inline]
40 fn from(value: JsDataView) -> Self {
41 value.inner
42 }
43}
44
45impl From<JsObject<DataView>> for JsDataView {
46 #[inline]
47 fn from(value: JsObject<DataView>) -> Self {
48 Self { inner: value }
49 }
50}
51
52impl JsDataView {
53 pub fn from_js_array_buffer(
55 buffer: JsArrayBuffer,
56 offset: Option<u64>,
57 byte_len: Option<u64>,
58 context: &mut Context,
59 ) -> JsResult<Self> {
60 let offset = offset.unwrap_or_default();
61
62 let (buf_byte_len, is_fixed_len) = {
63 let buffer = buffer.borrow();
64 let buffer = buffer.data();
65
66 let Some(slice) = buffer.bytes() else {
68 return Err(JsNativeError::typ()
69 .with_message("ArrayBuffer is detached")
70 .into());
71 };
72
73 let buf_len = slice.len() as u64;
75
76 if offset > buf_len {
78 return Err(JsNativeError::range()
79 .with_message("Start offset is outside the bounds of the buffer")
80 .into());
81 }
82
83 (buf_len, buffer.is_fixed_len())
85 };
86
87 let view_byte_len = if let Some(byte_len) = byte_len {
89 if offset + byte_len > buf_byte_len {
93 return Err(JsNativeError::range()
94 .with_message("Invalid data view length")
95 .into());
96 }
97
98 Some(byte_len)
99 } else {
100 is_fixed_len.then_some(buf_byte_len - offset)
105 };
106
107 let prototype = context.intrinsics().constructors().data_view().prototype();
110
111 let Some(buf_byte_len) = buffer.borrow().data().bytes().map(|s| s.len() as u64) else {
114 return Err(JsNativeError::typ()
115 .with_message("ArrayBuffer is detached")
116 .into());
117 };
118
119 if offset > buf_byte_len {
121 return Err(JsNativeError::range()
122 .with_message("DataView offset outside of buffer array bounds")
123 .into());
124 }
125
126 if let Some(view_byte_len) = view_byte_len
129 && byte_len.is_some()
130 && offset + view_byte_len > buf_byte_len
131 {
132 return Err(JsNativeError::range()
133 .with_message("DataView offset outside of buffer array bounds")
134 .into());
135 }
136
137 let obj = JsObject::new(
138 context.root_shape(),
139 prototype,
140 DataView {
141 viewed_array_buffer: BufferObject::Buffer(buffer.into()),
143 byte_length: view_byte_len,
145 byte_offset: offset,
147 },
148 );
149
150 Ok(Self { inner: obj })
152 }
153
154 #[inline]
156 pub fn from_object(object: JsObject) -> JsResult<Self> {
157 object
158 .downcast::<DataView>()
159 .map(|inner| Self { inner })
160 .map_err(|_| {
161 JsNativeError::typ()
162 .with_message("object is not a DataView")
163 .into()
164 })
165 }
166
167 #[inline]
169 pub fn buffer(&self, context: &mut Context) -> JsResult<JsValue> {
170 DataView::get_buffer(&self.inner.clone().upcast().into(), &[], context)
171 }
172
173 #[inline]
175 pub fn byte_length(&self, context: &mut Context) -> JsResult<u64> {
176 DataView::get_byte_length(&self.inner.clone().upcast().into(), &[], context).and_then(|v| {
177 v.as_number()
178 .js_expect("value should be a number")
179 .map(|n| n as u64)
180 .map_err(Into::into)
181 })
182 }
183
184 #[inline]
186 pub fn byte_offset(&self, context: &mut Context) -> JsResult<u64> {
187 DataView::get_byte_offset(&self.inner.clone().upcast().into(), &[], context).and_then(|v| {
188 v.as_number()
189 .js_expect("byte_offset value must be a number")
190 .map(|n| n as u64)
191 .map_err(Into::into)
192 })
193 }
194
195 #[inline]
197 pub fn get_big_int64(
198 &self,
199 byte_offset: usize,
200 is_little_endian: bool,
201 context: &mut Context,
202 ) -> JsResult<i64> {
203 DataView::get_big_int64(
204 &self.inner.clone().upcast().into(),
205 &[byte_offset.into(), is_little_endian.into()],
206 context,
207 )
208 .and_then(|v| {
209 v.as_number()
210 .js_expect("value must be a number")
211 .map(|n| n as i64)
212 .map_err(Into::into)
213 })
214 }
215
216 #[inline]
218 pub fn get_big_uint64(
219 &self,
220 byte_offset: usize,
221 is_little_endian: bool,
222 context: &mut Context,
223 ) -> JsResult<u64> {
224 DataView::get_big_uint64(
225 &self.inner.clone().upcast().into(),
226 &[byte_offset.into(), is_little_endian.into()],
227 context,
228 )
229 .and_then(|v| {
230 v.as_number()
231 .js_expect("value must be a number")
232 .map(|n| n as u64)
233 .map_err(Into::into)
234 })
235 }
236
237 #[inline]
239 pub fn get_float32(
240 &self,
241 byte_offset: usize,
242 is_little_endian: bool,
243 context: &mut Context,
244 ) -> JsResult<f32> {
245 DataView::get_float32(
246 &self.inner.clone().upcast().into(),
247 &[byte_offset.into(), is_little_endian.into()],
248 context,
249 )
250 .and_then(|v| {
251 v.as_number()
252 .js_expect("value must be a number")
253 .map(|n| n as f32)
254 .map_err(Into::into)
255 })
256 }
257
258 #[inline]
260 pub fn get_float64(
261 &self,
262 byte_offset: usize,
263 is_little_endian: bool,
264 context: &mut Context,
265 ) -> JsResult<f64> {
266 DataView::get_float64(
267 &self.inner.clone().upcast().into(),
268 &[byte_offset.into(), is_little_endian.into()],
269 context,
270 )
271 .and_then(|v| {
272 v.as_number()
273 .js_expect("value must be a number")
274 .map_err(Into::into)
275 })
276 }
277
278 #[inline]
280 pub fn get_int8(
281 &self,
282 byte_offset: usize,
283 is_little_endian: bool,
284 context: &mut Context,
285 ) -> JsResult<i8> {
286 DataView::get_int8(
287 &self.inner.clone().upcast().into(),
288 &[byte_offset.into(), is_little_endian.into()],
289 context,
290 )
291 .and_then(|v| {
292 v.as_number()
293 .js_expect("value must be a number")
294 .map(|n| n as i8)
295 .map_err(Into::into)
296 })
297 }
298
299 #[inline]
301 pub fn get_int16(
302 &self,
303 byte_offset: usize,
304 is_little_endian: bool,
305 context: &mut Context,
306 ) -> JsResult<i16> {
307 DataView::get_int16(
308 &self.inner.clone().upcast().into(),
309 &[byte_offset.into(), is_little_endian.into()],
310 context,
311 )
312 .and_then(|v| {
313 v.as_number()
314 .js_expect("value must be a number")
315 .map(|n| n as i16)
316 .map_err(Into::into)
317 })
318 }
319
320 #[inline]
322 pub fn get_int32(
323 &self,
324 byte_offset: usize,
325 is_little_endian: bool,
326 context: &mut Context,
327 ) -> JsResult<i32> {
328 DataView::get_int32(
329 &self.inner.clone().upcast().into(),
330 &[byte_offset.into(), is_little_endian.into()],
331 context,
332 )
333 .and_then(|v| {
334 v.as_number()
335 .js_expect("value must be a number")
336 .map(|n| n as i32)
337 .map_err(Into::into)
338 })
339 }
340
341 #[inline]
343 pub fn get_uint8(
344 &self,
345 byte_offset: usize,
346 is_little_endian: bool,
347 context: &mut Context,
348 ) -> JsResult<u8> {
349 DataView::get_uint8(
350 &self.inner.clone().upcast().into(),
351 &[byte_offset.into(), is_little_endian.into()],
352 context,
353 )
354 .and_then(|v| {
355 v.as_number()
356 .js_expect("value must be a number")
357 .map(|n| n as u8)
358 .map_err(Into::into)
359 })
360 }
361
362 #[inline]
364 pub fn get_unit16(
365 &self,
366 byte_offset: usize,
367 is_little_endian: bool,
368 context: &mut Context,
369 ) -> JsResult<u16> {
370 DataView::get_uint16(
371 &self.inner.clone().upcast().into(),
372 &[byte_offset.into(), is_little_endian.into()],
373 context,
374 )
375 .and_then(|v| {
376 v.as_number()
377 .js_expect("value must be a number")
378 .map(|n| n as u16)
379 .map_err(Into::into)
380 })
381 }
382
383 #[inline]
385 pub fn get_uint32(
386 &self,
387 byte_offset: usize,
388 is_little_endian: bool,
389 context: &mut Context,
390 ) -> JsResult<u32> {
391 DataView::get_uint32(
392 &self.inner.clone().upcast().into(),
393 &[byte_offset.into(), is_little_endian.into()],
394 context,
395 )
396 .and_then(|v| {
397 v.as_number()
398 .js_expect("value must be a number")
399 .map(|n| n as u32)
400 .map_err(Into::into)
401 })
402 }
403
404 #[inline]
406 pub fn set_big_int64(
407 &self,
408 byte_offset: usize,
409 value: i64,
410 is_little_endian: bool,
411 context: &mut Context,
412 ) -> JsResult<JsValue> {
413 DataView::set_big_int64(
414 &self.inner.clone().upcast().into(),
415 &[byte_offset.into(), value.into(), is_little_endian.into()],
416 context,
417 )
418 }
419
420 #[inline]
422 pub fn set_big_uint64(
423 &self,
424 byte_offset: usize,
425 value: u64,
426 is_little_endian: bool,
427 context: &mut Context,
428 ) -> JsResult<JsValue> {
429 DataView::set_big_uint64(
430 &self.inner.clone().upcast().into(),
431 &[byte_offset.into(), value.into(), is_little_endian.into()],
432 context,
433 )
434 }
435
436 #[inline]
438 pub fn set_float32(
439 &self,
440 byte_offset: usize,
441 value: f32,
442 is_little_endian: bool,
443 context: &mut Context,
444 ) -> JsResult<JsValue> {
445 DataView::set_float32(
446 &self.inner.clone().upcast().into(),
447 &[byte_offset.into(), value.into(), is_little_endian.into()],
448 context,
449 )
450 }
451
452 #[inline]
454 pub fn set_float64(
455 &self,
456 byte_offset: usize,
457 value: f64,
458 is_little_endian: bool,
459 context: &mut Context,
460 ) -> JsResult<JsValue> {
461 DataView::set_float64(
462 &self.inner.clone().upcast().into(),
463 &[byte_offset.into(), value.into(), is_little_endian.into()],
464 context,
465 )
466 }
467
468 #[inline]
470 pub fn set_int8(
471 &self,
472 byte_offset: usize,
473 value: i8,
474 is_little_endian: bool,
475 context: &mut Context,
476 ) -> JsResult<JsValue> {
477 DataView::set_int8(
478 &self.inner.clone().upcast().into(),
479 &[byte_offset.into(), value.into(), is_little_endian.into()],
480 context,
481 )
482 }
483
484 #[inline]
486 pub fn set_int16(
487 &self,
488 byte_offset: usize,
489 value: i16,
490 is_little_endian: bool,
491 context: &mut Context,
492 ) -> JsResult<JsValue> {
493 DataView::set_int16(
494 &self.inner.clone().upcast().into(),
495 &[byte_offset.into(), value.into(), is_little_endian.into()],
496 context,
497 )
498 }
499
500 #[inline]
502 pub fn set_int32(
503 &self,
504 byte_offset: usize,
505 value: i32,
506 is_little_endian: bool,
507 context: &mut Context,
508 ) -> JsResult<JsValue> {
509 DataView::set_int32(
510 &self.inner.clone().upcast().into(),
511 &[byte_offset.into(), value.into(), is_little_endian.into()],
512 context,
513 )
514 }
515
516 #[inline]
518 pub fn set_uint8(
519 &self,
520 byte_offset: usize,
521 value: u8,
522 is_little_endian: bool,
523 context: &mut Context,
524 ) -> JsResult<JsValue> {
525 DataView::set_uint8(
526 &self.inner.clone().upcast().into(),
527 &[byte_offset.into(), value.into(), is_little_endian.into()],
528 context,
529 )
530 }
531
532 #[inline]
534 pub fn set_unit16(
535 &self,
536 byte_offset: usize,
537 value: u16,
538 is_little_endian: bool,
539 context: &mut Context,
540 ) -> JsResult<JsValue> {
541 DataView::set_uint16(
542 &self.inner.clone().upcast().into(),
543 &[byte_offset.into(), value.into(), is_little_endian.into()],
544 context,
545 )
546 }
547
548 #[inline]
550 pub fn set_unit32(
551 &self,
552 byte_offset: usize,
553 value: u32,
554 is_little_endian: bool,
555 context: &mut Context,
556 ) -> JsResult<JsValue> {
557 DataView::set_uint32(
558 &self.inner.clone().upcast().into(),
559 &[byte_offset.into(), value.into(), is_little_endian.into()],
560 context,
561 )
562 }
563}
564
565impl From<JsDataView> for JsObject {
566 #[inline]
567 fn from(o: JsDataView) -> Self {
568 o.inner.upcast()
569 }
570}
571
572impl From<JsDataView> for JsValue {
573 #[inline]
574 fn from(o: JsDataView) -> Self {
575 o.inner.upcast().into()
576 }
577}
578
579impl Deref for JsDataView {
580 type Target = JsObject<DataView>;
581
582 #[inline]
583 fn deref(&self) -> &Self::Target {
584 &self.inner
585 }
586}
587
588impl TryFromJs for JsDataView {
589 fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
590 if let Some(o) = value.as_object() {
591 Self::from_object(o.clone())
592 } else {
593 Err(JsNativeError::typ()
594 .with_message("value is not a DataView object")
595 .into())
596 }
597 }
598}