use crate::{
builtins::DataView,
context::intrinsics::StandardConstructors,
object::{
internal_methods::get_prototype_from_constructor, JsArrayBuffer, JsObject, JsObjectType,
ObjectData,
},
value::TryFromJs,
Context, JsNativeError, JsResult, JsValue,
};
use boa_gc::{Finalize, Trace};
use std::ops::Deref;
#[derive(Debug, Clone, Trace, Finalize)]
pub struct JsDataView {
inner: JsObject,
}
impl JsDataView {
pub fn from_js_array_buffer(
array_buffer: &JsArrayBuffer,
offset: Option<u64>,
byte_length: Option<u64>,
context: &mut Context<'_>,
) -> JsResult<Self> {
let (byte_offset, byte_length) = {
let borrowed_buffer = array_buffer.borrow();
let buffer = borrowed_buffer.as_array_buffer().ok_or_else(|| {
JsNativeError::typ().with_message("buffer must be an ArrayBuffer")
})?;
let provided_offset = offset.unwrap_or(0_u64);
if buffer.is_detached_buffer() {
return Err(JsNativeError::typ()
.with_message("ArrayBuffer is detached")
.into());
};
let array_buffer_length = buffer.array_buffer_byte_length();
if provided_offset > array_buffer_length {
return Err(JsNativeError::range()
.with_message("Provided offset is outside the bounds of the buffer")
.into());
}
let view_byte_length = if let Some(..) = byte_length {
let provided_length = byte_length.expect("byte_length must be a u64");
if provided_offset + provided_length > array_buffer_length {
return Err(JsNativeError::range()
.with_message("Invalid data view length")
.into());
}
provided_length
} else {
array_buffer_length - provided_offset
};
(provided_offset, view_byte_length)
};
let constructor = context
.intrinsics()
.constructors()
.data_view()
.constructor()
.into();
let prototype =
get_prototype_from_constructor(&constructor, StandardConstructors::data_view, context)?;
let obj = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
ObjectData::data_view(DataView {
viewed_array_buffer: (**array_buffer).clone(),
byte_length,
byte_offset,
}),
);
Ok(Self { inner: obj })
}
#[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.is_data_view() {
Ok(Self { inner: object })
} else {
Err(JsNativeError::typ()
.with_message("object is not a DataView")
.into())
}
}
#[inline]
pub fn buffer(&self, context: &mut Context<'_>) -> JsResult<JsValue> {
DataView::get_buffer(&self.inner.clone().into(), &[], context)
}
#[inline]
pub fn byte_length(&self, context: &mut Context<'_>) -> JsResult<u64> {
DataView::get_byte_length(&self.inner.clone().into(), &[], context)
.map(|v| v.as_number().expect("value should be a number") as u64)
}
#[inline]
pub fn byte_offset(&self, context: &mut Context<'_>) -> JsResult<u64> {
DataView::get_byte_offset(&self.inner.clone().into(), &[], context)
.map(|v| v.as_number().expect("byte_offset value must be a number") as u64)
}
#[inline]
pub fn get_big_int64(
&self,
byte_offset: usize,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<i64> {
DataView::get_big_int64(
&self.inner.clone().into(),
&[byte_offset.into(), is_little_endian.into()],
context,
)
.map(|v| v.as_number().expect("value must be a number") as i64)
}
#[inline]
pub fn get_big_uint64(
&self,
byte_offset: usize,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<u64> {
DataView::get_big_uint64(
&self.inner.clone().into(),
&[byte_offset.into(), is_little_endian.into()],
context,
)
.map(|v| v.as_number().expect("value must be a number") as u64)
}
#[inline]
pub fn get_float32(
&self,
byte_offset: usize,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<f32> {
DataView::get_float32(
&self.inner.clone().into(),
&[byte_offset.into(), is_little_endian.into()],
context,
)
.map(|v| v.as_number().expect("value must be a number") as f32)
}
#[inline]
pub fn get_float64(
&self,
byte_offset: usize,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<f64> {
DataView::get_float64(
&self.inner.clone().into(),
&[byte_offset.into(), is_little_endian.into()],
context,
)
.map(|v| v.as_number().expect("value must be a number"))
}
#[inline]
pub fn get_int8(
&self,
byte_offset: usize,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<i8> {
DataView::get_int8(
&self.inner.clone().into(),
&[byte_offset.into(), is_little_endian.into()],
context,
)
.map(|v| v.as_number().expect("value must be a number") as i8)
}
#[inline]
pub fn get_int16(
&self,
byte_offset: usize,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<i16> {
DataView::get_int16(
&self.inner.clone().into(),
&[byte_offset.into(), is_little_endian.into()],
context,
)
.map(|v| v.as_number().expect("value must be a number") as i16)
}
#[inline]
pub fn get_int32(
&self,
byte_offset: usize,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<i32> {
DataView::get_int32(
&self.inner.clone().into(),
&[byte_offset.into(), is_little_endian.into()],
context,
)
.map(|v| v.as_number().expect("value must be a number") as i32)
}
#[inline]
pub fn get_uint8(
&self,
byte_offset: usize,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<u8> {
DataView::get_uint8(
&self.inner.clone().into(),
&[byte_offset.into(), is_little_endian.into()],
context,
)
.map(|v| v.as_number().expect("value must be a number") as u8)
}
#[inline]
pub fn get_unit16(
&self,
byte_offset: usize,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<u16> {
DataView::get_uint16(
&self.inner.clone().into(),
&[byte_offset.into(), is_little_endian.into()],
context,
)
.map(|v| v.as_number().expect("value must be a number") as u16)
}
#[inline]
pub fn get_uint32(
&self,
byte_offset: usize,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<u32> {
DataView::get_uint32(
&self.inner.clone().into(),
&[byte_offset.into(), is_little_endian.into()],
context,
)
.map(|v| v.as_number().expect("value must be a number") as u32)
}
#[inline]
pub fn set_big_int64(
&self,
byte_offset: usize,
value: i64,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<JsValue> {
DataView::set_big_int64(
&self.inner.clone().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()],
context,
)
}
#[inline]
pub fn set_big_uint64(
&self,
byte_offset: usize,
value: u64,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<JsValue> {
DataView::set_big_uint64(
&self.inner.clone().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()],
context,
)
}
#[inline]
pub fn set_float32(
&self,
byte_offset: usize,
value: f32,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<JsValue> {
DataView::set_float32(
&self.inner.clone().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()],
context,
)
}
#[inline]
pub fn set_float64(
&self,
byte_offset: usize,
value: f64,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<JsValue> {
DataView::set_float64(
&self.inner.clone().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()],
context,
)
}
#[inline]
pub fn set_int8(
&self,
byte_offset: usize,
value: i8,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<JsValue> {
DataView::set_int8(
&self.inner.clone().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()],
context,
)
}
#[inline]
pub fn set_int16(
&self,
byte_offset: usize,
value: i16,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<JsValue> {
DataView::set_int16(
&self.inner.clone().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()],
context,
)
}
#[inline]
pub fn set_int32(
&self,
byte_offset: usize,
value: i32,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<JsValue> {
DataView::set_int32(
&self.inner.clone().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()],
context,
)
}
#[inline]
pub fn set_uint8(
&self,
byte_offset: usize,
value: u8,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<JsValue> {
DataView::set_uint8(
&self.inner.clone().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()],
context,
)
}
#[inline]
pub fn set_unit16(
&self,
byte_offset: usize,
value: u16,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<JsValue> {
DataView::set_uint16(
&self.inner.clone().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()],
context,
)
}
#[inline]
pub fn set_unit32(
&self,
byte_offset: usize,
value: u32,
is_little_endian: bool,
context: &mut Context<'_>,
) -> JsResult<JsValue> {
DataView::set_uint32(
&self.inner.clone().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()],
context,
)
}
}
impl From<JsDataView> for JsObject {
#[inline]
fn from(o: JsDataView) -> Self {
o.inner.clone()
}
}
impl From<JsDataView> for JsValue {
#[inline]
fn from(o: JsDataView) -> Self {
o.inner.clone().into()
}
}
impl Deref for JsDataView {
type Target = JsObject;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl JsObjectType for JsDataView {}
impl TryFromJs for JsDataView {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not an DataView object")
.into()),
}
}
}