pub(crate) mod property_table;
mod root_shape;
pub(crate) mod shared_shape;
pub(crate) mod slot;
pub(crate) mod unique_shape;
pub use root_shape::RootShape;
pub use shared_shape::SharedShape;
pub(crate) use unique_shape::UniqueShape;
use std::fmt::Debug;
use boa_gc::{Finalize, Trace};
use crate::property::PropertyKey;
use self::{shared_shape::TransitionKey, slot::Slot};
use super::JsPrototype;
pub(crate) enum ChangeTransitionAction {
Nothing,
Remove,
Insert,
}
pub(crate) struct ChangeTransition<T> {
pub(crate) shape: T,
pub(crate) action: ChangeTransitionAction,
}
#[derive(Debug, Trace, Finalize, Clone)]
enum Inner {
Unique(UniqueShape),
Shared(SharedShape),
}
#[derive(Debug, Trace, Finalize, Clone)]
pub struct Shape {
inner: Inner,
}
impl Default for Shape {
#[inline]
fn default() -> Self {
UniqueShape::default().into()
}
}
impl Shape {
const TRANSITION_COUNT_MAX: u16 = 1024;
#[inline]
pub const fn is_shared(&self) -> bool {
matches!(self.inner, Inner::Shared(_))
}
#[inline]
pub const fn is_unique(&self) -> bool {
matches!(self.inner, Inner::Unique(_))
}
pub(crate) const fn as_unique(&self) -> Option<&UniqueShape> {
if let Inner::Unique(shape) = &self.inner {
return Some(shape);
}
None
}
pub(crate) fn insert_property_transition(&self, key: TransitionKey) -> Self {
match &self.inner {
Inner::Shared(shape) => {
let shape = shape.insert_property_transition(key);
if shape.transition_count() >= Self::TRANSITION_COUNT_MAX {
return shape.to_unique().into();
}
shape.into()
}
Inner::Unique(shape) => shape.insert_property_transition(key).into(),
}
}
pub(crate) fn change_attributes_transition(
&self,
key: TransitionKey,
) -> ChangeTransition<Self> {
match &self.inner {
Inner::Shared(shape) => {
let change_transition = shape.change_attributes_transition(key);
let shape =
if change_transition.shape.transition_count() >= Self::TRANSITION_COUNT_MAX {
change_transition.shape.to_unique().into()
} else {
change_transition.shape.into()
};
ChangeTransition {
shape,
action: change_transition.action,
}
}
Inner::Unique(shape) => shape.change_attributes_transition(&key),
}
}
pub(crate) fn remove_property_transition(&self, key: &PropertyKey) -> Self {
match &self.inner {
Inner::Shared(shape) => {
let shape = shape.remove_property_transition(key);
if shape.transition_count() >= Self::TRANSITION_COUNT_MAX {
return shape.to_unique().into();
}
shape.into()
}
Inner::Unique(shape) => shape.remove_property_transition(key).into(),
}
}
pub(crate) fn change_prototype_transition(&self, prototype: JsPrototype) -> Self {
match &self.inner {
Inner::Shared(shape) => {
let shape = shape.change_prototype_transition(prototype);
if shape.transition_count() >= Self::TRANSITION_COUNT_MAX {
return shape.to_unique().into();
}
shape.into()
}
Inner::Unique(shape) => shape.change_prototype_transition(prototype).into(),
}
}
pub fn prototype(&self) -> JsPrototype {
match &self.inner {
Inner::Shared(shape) => shape.prototype(),
Inner::Unique(shape) => shape.prototype(),
}
}
#[inline]
pub(crate) fn lookup(&self, key: &PropertyKey) -> Option<Slot> {
match &self.inner {
Inner::Shared(shape) => shape.lookup(key),
Inner::Unique(shape) => shape.lookup(key),
}
}
#[inline]
pub fn keys(&self) -> Vec<PropertyKey> {
match &self.inner {
Inner::Shared(shape) => shape.keys(),
Inner::Unique(shape) => shape.keys(),
}
}
#[inline]
pub fn to_addr_usize(&self) -> usize {
match &self.inner {
Inner::Shared(shape) => shape.to_addr_usize(),
Inner::Unique(shape) => shape.to_addr_usize(),
}
}
}
impl From<UniqueShape> for Shape {
fn from(shape: UniqueShape) -> Self {
Self {
inner: Inner::Unique(shape),
}
}
}
impl From<SharedShape> for Shape {
fn from(shape: SharedShape) -> Self {
Self {
inner: Inner::Shared(shape),
}
}
}