Skip to main content

boa_engine/object/
property_map.rs

1use super::{
2    JsPrototype, ObjectStorage, PropertyDescriptor, PropertyKey,
3    shape::{
4        ChangeTransitionAction, RootShape, Shape, UniqueShape,
5        property_table::PropertyTableInner,
6        shared_shape::TransitionKey,
7        slot::{Slot, SlotAttributes},
8    },
9};
10use crate::value::JsVariant;
11use crate::{JsValue, property::PropertyDescriptorBuilder};
12use boa_gc::{Finalize, Trace};
13use rustc_hash::FxHashMap;
14use std::{collections::hash_map, iter::FusedIterator};
15use thin_vec::ThinVec;
16
17/// This represents all the indexed properties.
18///
19/// The index properties can be stored in two storage methods:
20///
21/// ## Dense Storage
22///
23/// Dense storage holds a contiguous array of properties where the index in the array is the key of the property.
24/// These are known to be data descriptors with a value field, writable field set to `true`, configurable field set to `true`, enumerable field set to `true`.
25///
26/// Since we know the properties of the property descriptors (and they are all the same) we can omit it and just store only
27/// the value field and construct the data property descriptor on demand.
28///
29/// ## Sparse Storage
30///
31/// This storage is used as a backup if the element keys are not continuous or the property descriptors
32/// are not data descriptors with a value field, writable field set to `true`, configurable field set to `true`, enumerable field set to `true`.
33///
34/// This method uses more space, since we also have to store the property descriptors, not just the value.
35/// It is also slower because we need to do a hash lookup.
36#[derive(Debug, Trace, Finalize)]
37pub enum IndexedProperties {
38    /// Dense [`i32`] storage.
39    // SAFETY: Numeric elements contain no GC pointers and have empty finalizers.
40    DenseI32(#[unsafe_ignore_trace] ThinVec<i32>),
41
42    /// Dense [`f64`] storage.
43    // SAFETY: Numeric elements contain no GC pointers and have empty finalizers.
44    DenseF64(#[unsafe_ignore_trace] ThinVec<f64>),
45
46    /// Dense [`JsValue`] storage.
47    DenseElement(ThinVec<JsValue>),
48
49    /// Sparse storage that only keeps element values.
50    SparseElement(Box<FxHashMap<u32, JsValue>>),
51
52    /// Sparse storage that keeps full property descriptors.
53    SparseProperty(Box<FxHashMap<u32, PropertyDescriptor>>),
54}
55
56impl Default for IndexedProperties {
57    #[inline]
58    fn default() -> Self {
59        Self::DenseI32(ThinVec::new())
60    }
61}
62
63impl IndexedProperties {
64    pub(crate) fn from_dense_js_value(elements: ThinVec<JsValue>) -> Self {
65        if elements.is_empty() {
66            return Self::default();
67        }
68        Self::DenseElement(elements)
69    }
70
71    #[inline]
72    fn property_simple_value(property: &PropertyDescriptor) -> Option<&JsValue> {
73        if property.writable().unwrap_or(false)
74            && property.enumerable().unwrap_or(false)
75            && property.configurable().unwrap_or(false)
76        {
77            property.value()
78        } else {
79            None
80        }
81    }
82
83    /// Get a property descriptor if it exists.
84    fn get(&self, key: u32) -> Option<PropertyDescriptor> {
85        let value = match self {
86            Self::DenseI32(vec) => vec.get(key as usize).copied()?.into(),
87            Self::DenseF64(vec) => vec.get(key as usize).copied()?.into(),
88            Self::DenseElement(vec) => vec.get(key as usize)?.clone(),
89            Self::SparseElement(map) => map.get(&key)?.clone(),
90            Self::SparseProperty(map) => return map.get(&key).cloned(),
91        };
92
93        Some(
94            PropertyDescriptorBuilder::new()
95                .writable(true)
96                .enumerable(true)
97                .configurable(true)
98                .value(value)
99                .build(),
100        )
101    }
102
103    /// Helper function for converting from a dense storage type to sparse storage type.
104    fn convert_dense_to_sparse<T>(vec: &mut ThinVec<T>) -> FxHashMap<u32, PropertyDescriptor>
105    where
106        T: Into<JsValue>,
107    {
108        let data = std::mem::take(vec);
109
110        data.into_iter()
111            .enumerate()
112            .map(|(index, value)| {
113                (
114                    index as u32,
115                    PropertyDescriptorBuilder::new()
116                        .writable(true)
117                        .enumerable(true)
118                        .configurable(true)
119                        .value(value.into())
120                        .build(),
121                )
122            })
123            .collect()
124    }
125
126    fn convert_dense_to_sparse_values<T>(vec: &mut ThinVec<T>) -> FxHashMap<u32, JsValue>
127    where
128        T: Into<JsValue>,
129    {
130        let data = std::mem::take(vec);
131
132        data.into_iter()
133            .enumerate()
134            .map(|(index, value)| (index as u32, value.into()))
135            .collect()
136    }
137
138    fn convert_to_sparse_and_insert(&mut self, key: u32, property: PropertyDescriptor) -> bool {
139        let mut descriptors = match self {
140            Self::DenseI32(vec) => Self::convert_dense_to_sparse(vec),
141            Self::DenseF64(vec) => Self::convert_dense_to_sparse(vec),
142            Self::DenseElement(vec) => Self::convert_dense_to_sparse(vec),
143            Self::SparseElement(map) => map
144                .iter()
145                .map(|(&index, value)| {
146                    (
147                        index,
148                        PropertyDescriptorBuilder::new()
149                            .writable(true)
150                            .enumerable(true)
151                            .configurable(true)
152                            .value(value.clone())
153                            .build(),
154                    )
155                })
156                .collect(),
157            Self::SparseProperty(map) => {
158                return map.insert(key, property).is_some();
159            }
160        };
161
162        let replaced = descriptors.insert(key, property).is_some();
163        *self = Self::SparseProperty(Box::new(descriptors));
164        replaced
165    }
166
167    /// Inserts a property descriptor with the specified key.
168    fn insert(&mut self, key: u32, property: PropertyDescriptor) -> bool {
169        let Some(value) = Self::property_simple_value(&property) else {
170            return self.convert_to_sparse_and_insert(key, property);
171        };
172
173        match self {
174            // Fast Path: contiguous array access without creating holes.
175            Self::DenseI32(vec) if key <= vec.len() as u32 => {
176                let len = vec.len() as u32;
177
178                if let Some(val) = value.as_i32() {
179                    // If the key is pointing one past the last element, we push it!
180                    //
181                    // Since the previous key is the current key - 1. Meaning that the elements are continuous.
182                    if key == len {
183                        vec.push(val);
184                        return false;
185                    }
186
187                    // If the key points at an already taken index, set it.
188                    vec[key as usize] = val;
189                    return true;
190                }
191
192                if let Some(num) = value.as_number() {
193                    let mut vec = vec.iter().copied().map(f64::from).collect::<ThinVec<_>>();
194
195                    // If the key is pointing one past the last element, we push it!
196                    //
197                    // Since the previous key is the current key - 1. Meaning that the elements are continuous.
198                    if key == len {
199                        vec.push(num);
200                        *self = Self::DenseF64(vec);
201                        return false;
202                    }
203
204                    // If the key points at an already taken index, set it.
205                    vec[key as usize] = num;
206                    *self = Self::DenseF64(vec);
207                    return true;
208                }
209
210                let mut vec = vec
211                    .iter()
212                    .copied()
213                    .map(JsValue::from)
214                    .collect::<ThinVec<JsValue>>();
215
216                // If the key is pointing one past the last element, we push it!
217                //
218                // Since the previous key is the current key - 1. Meaning that the elements are continuous.
219                if key == len {
220                    vec.push(value.clone());
221                    *self = Self::DenseElement(vec);
222                    return false;
223                }
224
225                // If the key points at an already taken index, set it.
226                vec[key as usize] = value.clone();
227                *self = Self::DenseElement(vec);
228                true
229            }
230            Self::DenseF64(vec) if key <= vec.len() as u32 => {
231                let len = vec.len() as u32;
232
233                if let Some(num) = value.as_number() {
234                    // If the key is pointing one past the last element, we push it!
235                    //
236                    // Since the previous key is the current key - 1. Meaning that the elements are continuous.
237                    if key == len {
238                        vec.push(num);
239                        return false;
240                    }
241
242                    // If the key points at an already taken index, swap and return it.
243                    vec[key as usize] = num;
244                    return true;
245                }
246
247                let mut vec = vec
248                    .iter()
249                    .copied()
250                    .map(JsValue::from)
251                    .collect::<ThinVec<JsValue>>();
252
253                // If the key is pointing one past the last element, we push it!
254                //
255                // Since the previous key is the current key - 1. Meaning that the elements are continuous.
256                if key == len {
257                    vec.push(value.clone());
258                    *self = Self::DenseElement(vec);
259                    return false;
260                }
261
262                // If the key points at an already taken index, set it.
263                vec[key as usize] = value.clone();
264                *self = Self::DenseElement(vec);
265                true
266            }
267            Self::DenseElement(vec) if key <= vec.len() as u32 => {
268                let len = vec.len() as u32;
269
270                // If the key is pointing one past the last element, we push it!
271                //
272                // Since the previous key is the current key - 1. Meaning that the elements are continuous.
273                if key == len {
274                    vec.push(value.clone());
275                    return false;
276                }
277
278                // If the key points at an already taken index, set it.
279                vec[key as usize] = value.clone();
280                true
281            }
282            // Creating a hole: transition dense storage into sparse element storage.
283            Self::DenseI32(vec) => {
284                let mut values = Self::convert_dense_to_sparse_values(vec);
285                let replaced = values.insert(key, value.clone()).is_some();
286                *self = Self::SparseElement(Box::new(values));
287                replaced
288            }
289            Self::DenseF64(vec) => {
290                let mut values = Self::convert_dense_to_sparse_values(vec);
291                let replaced = values.insert(key, value.clone()).is_some();
292                *self = Self::SparseElement(Box::new(values));
293                replaced
294            }
295            Self::DenseElement(vec) => {
296                let mut values = Self::convert_dense_to_sparse_values(vec);
297                let replaced = values.insert(key, value.clone()).is_some();
298                *self = Self::SparseElement(Box::new(values));
299                replaced
300            }
301            Self::SparseElement(map) => map.insert(key, value.clone()).is_some(),
302            Self::SparseProperty(map) => {
303                let descriptor = PropertyDescriptorBuilder::new()
304                    .value(value.clone())
305                    .writable(true)
306                    .enumerable(true)
307                    .configurable(true)
308                    .build();
309                map.insert(key, descriptor).is_some()
310            }
311        }
312    }
313
314    fn convert_to_sparse_and_remove(&mut self, key: u32) -> bool {
315        let mut descriptors = match self {
316            IndexedProperties::DenseI32(vec) => Self::convert_dense_to_sparse_values(vec),
317            IndexedProperties::DenseF64(vec) => Self::convert_dense_to_sparse_values(vec),
318            IndexedProperties::DenseElement(vec) => Self::convert_dense_to_sparse_values(vec),
319            IndexedProperties::SparseProperty(map) => return map.remove(&key).is_some(),
320            IndexedProperties::SparseElement(map) => {
321                return map.remove(&key).is_some();
322            }
323        };
324
325        let removed = descriptors.remove(&key).is_some();
326        *self = Self::SparseElement(Box::new(descriptors));
327        removed
328    }
329
330    /// Removes a property descriptor with the specified key.
331    fn remove(&mut self, key: u32) -> bool {
332        match self {
333            // Fast Paths: contiguous storage.
334            //
335            // If the key is pointing at the last element, then we pop it.
336            Self::DenseI32(vec) if (key + 1) == vec.len() as u32 => {
337                vec.pop();
338                true
339            }
340            // If the key is out of range then don't do anything.
341            Self::DenseI32(vec) if key >= vec.len() as u32 => false,
342            Self::DenseF64(vec) if (key + 1) == vec.len() as u32 => {
343                vec.pop();
344                true
345            }
346            Self::DenseF64(vec) if key >= vec.len() as u32 => false,
347            Self::DenseElement(vec) if (key + 1) == vec.len() as u32 => {
348                vec.pop();
349                true
350            }
351            Self::DenseElement(vec) if key >= vec.len() as u32 => false,
352            // Slow Paths: non-contiguous storage.
353            Self::SparseElement(map) => map.remove(&key).is_some(),
354            Self::SparseProperty(map) => map.remove(&key).is_some(),
355            _ => self.convert_to_sparse_and_remove(key),
356        }
357    }
358
359    /// Check if we contain the key to a property descriptor.
360    fn contains_key(&self, key: u32) -> bool {
361        match self {
362            Self::DenseI32(vec) => (0..vec.len() as u32).contains(&key),
363            Self::DenseF64(vec) => (0..vec.len() as u32).contains(&key),
364            Self::DenseElement(vec) => (0..vec.len() as u32).contains(&key),
365            Self::SparseElement(map) => map.contains_key(&key),
366            Self::SparseProperty(map) => map.contains_key(&key),
367        }
368    }
369
370    /// Pushes a value to the end of the dense indexed properties.
371    ///
372    /// Returns `true` if the push succeeded (storage is dense), `false` if
373    /// the storage is sparse and the caller should fall back to the slow path.
374    ///
375    /// Handles type transitions: `DenseI32` → `DenseF64` → `DenseElement`.
376    pub(crate) fn push_dense(&mut self, value: &JsValue) -> bool {
377        match self {
378            Self::DenseI32(vec) => {
379                if let Some(i) = value.as_i32() {
380                    vec.push(i);
381                } else if let Some(n) = value.as_number() {
382                    let mut new_vec: ThinVec<f64> = vec.iter().copied().map(f64::from).collect();
383                    new_vec.push(n);
384                    *self = Self::DenseF64(new_vec);
385                } else {
386                    let mut new_vec: ThinVec<JsValue> =
387                        vec.iter().copied().map(JsValue::from).collect();
388                    new_vec.push(value.clone());
389                    *self = Self::DenseElement(new_vec);
390                }
391                true
392            }
393            Self::DenseF64(vec) => {
394                if let Some(n) = value.as_number() {
395                    vec.push(n);
396                } else {
397                    let mut new_vec: ThinVec<JsValue> =
398                        vec.iter().copied().map(JsValue::from).collect();
399                    new_vec.push(value.clone());
400                    *self = Self::DenseElement(new_vec);
401                }
402                true
403            }
404            Self::DenseElement(vec) => {
405                vec.push(value.clone());
406                true
407            }
408            Self::SparseElement(_) | Self::SparseProperty(_) => false,
409        }
410    }
411
412    /// The number of elements in dense storage, or `None` if the storage is sparse.
413    pub(crate) fn dense_len(&self) -> Option<usize> {
414        match self {
415            Self::DenseI32(vec) => Some(vec.len()),
416            Self::DenseF64(vec) => Some(vec.len()),
417            Self::DenseElement(vec) => Some(vec.len()),
418            Self::SparseElement(_) | Self::SparseProperty(_) => None,
419        }
420    }
421
422    /// Transform this array into a sparse array if it isn't so already.
423    pub(crate) fn transform_to_sparse(&mut self) {
424        match &*self {
425            Self::DenseI32(v) => {
426                *self = Self::SparseElement(Box::new(
427                    v.into_iter()
428                        .copied()
429                        .enumerate()
430                        .map(|(i, v)| (i as u32, JsValue::from(v)))
431                        .collect(),
432                ));
433            }
434            Self::DenseF64(v) => {
435                *self = Self::SparseElement(Box::new(
436                    v.into_iter()
437                        .copied()
438                        .enumerate()
439                        .map(|(i, v)| (i as u32, JsValue::from(v)))
440                        .collect(),
441                ));
442            }
443            Self::DenseElement(v) => {
444                *self = Self::SparseElement(Box::new(
445                    v.into_iter()
446                        .enumerate()
447                        .map(|(i, v)| (i as u32, v.clone()))
448                        .collect(),
449                ));
450            }
451            _ => {}
452        }
453    }
454
455    fn iter(&self) -> IndexProperties<'_> {
456        match self {
457            Self::DenseI32(vec) => IndexProperties::DenseI32(vec.iter().enumerate()),
458            Self::DenseF64(vec) => IndexProperties::DenseF64(vec.iter().enumerate()),
459            Self::DenseElement(vec) => IndexProperties::DenseElement(vec.iter().enumerate()),
460            Self::SparseElement(map) => IndexProperties::SparseElement(map.iter()),
461            Self::SparseProperty(map) => IndexProperties::SparseProperty(map.iter()),
462        }
463    }
464
465    fn keys(&self) -> IndexPropertyKeys<'_> {
466        match self {
467            Self::DenseI32(vec) => IndexPropertyKeys::Dense(0..vec.len() as u32),
468            Self::DenseF64(vec) => IndexPropertyKeys::Dense(0..vec.len() as u32),
469            Self::DenseElement(vec) => IndexPropertyKeys::Dense(0..vec.len() as u32),
470            Self::SparseElement(map) => IndexPropertyKeys::SparseElement(map.keys()),
471            Self::SparseProperty(map) => IndexPropertyKeys::SparseProperty(map.keys()),
472        }
473    }
474
475    fn values(&self) -> IndexPropertyValues<'_> {
476        match self {
477            Self::DenseI32(vec) => IndexPropertyValues::DenseI32(vec.iter()),
478            Self::DenseF64(vec) => IndexPropertyValues::DenseF64(vec.iter()),
479            Self::DenseElement(vec) => IndexPropertyValues::DenseElement(vec.iter()),
480            Self::SparseElement(map) => IndexPropertyValues::SparseElement(map.values()),
481            Self::SparseProperty(map) => IndexPropertyValues::SparseProperty(map.values()),
482        }
483    }
484}
485
486impl<'a> IntoIterator for &'a IndexedProperties {
487    type IntoIter = IndexProperties<'a>;
488    type Item = (u32, PropertyDescriptor);
489    fn into_iter(self) -> Self::IntoIter {
490        self.iter()
491    }
492}
493
494/// A [`PropertyMap`] contains all the properties of an object.
495///
496/// The property values are stored in different data structures based on keys.
497#[derive(Default, Debug, Trace, Finalize)]
498pub struct PropertyMap {
499    /// Properties stored with integers as keys.
500    pub(crate) indexed_properties: IndexedProperties,
501
502    pub(crate) shape: Shape,
503    pub(crate) storage: ObjectStorage,
504}
505
506impl PropertyMap {
507    /// Create a new [`PropertyMap`].
508    #[must_use]
509    #[inline]
510    pub fn new(shape: Shape, indexed_properties: IndexedProperties) -> Self {
511        Self {
512            indexed_properties,
513            shape,
514            storage: Vec::default(),
515        }
516    }
517
518    /// Construct a [`PropertyMap`] with the given prototype with a unique [`Shape`].
519    #[must_use]
520    #[inline]
521    pub fn from_prototype_unique_shape(prototype: JsPrototype) -> Self {
522        Self {
523            indexed_properties: IndexedProperties::default(),
524            shape: UniqueShape::new(prototype, PropertyTableInner::default()).into(),
525            storage: Vec::default(),
526        }
527    }
528
529    /// Construct a [`PropertyMap`] with the given prototype with a shared shape [`Shape`].
530    #[must_use]
531    #[inline]
532    pub fn from_prototype_with_shared_shape(
533        root_shape: &RootShape,
534        prototype: JsPrototype,
535    ) -> Self {
536        let shape = root_shape.shape().change_prototype_transition(prototype);
537        Self {
538            indexed_properties: IndexedProperties::default(),
539            shape: shape.into(),
540            storage: Vec::default(),
541        }
542    }
543
544    /// Get the property with the given key from the [`PropertyMap`].
545    #[must_use]
546    pub fn get(&self, key: &PropertyKey) -> Option<PropertyDescriptor> {
547        if let PropertyKey::Index(index) = key {
548            return self.indexed_properties.get(index.get());
549        }
550        if let Some(slot) = self.shape.lookup(key) {
551            return Some(self.get_storage(slot));
552        }
553
554        None
555    }
556
557    /// Get the property with the given key from the [`PropertyMap`].
558    #[must_use]
559    pub(crate) fn get_with_slot(
560        &self,
561        key: &PropertyKey,
562        out_slot: &mut Slot,
563    ) -> Option<PropertyDescriptor> {
564        if let PropertyKey::Index(index) = key {
565            return self.indexed_properties.get(index.get());
566        }
567        if let Some(slot) = self.shape.lookup(key) {
568            out_slot.index = slot.index;
569
570            // Remove all descriptor attributes, but keep inline caching bits.
571            out_slot.attributes = (out_slot.attributes & SlotAttributes::INLINE_CACHE_BITS)
572                | slot.attributes
573                | SlotAttributes::FOUND;
574            return Some(self.get_storage(slot));
575        }
576
577        None
578    }
579
580    /// Get the property with the given key from the [`PropertyMap`].
581    #[must_use]
582    pub(crate) fn get_storage(&self, Slot { index, attributes }: Slot) -> PropertyDescriptor {
583        let index = index as usize;
584        let mut builder = PropertyDescriptor::builder()
585            .configurable(attributes.contains(SlotAttributes::CONFIGURABLE))
586            .enumerable(attributes.contains(SlotAttributes::ENUMERABLE));
587        if attributes.is_accessor_descriptor() {
588            if attributes.has_get() {
589                builder = builder.get(self.storage[index].clone());
590            }
591            if attributes.has_set() {
592                builder = builder.set(self.storage[index + 1].clone());
593            }
594        } else {
595            builder = builder.writable(attributes.contains(SlotAttributes::WRITABLE));
596            builder = builder.value(self.storage[index].clone());
597        }
598        builder.build()
599    }
600
601    /// Insert the given property descriptor with the given key [`PropertyMap`].
602    pub fn insert(&mut self, key: &PropertyKey, property: PropertyDescriptor) -> bool {
603        let mut dummy_slot = Slot::new();
604        self.insert_with_slot(key, property, &mut dummy_slot)
605    }
606
607    /// Insert the given property descriptor with the given key [`PropertyMap`].
608    pub(crate) fn insert_with_slot(
609        &mut self,
610        key: &PropertyKey,
611        property: PropertyDescriptor,
612        out_slot: &mut Slot,
613    ) -> bool {
614        if let PropertyKey::Index(index) = key {
615            return self.indexed_properties.insert(index.get(), property);
616        }
617
618        let attributes = property.to_slot_attributes();
619
620        if let Some(slot) = self.shape.lookup(key) {
621            let index = slot.index as usize;
622
623            if slot.attributes != attributes {
624                let key = TransitionKey {
625                    property_key: key.clone(),
626                    attributes,
627                };
628                let transition = self.shape.change_attributes_transition(key);
629                self.shape = transition.shape;
630                match transition.action {
631                    ChangeTransitionAction::Nothing => {}
632                    ChangeTransitionAction::Remove => {
633                        self.storage.remove(slot.index as usize + 1);
634                    }
635                    ChangeTransitionAction::Insert => {
636                        // insert after index which is (index + 1).
637                        self.storage.insert(index, JsValue::undefined());
638                    }
639                }
640            }
641
642            if attributes.is_accessor_descriptor() {
643                if attributes.has_get() {
644                    self.storage[index] = property
645                        .get()
646                        .cloned()
647                        .map(JsValue::new)
648                        .unwrap_or_default();
649                }
650                if attributes.has_set() {
651                    self.storage[index + 1] = property
652                        .set()
653                        .cloned()
654                        .map(JsValue::new)
655                        .unwrap_or_default();
656                }
657            } else {
658                self.storage[index] = property.expect_value().clone();
659            }
660            out_slot.index = slot.index;
661            out_slot.attributes =
662                (out_slot.attributes & SlotAttributes::INLINE_CACHE_BITS) | attributes;
663            return true;
664        }
665
666        let transition_key = TransitionKey {
667            property_key: key.clone(),
668            attributes,
669        };
670        self.shape = self.shape.insert_property_transition(transition_key);
671
672        // Make Sure that if we are inserting, it has the correct slot index.
673        debug_assert_eq!(
674            self.shape.lookup(key),
675            Some(Slot {
676                index: self.storage.len() as u32,
677                attributes
678            })
679        );
680
681        out_slot.index = self.storage.len() as u32;
682        out_slot.attributes =
683            (out_slot.attributes & SlotAttributes::INLINE_CACHE_BITS) | attributes;
684
685        if attributes.is_accessor_descriptor() {
686            self.storage.push(
687                property
688                    .get()
689                    .cloned()
690                    .map(JsValue::new)
691                    .unwrap_or_default(),
692            );
693            self.storage.push(
694                property
695                    .set()
696                    .cloned()
697                    .map(JsValue::new)
698                    .unwrap_or_default(),
699            );
700        } else {
701            self.storage
702                .push(property.value().cloned().unwrap_or_default());
703        }
704
705        false
706    }
707
708    /// Remove the property with the given key from the [`PropertyMap`].
709    pub fn remove(&mut self, key: &PropertyKey) -> bool {
710        if let PropertyKey::Index(index) = key {
711            return self.indexed_properties.remove(index.get());
712        }
713        if let Some(slot) = self.shape.lookup(key) {
714            // shift all elements when removing.
715            if slot.attributes.is_accessor_descriptor() {
716                self.storage.remove(slot.index as usize + 1);
717            }
718            self.storage.remove(slot.index as usize);
719
720            self.shape = self.shape.remove_property_transition(key);
721            return true;
722        }
723
724        false
725    }
726
727    /// Overrides all the indexed properties, setting it to dense storage.
728    pub(crate) fn override_indexed_properties(&mut self, properties: ThinVec<JsValue>) {
729        self.indexed_properties = IndexedProperties::DenseElement(properties);
730    }
731
732    pub(crate) fn get_dense_property(&self, index: u32) -> Option<JsValue> {
733        let index = index as usize;
734        match &self.indexed_properties {
735            IndexedProperties::DenseI32(properties) => {
736                properties.get(index).copied().map(JsValue::from)
737            }
738            IndexedProperties::DenseF64(properties) => {
739                properties.get(index).copied().map(JsValue::from)
740            }
741            IndexedProperties::DenseElement(properties) => properties.get(index).cloned(),
742            IndexedProperties::SparseProperty(_) | IndexedProperties::SparseElement(_) => None,
743        }
744    }
745
746    pub(crate) fn set_dense_property(&mut self, index: u32, value: &JsValue) -> bool {
747        let index = index as usize;
748
749        match &mut self.indexed_properties {
750            IndexedProperties::DenseI32(properties) => {
751                let Some(element) = properties.get_mut(index) else {
752                    return false;
753                };
754
755                // If it can fit in a i32 and the truncated version is
756                // equal to the original then it is an integer.
757                let is_rational_integer = |n: f64| n.to_bits() == f64::from(n as i32).to_bits();
758
759                let value = match value.variant() {
760                    JsVariant::Integer32(n) => n,
761                    JsVariant::Float64(n) if is_rational_integer(n) => n as i32,
762                    JsVariant::Float64(value) => {
763                        let mut properties = properties
764                            .iter()
765                            .copied()
766                            .map(f64::from)
767                            .collect::<ThinVec<_>>();
768                        properties[index] = value;
769                        self.indexed_properties = IndexedProperties::DenseF64(properties);
770
771                        return true;
772                    }
773                    _ => {
774                        let mut properties = properties
775                            .iter()
776                            .copied()
777                            .map(JsValue::from)
778                            .collect::<ThinVec<_>>();
779                        properties[index] = value.clone();
780                        self.indexed_properties = IndexedProperties::DenseElement(properties);
781
782                        return true;
783                    }
784                };
785
786                *element = value;
787                true
788            }
789            IndexedProperties::DenseF64(properties) => {
790                let Some(element) = properties.get_mut(index) else {
791                    return false;
792                };
793
794                let Some(value) = value.as_number() else {
795                    let mut properties = properties
796                        .iter()
797                        .copied()
798                        .map(JsValue::from)
799                        .collect::<ThinVec<_>>();
800                    properties[index] = value.clone();
801                    self.indexed_properties = IndexedProperties::DenseElement(properties);
802                    return true;
803                };
804
805                *element = value;
806                true
807            }
808            IndexedProperties::DenseElement(properties) => {
809                let Some(element) = properties.get_mut(index) else {
810                    return false;
811                };
812                *element = value.clone();
813                true
814            }
815            IndexedProperties::SparseProperty(_) | IndexedProperties::SparseElement(_) => false,
816        }
817    }
818
819    /// Returns the vec of dense indexed properties if they exist.
820    pub(crate) fn to_dense_indexed_properties(&self) -> Option<ThinVec<JsValue>> {
821        match &self.indexed_properties {
822            IndexedProperties::DenseI32(properties) => {
823                Some(properties.iter().copied().map(JsValue::from).collect())
824            }
825            IndexedProperties::DenseF64(properties) => {
826                Some(properties.iter().copied().map(JsValue::from).collect())
827            }
828            IndexedProperties::DenseElement(properties) => Some(properties.clone()),
829            IndexedProperties::SparseProperty(_) | IndexedProperties::SparseElement(_) => None,
830        }
831    }
832
833    /// Appends dense indexed values in argument order without a temporary vector.
834    ///
835    /// Numeric storage is converted exactly as in `to_dense_indexed_properties`.
836    /// Sparse storage leaves the destination unchanged.
837    pub(crate) fn append_dense_indexed_properties(
838        &self,
839        out: &mut impl Extend<JsValue>,
840    ) -> Option<usize> {
841        match &self.indexed_properties {
842            IndexedProperties::DenseI32(properties) => {
843                out.extend(properties.iter().copied().map(JsValue::from));
844                Some(properties.len())
845            }
846            IndexedProperties::DenseF64(properties) => {
847                out.extend(properties.iter().copied().map(JsValue::from));
848                Some(properties.len())
849            }
850            IndexedProperties::DenseElement(properties) => {
851                out.extend(properties.iter().cloned());
852                Some(properties.len())
853            }
854            IndexedProperties::SparseProperty(_) | IndexedProperties::SparseElement(_) => None,
855        }
856    }
857
858    /// Returns the vec of dense indexed properties if they exist.
859    pub(crate) fn dense_indexed_properties_mut(&mut self) -> Option<&mut ThinVec<JsValue>> {
860        if let IndexedProperties::DenseElement(properties) = &mut self.indexed_properties {
861            Some(properties)
862        } else {
863            None
864        }
865    }
866
867    /// An iterator visiting all indexed key-value pairs in arbitrary order. The iterator element type is `(&'a u32, &'a Property)`.
868    ///
869    /// This iterator does not recurse down the prototype chain.
870    #[inline]
871    #[must_use]
872    pub fn index_properties(&self) -> IndexProperties<'_> {
873        self.indexed_properties.iter()
874    }
875
876    /// An iterator visiting all index keys in arbitrary order. The iterator element type is `&'a u32`.
877    ///
878    /// This iterator does not recurse down the prototype chain.
879    #[inline]
880    #[must_use]
881    pub fn index_property_keys(&self) -> IndexPropertyKeys<'_> {
882        self.indexed_properties.keys()
883    }
884
885    /// An iterator visiting all index values in arbitrary order. The iterator element type is `&'a Property`.
886    ///
887    /// This iterator does not recurse down the prototype chain.
888    #[inline]
889    #[must_use]
890    pub fn index_property_values(&self) -> IndexPropertyValues<'_> {
891        self.indexed_properties.values()
892    }
893
894    /// Returns `true` if the given key is contained in the [`PropertyMap`].
895    #[inline]
896    #[must_use]
897    pub fn contains_key(&self, key: &PropertyKey) -> bool {
898        if let PropertyKey::Index(index) = key {
899            return self.indexed_properties.contains_key(index.get());
900        }
901        if self.shape.lookup(key).is_some() {
902            return true;
903        }
904
905        false
906    }
907}
908
909/// An iterator over the indexed property entries of an `Object`.
910#[derive(Debug, Clone)]
911pub enum IndexProperties<'a> {
912    /// An iterator over dense i32, Vec backed indexed property entries of an `Object`.
913    DenseI32(std::iter::Enumerate<std::slice::Iter<'a, i32>>),
914
915    /// An iterator over dense f64, Vec backed indexed property entries of an `Object`.
916    DenseF64(std::iter::Enumerate<std::slice::Iter<'a, f64>>),
917
918    /// An iterator over dense, Vec backed indexed property entries of an `Object`.
919    DenseElement(std::iter::Enumerate<std::slice::Iter<'a, JsValue>>),
920
921    /// An iterator over sparse, `HashMap` backed indexed property entries storing raw values.
922    SparseElement(hash_map::Iter<'a, u32, JsValue>),
923
924    /// An iterator over sparse, `HashMap` backed indexed property entries storing descriptors.
925    SparseProperty(hash_map::Iter<'a, u32, PropertyDescriptor>),
926}
927
928impl Iterator for IndexProperties<'_> {
929    type Item = (u32, PropertyDescriptor);
930
931    fn next(&mut self) -> Option<Self::Item> {
932        let (index, value) = match self {
933            Self::DenseI32(vec) => vec
934                .next()
935                .map(|(index, value)| (index, JsValue::from(*value)))?,
936            Self::DenseF64(vec) => vec
937                .next()
938                .map(|(index, value)| (index, JsValue::from(*value)))?,
939            Self::DenseElement(vec) => vec.next().map(|(index, value)| (index, value.clone()))?,
940            Self::SparseProperty(map) => {
941                return map.next().map(|(index, value)| (*index, value.clone()));
942            }
943            Self::SparseElement(map) => map
944                .next()
945                .map(|(index, value)| (*index as usize, value.clone()))?,
946        };
947
948        Some((
949            index as u32,
950            PropertyDescriptorBuilder::new()
951                .writable(true)
952                .configurable(true)
953                .enumerable(true)
954                .value(value.clone())
955                .build(),
956        ))
957    }
958
959    #[inline]
960    fn size_hint(&self) -> (usize, Option<usize>) {
961        match self {
962            Self::DenseI32(vec) => vec.size_hint(),
963            Self::DenseF64(vec) => vec.size_hint(),
964            Self::DenseElement(vec) => vec.size_hint(),
965            Self::SparseProperty(map) => map.size_hint(),
966            Self::SparseElement(map) => map.size_hint(),
967        }
968    }
969}
970
971impl ExactSizeIterator for IndexProperties<'_> {
972    #[inline]
973    fn len(&self) -> usize {
974        match self {
975            Self::DenseI32(vec) => vec.len(),
976            Self::DenseF64(vec) => vec.len(),
977            Self::DenseElement(vec) => vec.len(),
978            Self::SparseProperty(map) => map.len(),
979            Self::SparseElement(map) => map.len(),
980        }
981    }
982}
983
984impl FusedIterator for IndexProperties<'_> {}
985
986/// An iterator over the index keys (`u32`) of an `Object`.
987#[derive(Debug, Clone)]
988pub enum IndexPropertyKeys<'a> {
989    /// An iterator over dense, Vec backed indexed property entries of an `Object`.
990    Dense(std::ops::Range<u32>),
991
992    /// An iterator over sparse, `HashMap` backed indexed property entries of an `Object`.
993    SparseProperty(hash_map::Keys<'a, u32, PropertyDescriptor>),
994
995    /// An iterator over sparse, `HashMap` backed indexed property entries storing values.
996    SparseElement(hash_map::Keys<'a, u32, JsValue>),
997}
998
999impl Iterator for IndexPropertyKeys<'_> {
1000    type Item = u32;
1001
1002    #[inline]
1003    fn next(&mut self) -> Option<Self::Item> {
1004        match self {
1005            Self::Dense(vec) => vec.next(),
1006            Self::SparseProperty(map) => map.next().copied(),
1007            Self::SparseElement(map) => map.next().copied(),
1008        }
1009    }
1010
1011    #[inline]
1012    fn size_hint(&self) -> (usize, Option<usize>) {
1013        match self {
1014            Self::Dense(vec) => vec.size_hint(),
1015            Self::SparseProperty(map) => map.size_hint(),
1016            Self::SparseElement(map) => map.size_hint(),
1017        }
1018    }
1019}
1020
1021impl ExactSizeIterator for IndexPropertyKeys<'_> {
1022    #[inline]
1023    fn len(&self) -> usize {
1024        match self {
1025            Self::Dense(vec) => vec.len(),
1026            Self::SparseProperty(map) => map.len(),
1027            Self::SparseElement(map) => map.len(),
1028        }
1029    }
1030}
1031
1032impl FusedIterator for IndexPropertyKeys<'_> {}
1033
1034/// An iterator over the index values (`Property`) of an `Object`.
1035#[derive(Debug, Clone)]
1036#[allow(variant_size_differences)]
1037pub enum IndexPropertyValues<'a> {
1038    /// An iterator over dense, Vec backed indexed property entries of an `Object`.
1039    DenseI32(std::slice::Iter<'a, i32>),
1040
1041    /// An iterator over dense, Vec backed indexed property entries of an `Object`.
1042    DenseF64(std::slice::Iter<'a, f64>),
1043
1044    /// An iterator over dense, Vec backed indexed property entries of an `Object`.
1045    DenseElement(std::slice::Iter<'a, JsValue>),
1046
1047    /// An iterator over sparse, `HashMap` backed indexed property entries of an `Object`.
1048    SparseProperty(hash_map::Values<'a, u32, PropertyDescriptor>),
1049
1050    /// An iterator over sparse, `HashMap` backed indexed property entries storing values.
1051    SparseElement(hash_map::Values<'a, u32, JsValue>),
1052}
1053
1054impl Iterator for IndexPropertyValues<'_> {
1055    type Item = PropertyDescriptor;
1056
1057    fn next(&mut self) -> Option<Self::Item> {
1058        let value = match self {
1059            Self::DenseI32(vec) => vec.next().copied()?.into(),
1060            Self::DenseF64(vec) => vec.next().copied()?.into(),
1061            Self::DenseElement(vec) => vec.next().cloned()?,
1062            Self::SparseProperty(map) => return map.next().cloned(),
1063            Self::SparseElement(map) => map.next().cloned()?,
1064        };
1065
1066        Some(
1067            PropertyDescriptorBuilder::new()
1068                .writable(true)
1069                .configurable(true)
1070                .enumerable(true)
1071                .value(value)
1072                .build(),
1073        )
1074    }
1075
1076    #[inline]
1077    fn size_hint(&self) -> (usize, Option<usize>) {
1078        match self {
1079            Self::DenseI32(vec) => vec.size_hint(),
1080            Self::DenseF64(vec) => vec.size_hint(),
1081            Self::DenseElement(vec) => vec.size_hint(),
1082            Self::SparseProperty(map) => map.size_hint(),
1083            Self::SparseElement(map) => map.size_hint(),
1084        }
1085    }
1086}
1087
1088impl ExactSizeIterator for IndexPropertyValues<'_> {
1089    #[inline]
1090    fn len(&self) -> usize {
1091        match self {
1092            Self::DenseI32(vec) => vec.len(),
1093            Self::DenseF64(vec) => vec.len(),
1094            Self::DenseElement(vec) => vec.len(),
1095            Self::SparseProperty(map) => map.len(),
1096            Self::SparseElement(map) => map.len(),
1097        }
1098    }
1099}