use crate::{
object::JsObject,
property::{PropertyDescriptor, PropertyKey},
Context, JsResult, JsValue,
};
use super::{InternalObjectMethods, ORDINARY_INTERNAL_METHODS};
pub(crate) static STRING_EXOTIC_INTERNAL_METHODS: InternalObjectMethods = InternalObjectMethods {
__get_own_property__: string_exotic_get_own_property,
__define_own_property__: string_exotic_define_own_property,
__own_property_keys__: string_exotic_own_property_keys,
..ORDINARY_INTERNAL_METHODS
};
#[inline]
pub(crate) fn string_exotic_get_own_property(
obj: &JsObject,
key: &PropertyKey,
context: &mut Context,
) -> JsResult<Option<PropertyDescriptor>> {
let desc = super::ordinary_get_own_property(obj, key, context)?;
if desc.is_some() {
Ok(desc)
} else {
Ok(string_get_own_property(obj, key))
}
}
#[inline]
pub(crate) fn string_exotic_define_own_property(
obj: &JsObject,
key: PropertyKey,
desc: PropertyDescriptor,
context: &mut Context,
) -> JsResult<bool> {
let string_desc = string_get_own_property(obj, &key);
if let Some(string_desc) = string_desc {
let extensible = obj.borrow().extensible;
Ok(super::is_compatible_property_descriptor(
extensible,
desc,
Some(string_desc),
))
} else {
super::ordinary_define_own_property(obj, key, desc, context)
}
}
#[inline]
#[allow(clippy::unnecessary_wraps)]
pub(crate) fn string_exotic_own_property_keys(
obj: &JsObject,
_context: &mut Context,
) -> JsResult<Vec<PropertyKey>> {
let obj = obj.borrow();
let string = obj
.as_string()
.expect("string exotic method should only be callable from string objects");
let len = string.encode_utf16().count();
let mut keys = Vec::with_capacity(len);
keys.extend((0..len).into_iter().map(Into::into));
let mut remaining_indices: Vec<_> = obj
.properties
.index_property_keys()
.filter(|idx| (*idx as usize) >= len)
.collect();
remaining_indices.sort_unstable();
keys.extend(remaining_indices.into_iter().map(Into::into));
keys.extend(
obj.properties
.string_property_keys()
.cloned()
.map(Into::into),
);
keys.extend(
obj.properties
.symbol_property_keys()
.cloned()
.map(Into::into),
);
Ok(keys)
}
#[allow(clippy::float_cmp)]
#[inline]
fn string_get_own_property(obj: &JsObject, key: &PropertyKey) -> Option<PropertyDescriptor> {
let pos = match key {
PropertyKey::Index(index) => *index as usize,
_ => return None,
};
let string = obj
.borrow()
.as_string()
.expect("string exotic method should only be callable from string objects");
let result_str = string
.encode_utf16()
.nth(pos)
.map(|c| JsValue::from(String::from_utf16_lossy(&[c])))?;
let desc = PropertyDescriptor::builder()
.value(result_str)
.writable(false)
.enumerable(true)
.configurable(false)
.build();
Some(desc)
}