use std::{cell::RefCell, fmt::Debug};
use boa_gc::{Finalize, Gc, GcRefCell, Trace, WeakGc};
use crate::property::PropertyKey;
use super::{
ChangeTransition, ChangeTransitionAction, JsPrototype, Shape, Slot,
property_table::PropertyTableInner, shared_shape::TransitionKey,
};
#[derive(Default, Debug, Trace, Finalize)]
struct Inner {
#[unsafe_ignore_trace]
property_table: RefCell<PropertyTableInner>,
prototype: GcRefCell<JsPrototype>,
}
#[derive(Default, Debug, Clone, Trace, Finalize)]
pub(crate) struct UniqueShape {
inner: Gc<Inner>,
}
impl UniqueShape {
pub(crate) fn new(prototype: JsPrototype, property_table: PropertyTableInner) -> Self {
Self {
inner: Gc::new(Inner {
property_table: RefCell::new(property_table),
prototype: GcRefCell::new(prototype),
}),
}
}
pub(crate) fn override_internal(
&self,
property_table: PropertyTableInner,
prototype: JsPrototype,
) {
*self.inner.property_table.borrow_mut() = property_table;
*self.inner.prototype.borrow_mut() = prototype;
}
pub(crate) fn prototype(&self) -> JsPrototype {
self.inner.prototype.borrow().clone()
}
fn property_table(&self) -> &RefCell<PropertyTableInner> {
&self.inner.property_table
}
pub(crate) fn insert_property_transition(&self, key: TransitionKey) -> Self {
let mut property_table = self.property_table().borrow_mut();
property_table.insert(key.property_key, key.attributes);
self.clone()
}
pub(crate) fn remove_property_transition(&self, key: &PropertyKey) -> Self {
let mut property_table = self.property_table().borrow_mut();
let Some((index, _attributes)) = property_table.map.remove(key) else {
return self.clone();
};
let index = index as usize;
property_table.keys.remove(index);
let mut property_table = std::mem::take(&mut *property_table);
if index != property_table.keys.len() {
let mut previous_slot = property_table.keys.get(index.wrapping_sub(1)).map(|x| x.1);
for (index, (key, slot)) in property_table.keys.iter_mut().enumerate().skip(index) {
*slot = Slot::from_previous(previous_slot, slot.attributes);
let Some((map_index, map_slot)) = property_table.map.get_mut(key) else {
unreachable!("There should already be a property")
};
*map_index = index as u32;
*map_slot = *slot;
previous_slot = Some(*slot);
}
}
let prototype = self.inner.prototype.borrow_mut().take();
Self::new(prototype, property_table)
}
pub(crate) fn lookup(&self, key: &PropertyKey) -> Option<Slot> {
let property_table = self.property_table().borrow();
if let Some((_, slot)) = property_table.map.get(key) {
return Some(*slot);
}
None
}
pub(crate) fn change_attributes_transition(
&self,
key: &TransitionKey,
) -> ChangeTransition<Shape> {
let mut property_table = self.property_table().borrow_mut();
let Some((index, slot)) = property_table.map.get_mut(&key.property_key) else {
unreachable!("Attribute change can only happen on existing property")
};
let index = *index as usize;
if slot.attributes.width_match(key.attributes) {
slot.attributes = key.attributes;
property_table.keys[index].1.attributes = key.attributes;
return ChangeTransition {
shape: self.clone().into(),
action: ChangeTransitionAction::Nothing,
};
}
slot.attributes = key.attributes;
let mut previous_slot = *slot;
property_table.keys[index].1.attributes = key.attributes;
let action = if key.attributes.is_accessor_descriptor() {
ChangeTransitionAction::Insert
} else {
ChangeTransitionAction::Remove
};
let mut property_table = std::mem::take(&mut *property_table);
let next = index + 1;
for (key, slot) in property_table.keys.iter_mut().skip(next) {
*slot = Slot::from_previous(Some(previous_slot), slot.attributes);
let Some((_, map_slot)) = property_table.map.get_mut(key) else {
unreachable!("There should already be a property")
};
*map_slot = *slot;
previous_slot = *slot;
}
let prototype = self.inner.prototype.borrow_mut().take();
let shape = Self::new(prototype, property_table);
ChangeTransition {
shape: shape.into(),
action,
}
}
pub(crate) fn change_prototype_transition(&self, prototype: JsPrototype) -> Self {
let mut property_table = self.inner.property_table.borrow_mut();
let property_table = std::mem::take(&mut *property_table);
Self::new(prototype, property_table)
}
pub(crate) fn keys(&self) -> Vec<PropertyKey> {
self.property_table().borrow().keys()
}
pub(crate) fn to_addr_usize(&self) -> usize {
let ptr: *const _ = self.inner.as_ref();
ptr as usize
}
}
#[derive(Debug, Clone, Trace, Finalize, PartialEq)]
pub(crate) struct WeakUniqueShape {
inner: WeakGc<Inner>,
}
impl WeakUniqueShape {
#[inline]
#[must_use]
pub(crate) fn to_addr_usize(&self) -> usize {
self.inner.upgrade().map_or(0, |inner| {
let ptr: *const _ = inner.as_ref();
ptr as usize
})
}
#[inline]
#[must_use]
pub(crate) fn upgrade(&self) -> Option<UniqueShape> {
Some(UniqueShape {
inner: self.inner.upgrade()?,
})
}
}
impl From<&UniqueShape> for WeakUniqueShape {
fn from(value: &UniqueShape) -> Self {
WeakUniqueShape {
inner: WeakGc::new(&value.inner),
}
}
}