use crate::{
object::JsObject,
property::{PropertyDescriptor, PropertyKey},
Context, JsResult,
};
use super::{InternalObjectMethods, ORDINARY_INTERNAL_METHODS};
pub(crate) static ARRAY_EXOTIC_INTERNAL_METHODS: InternalObjectMethods = InternalObjectMethods {
__define_own_property__: array_exotic_define_own_property,
..ORDINARY_INTERNAL_METHODS
};
pub(crate) fn array_exotic_define_own_property(
obj: &JsObject,
key: PropertyKey,
desc: PropertyDescriptor,
context: &mut Context,
) -> JsResult<bool> {
match key {
PropertyKey::String(ref s) if s == "length" => {
let new_len_val = match desc.value() {
Some(value) => value,
_ => {
return super::ordinary_define_own_property(
obj,
"length".into(),
desc,
context,
);
}
};
let new_len = new_len_val.to_u32(context)?;
let number_len = new_len_val.to_number(context)?;
#[allow(clippy::float_cmp)]
if new_len as f64 != number_len {
return Err(context.construct_range_error("bad length for array"));
}
let mut new_len_desc = PropertyDescriptor::builder()
.value(new_len)
.maybe_writable(desc.writable())
.maybe_enumerable(desc.enumerable())
.maybe_configurable(desc.configurable());
let old_len_desc =
super::ordinary_get_own_property(obj, &"length".into(), context)?.unwrap();
debug_assert!(old_len_desc.is_data_descriptor());
debug_assert!(!old_len_desc.expect_configurable());
let old_len = old_len_desc.expect_value();
if new_len >= old_len.to_u32(context)? {
return super::ordinary_define_own_property(
obj,
"length".into(),
new_len_desc.build(),
context,
);
}
if !old_len_desc.expect_writable() {
return Ok(false);
}
let new_writable = if new_len_desc.inner().writable().unwrap_or(true) {
true
}
else {
new_len_desc = new_len_desc.writable(true);
false
};
if !super::ordinary_define_own_property(
obj,
"length".into(),
new_len_desc.clone().build(),
context,
)
.unwrap()
{
return Ok(false);
}
let ordered_keys = {
let mut keys: Vec<_> = obj
.borrow()
.properties
.index_property_keys()
.filter(|idx| new_len <= **idx && **idx < u32::MAX)
.copied()
.collect();
keys.sort_unstable_by(|x, y| y.cmp(x));
keys
};
for index in ordered_keys {
if !obj.__delete__(&index.into(), context)? {
new_len_desc = new_len_desc.value(index + 1);
if !new_writable {
new_len_desc = new_len_desc.writable(false);
}
super::ordinary_define_own_property(
obj,
"length".into(),
new_len_desc.build(),
context,
)
.unwrap();
return Ok(false);
}
}
if !new_writable {
let succeeded = super::ordinary_define_own_property(
obj,
"length".into(),
PropertyDescriptor::builder().writable(false).build(),
context,
)?;
debug_assert!(succeeded);
}
Ok(true)
}
PropertyKey::Index(index) if index < u32::MAX => {
let old_len_desc =
super::ordinary_get_own_property(obj, &"length".into(), context)?.unwrap();
debug_assert!(old_len_desc.is_data_descriptor());
debug_assert!(!old_len_desc.expect_configurable());
let old_len = old_len_desc.expect_value().to_u32(context).unwrap();
if index >= old_len && !old_len_desc.expect_writable() {
return Ok(false);
}
if super::ordinary_define_own_property(obj, key, desc, context)? {
if index >= old_len {
let old_len_desc = PropertyDescriptor::builder()
.value(index + 1)
.maybe_writable(old_len_desc.writable())
.maybe_enumerable(old_len_desc.enumerable())
.maybe_configurable(old_len_desc.configurable());
let succeeded = super::ordinary_define_own_property(
obj,
"length".into(),
old_len_desc.into(),
context,
)
.unwrap();
debug_assert!(succeeded);
}
Ok(true)
} else {
Ok(false)
}
}
_ => super::ordinary_define_own_property(obj, key, desc, context),
}
}