1use crate::{
3 Context, 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)
177 .map(|v| v.as_number().expect("value should be a number") as u64)
178 }
179
180 #[inline]
182 pub fn byte_offset(&self, context: &mut Context) -> JsResult<u64> {
183 DataView::get_byte_offset(&self.inner.clone().upcast().into(), &[], context)
184 .map(|v| v.as_number().expect("byte_offset value must be a number") as u64)
185 }
186
187 #[inline]
189 pub fn get_big_int64(
190 &self,
191 byte_offset: usize,
192 is_little_endian: bool,
193 context: &mut Context,
194 ) -> JsResult<i64> {
195 DataView::get_big_int64(
196 &self.inner.clone().upcast().into(),
197 &[byte_offset.into(), is_little_endian.into()],
198 context,
199 )
200 .map(|v| v.as_number().expect("value must be a number") as i64)
201 }
202
203 #[inline]
205 pub fn get_big_uint64(
206 &self,
207 byte_offset: usize,
208 is_little_endian: bool,
209 context: &mut Context,
210 ) -> JsResult<u64> {
211 DataView::get_big_uint64(
212 &self.inner.clone().upcast().into(),
213 &[byte_offset.into(), is_little_endian.into()],
214 context,
215 )
216 .map(|v| v.as_number().expect("value must be a number") as u64)
217 }
218
219 #[inline]
221 pub fn get_float32(
222 &self,
223 byte_offset: usize,
224 is_little_endian: bool,
225 context: &mut Context,
226 ) -> JsResult<f32> {
227 DataView::get_float32(
228 &self.inner.clone().upcast().into(),
229 &[byte_offset.into(), is_little_endian.into()],
230 context,
231 )
232 .map(|v| v.as_number().expect("value must be a number") as f32)
233 }
234
235 #[inline]
237 pub fn get_float64(
238 &self,
239 byte_offset: usize,
240 is_little_endian: bool,
241 context: &mut Context,
242 ) -> JsResult<f64> {
243 DataView::get_float64(
244 &self.inner.clone().upcast().into(),
245 &[byte_offset.into(), is_little_endian.into()],
246 context,
247 )
248 .map(|v| v.as_number().expect("value must be a number"))
249 }
250
251 #[inline]
253 pub fn get_int8(
254 &self,
255 byte_offset: usize,
256 is_little_endian: bool,
257 context: &mut Context,
258 ) -> JsResult<i8> {
259 DataView::get_int8(
260 &self.inner.clone().upcast().into(),
261 &[byte_offset.into(), is_little_endian.into()],
262 context,
263 )
264 .map(|v| v.as_number().expect("value must be a number") as i8)
265 }
266
267 #[inline]
269 pub fn get_int16(
270 &self,
271 byte_offset: usize,
272 is_little_endian: bool,
273 context: &mut Context,
274 ) -> JsResult<i16> {
275 DataView::get_int16(
276 &self.inner.clone().upcast().into(),
277 &[byte_offset.into(), is_little_endian.into()],
278 context,
279 )
280 .map(|v| v.as_number().expect("value must be a number") as i16)
281 }
282
283 #[inline]
285 pub fn get_int32(
286 &self,
287 byte_offset: usize,
288 is_little_endian: bool,
289 context: &mut Context,
290 ) -> JsResult<i32> {
291 DataView::get_int32(
292 &self.inner.clone().upcast().into(),
293 &[byte_offset.into(), is_little_endian.into()],
294 context,
295 )
296 .map(|v| v.as_number().expect("value must be a number") as i32)
297 }
298
299 #[inline]
301 pub fn get_uint8(
302 &self,
303 byte_offset: usize,
304 is_little_endian: bool,
305 context: &mut Context,
306 ) -> JsResult<u8> {
307 DataView::get_uint8(
308 &self.inner.clone().upcast().into(),
309 &[byte_offset.into(), is_little_endian.into()],
310 context,
311 )
312 .map(|v| v.as_number().expect("value must be a number") as u8)
313 }
314
315 #[inline]
317 pub fn get_unit16(
318 &self,
319 byte_offset: usize,
320 is_little_endian: bool,
321 context: &mut Context,
322 ) -> JsResult<u16> {
323 DataView::get_uint16(
324 &self.inner.clone().upcast().into(),
325 &[byte_offset.into(), is_little_endian.into()],
326 context,
327 )
328 .map(|v| v.as_number().expect("value must be a number") as u16)
329 }
330
331 #[inline]
333 pub fn get_uint32(
334 &self,
335 byte_offset: usize,
336 is_little_endian: bool,
337 context: &mut Context,
338 ) -> JsResult<u32> {
339 DataView::get_uint32(
340 &self.inner.clone().upcast().into(),
341 &[byte_offset.into(), is_little_endian.into()],
342 context,
343 )
344 .map(|v| v.as_number().expect("value must be a number") as u32)
345 }
346
347 #[inline]
349 pub fn set_big_int64(
350 &self,
351 byte_offset: usize,
352 value: i64,
353 is_little_endian: bool,
354 context: &mut Context,
355 ) -> JsResult<JsValue> {
356 DataView::set_big_int64(
357 &self.inner.clone().upcast().into(),
358 &[byte_offset.into(), value.into(), is_little_endian.into()],
359 context,
360 )
361 }
362
363 #[inline]
365 pub fn set_big_uint64(
366 &self,
367 byte_offset: usize,
368 value: u64,
369 is_little_endian: bool,
370 context: &mut Context,
371 ) -> JsResult<JsValue> {
372 DataView::set_big_uint64(
373 &self.inner.clone().upcast().into(),
374 &[byte_offset.into(), value.into(), is_little_endian.into()],
375 context,
376 )
377 }
378
379 #[inline]
381 pub fn set_float32(
382 &self,
383 byte_offset: usize,
384 value: f32,
385 is_little_endian: bool,
386 context: &mut Context,
387 ) -> JsResult<JsValue> {
388 DataView::set_float32(
389 &self.inner.clone().upcast().into(),
390 &[byte_offset.into(), value.into(), is_little_endian.into()],
391 context,
392 )
393 }
394
395 #[inline]
397 pub fn set_float64(
398 &self,
399 byte_offset: usize,
400 value: f64,
401 is_little_endian: bool,
402 context: &mut Context,
403 ) -> JsResult<JsValue> {
404 DataView::set_float64(
405 &self.inner.clone().upcast().into(),
406 &[byte_offset.into(), value.into(), is_little_endian.into()],
407 context,
408 )
409 }
410
411 #[inline]
413 pub fn set_int8(
414 &self,
415 byte_offset: usize,
416 value: i8,
417 is_little_endian: bool,
418 context: &mut Context,
419 ) -> JsResult<JsValue> {
420 DataView::set_int8(
421 &self.inner.clone().upcast().into(),
422 &[byte_offset.into(), value.into(), is_little_endian.into()],
423 context,
424 )
425 }
426
427 #[inline]
429 pub fn set_int16(
430 &self,
431 byte_offset: usize,
432 value: i16,
433 is_little_endian: bool,
434 context: &mut Context,
435 ) -> JsResult<JsValue> {
436 DataView::set_int16(
437 &self.inner.clone().upcast().into(),
438 &[byte_offset.into(), value.into(), is_little_endian.into()],
439 context,
440 )
441 }
442
443 #[inline]
445 pub fn set_int32(
446 &self,
447 byte_offset: usize,
448 value: i32,
449 is_little_endian: bool,
450 context: &mut Context,
451 ) -> JsResult<JsValue> {
452 DataView::set_int32(
453 &self.inner.clone().upcast().into(),
454 &[byte_offset.into(), value.into(), is_little_endian.into()],
455 context,
456 )
457 }
458
459 #[inline]
461 pub fn set_uint8(
462 &self,
463 byte_offset: usize,
464 value: u8,
465 is_little_endian: bool,
466 context: &mut Context,
467 ) -> JsResult<JsValue> {
468 DataView::set_uint8(
469 &self.inner.clone().upcast().into(),
470 &[byte_offset.into(), value.into(), is_little_endian.into()],
471 context,
472 )
473 }
474
475 #[inline]
477 pub fn set_unit16(
478 &self,
479 byte_offset: usize,
480 value: u16,
481 is_little_endian: bool,
482 context: &mut Context,
483 ) -> JsResult<JsValue> {
484 DataView::set_uint16(
485 &self.inner.clone().upcast().into(),
486 &[byte_offset.into(), value.into(), is_little_endian.into()],
487 context,
488 )
489 }
490
491 #[inline]
493 pub fn set_unit32(
494 &self,
495 byte_offset: usize,
496 value: u32,
497 is_little_endian: bool,
498 context: &mut Context,
499 ) -> JsResult<JsValue> {
500 DataView::set_uint32(
501 &self.inner.clone().upcast().into(),
502 &[byte_offset.into(), value.into(), is_little_endian.into()],
503 context,
504 )
505 }
506}
507
508impl From<JsDataView> for JsObject {
509 #[inline]
510 fn from(o: JsDataView) -> Self {
511 o.inner.upcast()
512 }
513}
514
515impl From<JsDataView> for JsValue {
516 #[inline]
517 fn from(o: JsDataView) -> Self {
518 o.inner.upcast().into()
519 }
520}
521
522impl Deref for JsDataView {
523 type Target = JsObject<DataView>;
524
525 #[inline]
526 fn deref(&self) -> &Self::Target {
527 &self.inner
528 }
529}
530
531impl TryFromJs for JsDataView {
532 fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
533 if let Some(o) = value.as_object() {
534 Self::from_object(o.clone())
535 } else {
536 Err(JsNativeError::typ()
537 .with_message("value is not a DataView object")
538 .into())
539 }
540 }
541}