use std::collections::TryReserveError;
use ahash::AHashMap;
use crate::{
ecmascript::{
Agent, BUILTIN_STRING_MEMORY, InternalMethods, InternalSlots, Number, Object, ObjectRecord,
OrdinaryObject, Value,
},
engine::{Bindable, HeapRootData, NoGcScope, bindable_handle},
heap::{
CompactionLists, DirectArenaAccess, ElementDescriptor, HeapMarkAndSweep,
HeapSweepWeakReference, WellKnownSymbols, WorkQueues,
},
};
use super::{ObjectShape, ScopedArgumentsList};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct UnmappedArguments<'a>(OrdinaryObject<'a>);
pub(crate) fn create_unmapped_arguments_object<'a, 'b>(
agent: &mut Agent,
arguments_list: &ScopedArgumentsList<'b>,
gc: NoGcScope<'a, 'b>,
) -> Result<UnmappedArguments<'a>, TryReserveError> {
let len = arguments_list.len(agent);
let arguments_non_null_slice = unsafe { arguments_list.as_non_null_slice(agent) };
debug_assert!(len < u32::MAX as usize);
let len = len as u32;
let len_value = Number::from(len);
let prototype = agent.current_realm_record().intrinsics().object_prototype();
let mut shape = ObjectShape::get_shape_for_prototype(agent, Some(prototype.into()));
shape = shape.get_child_shape(agent, BUILTIN_STRING_MEMORY.length.to_property_key())?;
shape = shape.get_child_shape(agent, BUILTIN_STRING_MEMORY.callee.into())?;
shape = shape.get_child_shape(agent, WellKnownSymbols::Iterator.into())?;
for index in 0..len {
shape = shape.get_child_shape(agent, index.into())?;
}
let obj = OrdinaryObject::create_object_with_shape(agent, shape)
.expect("Failed to create Arguments object storage");
let array_prototype_values = agent
.current_realm_record()
.intrinsics()
.array_prototype_values()
.bind(gc);
let throw_type_error = agent
.current_realm_record()
.intrinsics()
.throw_type_error()
.bind(gc);
let storage = obj.get_elements_storage_mut(agent);
let values = storage.values;
let descriptors = storage.descriptors.or_insert(AHashMap::with_capacity(3));
values[0] = Some(len_value.unbind().into());
values[1] = None;
values[2] = Some(array_prototype_values.unbind().into());
descriptors.insert(0, ElementDescriptor::WritableUnenumerableConfigurableData);
descriptors.insert(
1,
ElementDescriptor::ReadWriteUnenumerableUnconfigurableAccessor {
get: throw_type_error.unbind().into(),
set: throw_type_error.unbind().into(),
},
);
descriptors.insert(2, ElementDescriptor::WritableUnenumerableConfigurableData);
for index in 0..len {
let val = unsafe { arguments_non_null_slice.as_ref() }
.get(index as usize)
.cloned()
.unwrap_or(Value::Undefined);
values[index as usize + 3] = Some(val);
}
Ok(UnmappedArguments(obj))
}
impl<'a> InternalSlots<'a> for UnmappedArguments<'a> {
#[inline(always)]
fn get_backing_object(self, _: &Agent) -> Option<OrdinaryObject<'static>> {
Some(self.0.unbind())
}
#[inline(always)]
fn set_backing_object(self, _agent: &mut Agent, _backing_object: OrdinaryObject<'static>) {
unreachable!();
}
#[inline(always)]
fn create_backing_object(self, _: &mut Agent) -> OrdinaryObject<'static> {
unreachable!();
}
#[inline(always)]
fn object_shape(self, agent: &mut Agent) -> ObjectShape<'static> {
self.0.object_shape(agent)
}
#[inline(always)]
fn internal_extensible(self, agent: &Agent) -> bool {
self.0.internal_extensible(agent)
}
#[inline(always)]
fn internal_set_extensible(self, agent: &mut Agent, value: bool) {
self.0.internal_set_extensible(agent, value);
}
#[inline(always)]
fn internal_prototype(self, agent: &Agent) -> Option<Object<'static>> {
self.0.internal_prototype(agent)
}
#[inline(always)]
fn internal_set_prototype(self, agent: &mut Agent, prototype: Option<Object>) {
self.0.internal_set_prototype(agent, prototype);
}
}
impl<'a> InternalMethods<'a> for UnmappedArguments<'a> {}
impl HeapMarkAndSweep for UnmappedArguments<'static> {
#[inline(always)]
fn mark_values(&self, queues: &mut WorkQueues) {
self.0.mark_values(queues);
}
#[inline(always)]
fn sweep_values(&mut self, compactions: &CompactionLists) {
self.0.sweep_values(compactions);
}
}
impl HeapSweepWeakReference for UnmappedArguments<'static> {
fn sweep_weak_reference(self, compactions: &CompactionLists) -> Option<Self> {
self.0.sweep_weak_reference(compactions).map(Self)
}
}
bindable_handle!(UnmappedArguments);
impl crate::heap::HeapIndexHandle for UnmappedArguments<'_> {
const _DEF: Self = Self(OrdinaryObject::_DEF);
#[inline]
fn from_index_u32(index: u32) -> Self {
Self(OrdinaryObject::from_index_u32(index))
}
#[inline]
fn get_index_u32(self) -> u32 {
self.0.get_index_u32()
}
}
impl<'a> From<UnmappedArguments<'a>> for HeapRootData {
#[inline(always)]
fn from(value: UnmappedArguments<'a>) -> Self {
Self::Arguments(value.unbind())
}
}
impl TryFrom<HeapRootData> for UnmappedArguments<'_> {
type Error = ();
#[inline]
fn try_from(value: HeapRootData) -> Result<Self, Self::Error> {
match value {
HeapRootData::Arguments(data) => Ok(data),
_ => Err(()),
}
}
}
impl<'a> From<UnmappedArguments<'a>> for Value<'a> {
#[inline(always)]
fn from(value: UnmappedArguments<'a>) -> Self {
Self::Arguments(value)
}
}
impl<'a> TryFrom<Value<'a>> for UnmappedArguments<'a> {
type Error = ();
#[inline]
fn try_from(value: Value<'a>) -> Result<Self, Self::Error> {
match value {
Value::Arguments(data) => Ok(data),
_ => Err(()),
}
}
}
impl<'a> From<UnmappedArguments<'a>> for Object<'a> {
#[inline(always)]
fn from(value: UnmappedArguments<'a>) -> Self {
Self::Arguments(value)
}
}
impl<'a> TryFrom<Object<'a>> for UnmappedArguments<'a> {
type Error = ();
#[inline]
fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
match value {
Object::Arguments(data) => Ok(data),
_ => Err(()),
}
}
}
impl<'a> DirectArenaAccess for UnmappedArguments<'a> {
type Data = ObjectRecord<'static>;
type Output = ObjectRecord<'a>;
#[inline]
fn get_direct(self, source: &Vec<Self::Data>) -> &Self::Output {
source
.get(crate::heap::HeapIndexHandle::get_index(self))
.unwrap_or_else(|| panic!("Invalid handle {:?}", self))
}
}
impl<'a> crate::heap::DirectArenaAccessMut for UnmappedArguments<'a> {
#[inline]
fn get_direct_mut(self, source: &mut Vec<Self::Data>) -> &mut Self::Output {
unsafe {
core::mem::transmute::<&mut ObjectRecord<'static>, &mut ObjectRecord<'a>>(
source
.get_mut(crate::heap::HeapIndexHandle::get_index(self))
.unwrap_or_else(|| panic!("Invalid handle {:?}", self)),
)
}
}
}