boa_engine/object/shape/
mod.rs1pub(crate) mod property_table;
4mod root_shape;
5pub(crate) mod shared_shape;
6pub(crate) mod slot;
7pub(crate) mod unique_shape;
8
9pub use root_shape::RootShape;
10pub use shared_shape::SharedShape;
11pub(crate) use unique_shape::UniqueShape;
12
13use std::fmt::Debug;
14
15use boa_gc::{Finalize, Trace};
16
17use crate::property::PropertyKey;
18
19use self::{
20 shared_shape::{TransitionKey, WeakSharedShape},
21 slot::Slot,
22 unique_shape::WeakUniqueShape,
23};
24
25use super::JsPrototype;
26
27pub(crate) enum ChangeTransitionAction {
39 Nothing,
41
42 Remove,
44
45 Insert,
47}
48
49pub(crate) struct ChangeTransition<T> {
51 pub(crate) shape: T,
53
54 pub(crate) action: ChangeTransitionAction,
56}
57
58#[derive(Debug, Trace, Finalize, Clone)]
60enum Inner {
61 Unique(UniqueShape),
62 Shared(SharedShape),
63}
64
65#[derive(Debug, Trace, Finalize, Clone)]
67pub struct Shape {
68 inner: Inner,
69}
70
71impl Default for Shape {
72 #[inline]
73 fn default() -> Self {
74 UniqueShape::default().into()
75 }
76}
77
78impl Shape {
79 const TRANSITION_COUNT_MAX: u16 = 1024;
84
85 #[inline]
87 #[must_use]
88 pub const fn is_shared(&self) -> bool {
89 matches!(self.inner, Inner::Shared(_))
90 }
91
92 #[inline]
94 #[must_use]
95 pub const fn is_unique(&self) -> bool {
96 matches!(self.inner, Inner::Unique(_))
97 }
98
99 pub(crate) const fn as_unique(&self) -> Option<&UniqueShape> {
100 if let Inner::Unique(shape) = &self.inner {
101 return Some(shape);
102 }
103 None
104 }
105
106 pub(crate) fn insert_property_transition(&self, key: TransitionKey) -> Self {
110 match &self.inner {
111 Inner::Shared(shape) => {
112 let shape = shape.insert_property_transition(key);
113 if shape.transition_count() >= Self::TRANSITION_COUNT_MAX {
114 return shape.to_unique().into();
115 }
116 shape.into()
117 }
118 Inner::Unique(shape) => shape.insert_property_transition(key).into(),
119 }
120 }
121
122 pub(crate) fn change_attributes_transition(
127 &self,
128 key: TransitionKey,
129 ) -> ChangeTransition<Self> {
130 match &self.inner {
131 Inner::Shared(shape) => {
132 let change_transition = shape.change_attributes_transition(key);
133 let shape =
134 if change_transition.shape.transition_count() >= Self::TRANSITION_COUNT_MAX {
135 change_transition.shape.to_unique().into()
136 } else {
137 change_transition.shape.into()
138 };
139 ChangeTransition {
140 shape,
141 action: change_transition.action,
142 }
143 }
144 Inner::Unique(shape) => shape.change_attributes_transition(&key),
145 }
146 }
147
148 pub(crate) fn remove_property_transition(&self, key: &PropertyKey) -> Self {
152 match &self.inner {
153 Inner::Shared(shape) => {
154 let shape = shape.remove_property_transition(key);
155 if shape.transition_count() >= Self::TRANSITION_COUNT_MAX {
156 return shape.to_unique().into();
157 }
158 shape.into()
159 }
160 Inner::Unique(shape) => shape.remove_property_transition(key).into(),
161 }
162 }
163
164 pub(crate) fn change_prototype_transition(&self, prototype: JsPrototype) -> Self {
166 match &self.inner {
167 Inner::Shared(shape) => {
168 let shape = shape.change_prototype_transition(prototype);
169 if shape.transition_count() >= Self::TRANSITION_COUNT_MAX {
170 return shape.to_unique().into();
171 }
172 shape.into()
173 }
174 Inner::Unique(shape) => shape.change_prototype_transition(prototype).into(),
175 }
176 }
177
178 #[must_use]
180 pub fn prototype(&self) -> JsPrototype {
181 match &self.inner {
182 Inner::Shared(shape) => shape.prototype(),
183 Inner::Unique(shape) => shape.prototype(),
184 }
185 }
186
187 #[inline]
189 pub(crate) fn lookup(&self, key: &PropertyKey) -> Option<Slot> {
190 match &self.inner {
191 Inner::Shared(shape) => shape.lookup(key),
192 Inner::Unique(shape) => shape.lookup(key),
193 }
194 }
195
196 #[inline]
198 #[must_use]
199 pub fn keys(&self) -> Vec<PropertyKey> {
200 match &self.inner {
201 Inner::Shared(shape) => shape.keys(),
202 Inner::Unique(shape) => shape.keys(),
203 }
204 }
205
206 #[inline]
208 #[must_use]
209 pub fn to_addr_usize(&self) -> usize {
210 match &self.inner {
211 Inner::Shared(shape) => shape.to_addr_usize(),
212 Inner::Unique(shape) => shape.to_addr_usize(),
213 }
214 }
215}
216
217impl From<UniqueShape> for Shape {
218 fn from(shape: UniqueShape) -> Self {
219 Self {
220 inner: Inner::Unique(shape),
221 }
222 }
223}
224
225impl From<SharedShape> for Shape {
226 fn from(shape: SharedShape) -> Self {
227 Self {
228 inner: Inner::Shared(shape),
229 }
230 }
231}
232
233#[derive(Debug, Trace, Finalize, Clone, PartialEq)]
235pub(crate) enum WeakShape {
236 Unique(WeakUniqueShape),
237 Shared(WeakSharedShape),
238}
239
240impl WeakShape {
241 #[inline]
245 #[must_use]
246 pub(crate) fn to_addr_usize(&self) -> usize {
247 self.upgrade().as_ref().map_or(0, Shape::to_addr_usize)
248 }
249
250 #[inline]
254 #[must_use]
255 pub(crate) fn upgrade(&self) -> Option<Shape> {
256 match self {
257 WeakShape::Shared(shape) => Some(shape.upgrade()?.into()),
258 WeakShape::Unique(shape) => Some(shape.upgrade()?.into()),
259 }
260 }
261}
262
263impl From<&Shape> for WeakShape {
264 fn from(value: &Shape) -> Self {
265 match &value.inner {
266 Inner::Shared(shape) => WeakShape::Shared(shape.into()),
267 Inner::Unique(shape) => WeakShape::Unique(shape.into()),
268 }
269 }
270}