use crate::{
builtins::{array_buffer::SharedMemoryOrder, typed_array::integer_indexed_object::ContentType},
object::JsObject,
property::{PropertyDescriptor, PropertyKey},
Context, JsResult, JsValue,
};
use super::{InternalObjectMethods, ORDINARY_INTERNAL_METHODS};
pub(crate) static INTEGER_INDEXED_EXOTIC_INTERNAL_METHODS: InternalObjectMethods =
InternalObjectMethods {
__get_own_property__: integer_indexed_exotic_get_own_property,
__has_property__: integer_indexed_exotic_has_property,
__define_own_property__: integer_indexed_exotic_define_own_property,
__get__: integer_indexed_exotic_get,
__set__: integer_indexed_exotic_set,
__delete__: integer_indexed_exotic_delete,
__own_property_keys__: integer_indexed_exotic_own_property_keys,
..ORDINARY_INTERNAL_METHODS
};
#[inline]
pub(crate) fn integer_indexed_exotic_get_own_property(
obj: &JsObject,
key: &PropertyKey,
context: &mut Context,
) -> JsResult<Option<PropertyDescriptor>> {
if let PropertyKey::Index(index) = key {
Ok(
integer_indexed_element_get(obj, u64::from(*index)).map(|v| {
PropertyDescriptor::builder()
.value(v)
.writable(true)
.enumerable(true)
.configurable(true)
.build()
}),
)
} else {
super::ordinary_get_own_property(obj, key, context)
}
}
#[inline]
pub(crate) fn integer_indexed_exotic_has_property(
obj: &JsObject,
key: &PropertyKey,
context: &mut Context,
) -> JsResult<bool> {
if let PropertyKey::Index(index) = key {
Ok(is_valid_integer_index(obj, u64::from(*index)))
} else {
super::ordinary_has_property(obj, key, context)
}
}
#[inline]
pub(crate) fn integer_indexed_exotic_define_own_property(
obj: &JsObject,
key: PropertyKey,
desc: PropertyDescriptor,
context: &mut Context,
) -> JsResult<bool> {
if let PropertyKey::Index(index) = key {
if !is_valid_integer_index(obj, u64::from(index))
|| !desc
.configurable()
.or_else(|| desc.enumerable())
.or_else(|| desc.writable())
.unwrap_or(true)
|| desc.is_accessor_descriptor()
{
return Ok(false);
}
if let Some(value) = desc.value() {
integer_indexed_element_set(obj, index as usize, value, context)?;
}
Ok(true)
} else {
super::ordinary_define_own_property(obj, key, desc, context)
}
}
#[inline]
pub(crate) fn integer_indexed_exotic_get(
obj: &JsObject,
key: &PropertyKey,
receiver: JsValue,
context: &mut Context,
) -> JsResult<JsValue> {
if let PropertyKey::Index(index) = key {
Ok(integer_indexed_element_get(obj, u64::from(*index)).unwrap_or_default())
} else {
super::ordinary_get(obj, key, receiver, context)
}
}
#[inline]
pub(crate) fn integer_indexed_exotic_set(
obj: &JsObject,
key: PropertyKey,
value: JsValue,
receiver: JsValue,
context: &mut Context,
) -> JsResult<bool> {
if let PropertyKey::Index(index) = key {
integer_indexed_element_set(obj, index as usize, &value, context)?;
Ok(true)
} else {
super::ordinary_set(obj, key, value, receiver, context)
}
}
#[inline]
pub(crate) fn integer_indexed_exotic_delete(
obj: &JsObject,
key: &PropertyKey,
context: &mut Context,
) -> JsResult<bool> {
if let PropertyKey::Index(index) = key {
Ok(!is_valid_integer_index(obj, u64::from(*index)))
} else {
super::ordinary_delete(obj, key, context)
}
}
#[inline]
#[allow(clippy::unnecessary_wraps)]
pub(crate) fn integer_indexed_exotic_own_property_keys(
obj: &JsObject,
_context: &mut Context,
) -> JsResult<Vec<PropertyKey>> {
let obj = obj.borrow();
let inner = obj.as_typed_array().expect(
"integer indexed exotic method should only be callable from integer indexed objects",
);
let mut keys = if inner.is_detached() {
vec![]
} else {
(0..inner.array_length())
.into_iter()
.map(|index| PropertyKey::Index(index as u32))
.collect()
};
keys.extend(
obj.properties
.string_property_keys()
.cloned()
.map(Into::into),
);
keys.extend(
obj.properties
.symbol_property_keys()
.cloned()
.map(Into::into),
);
Ok(keys)
}
pub(crate) fn is_valid_integer_index(obj: &JsObject, index: u64) -> bool {
let obj = obj.borrow();
let inner = obj.as_typed_array().expect(
"integer indexed exotic method should only be callable from integer indexed objects",
);
!inner.is_detached() && index < inner.array_length()
}
fn integer_indexed_element_get(obj: &JsObject, index: u64) -> Option<JsValue> {
if !is_valid_integer_index(obj, index) {
return None;
}
let obj = obj.borrow();
let inner = obj
.as_typed_array()
.expect("Already checked for detached buffer");
let buffer_obj = inner
.viewed_array_buffer()
.expect("Already checked for detached buffer");
let buffer_obj_borrow = buffer_obj.borrow();
let buffer = buffer_obj_borrow
.as_array_buffer()
.expect("Already checked for detached buffer");
let offset = inner.byte_offset();
let elem_type = inner.typed_array_name();
let size = elem_type.element_size();
let indexed_position = (index * size) + offset;
Some(buffer.get_value_from_buffer(
indexed_position,
elem_type,
true,
SharedMemoryOrder::Unordered,
None,
))
}
fn integer_indexed_element_set(
obj: &JsObject,
index: usize,
value: &JsValue,
context: &mut Context,
) -> JsResult<()> {
let obj_borrow = obj.borrow();
let inner = obj_borrow.as_typed_array().expect(
"integer indexed exotic method should only be callable from integer indexed objects",
);
let num_value = if inner.typed_array_name().content_type() == ContentType::BigInt {
value.to_bigint(context)?.into()
} else {
value.to_number(context)?.into()
};
if is_valid_integer_index(obj, index as u64) {
let offset = inner.byte_offset();
let elem_type = inner.typed_array_name();
let size = elem_type.element_size();
let indexed_position = (index as u64 * size) + offset;
let buffer_obj = inner
.viewed_array_buffer()
.expect("Already checked for detached buffer");
let mut buffer_obj_borrow = buffer_obj.borrow_mut();
let buffer = buffer_obj_borrow
.as_array_buffer_mut()
.expect("Already checked for detached buffer");
buffer
.set_value_in_buffer(
indexed_position,
elem_type,
&num_value,
SharedMemoryOrder::Unordered,
None,
context,
)
.expect("SetValueInBuffer cannot fail here");
}
Ok(())
}