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