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    /// Transform this array into a sparse array if it isn't so already.
411    pub(crate) fn transform_to_sparse(&mut self) {
412        match &*self {
413            Self::DenseI32(v) => {
414                *self = Self::SparseElement(Box::new(
415                    v.into_iter()
416                        .copied()
417                        .enumerate()
418                        .map(|(i, v)| (i as u32, JsValue::from(v)))
419                        .collect(),
420                ));
421            }
422            Self::DenseF64(v) => {
423                *self = Self::SparseElement(Box::new(
424                    v.into_iter()
425                        .copied()
426                        .enumerate()
427                        .map(|(i, v)| (i as u32, JsValue::from(v)))
428                        .collect(),
429                ));
430            }
431            Self::DenseElement(v) => {
432                *self = Self::SparseElement(Box::new(
433                    v.into_iter()
434                        .enumerate()
435                        .map(|(i, v)| (i as u32, v.clone()))
436                        .collect(),
437                ));
438            }
439            _ => {}
440        }
441    }
442
443    fn iter(&self) -> IndexProperties<'_> {
444        match self {
445            Self::DenseI32(vec) => IndexProperties::DenseI32(vec.iter().enumerate()),
446            Self::DenseF64(vec) => IndexProperties::DenseF64(vec.iter().enumerate()),
447            Self::DenseElement(vec) => IndexProperties::DenseElement(vec.iter().enumerate()),
448            Self::SparseElement(map) => IndexProperties::SparseElement(map.iter()),
449            Self::SparseProperty(map) => IndexProperties::SparseProperty(map.iter()),
450        }
451    }
452
453    fn keys(&self) -> IndexPropertyKeys<'_> {
454        match self {
455            Self::DenseI32(vec) => IndexPropertyKeys::Dense(0..vec.len() as u32),
456            Self::DenseF64(vec) => IndexPropertyKeys::Dense(0..vec.len() as u32),
457            Self::DenseElement(vec) => IndexPropertyKeys::Dense(0..vec.len() as u32),
458            Self::SparseElement(map) => IndexPropertyKeys::SparseElement(map.keys()),
459            Self::SparseProperty(map) => IndexPropertyKeys::SparseProperty(map.keys()),
460        }
461    }
462
463    fn values(&self) -> IndexPropertyValues<'_> {
464        match self {
465            Self::DenseI32(vec) => IndexPropertyValues::DenseI32(vec.iter()),
466            Self::DenseF64(vec) => IndexPropertyValues::DenseF64(vec.iter()),
467            Self::DenseElement(vec) => IndexPropertyValues::DenseElement(vec.iter()),
468            Self::SparseElement(map) => IndexPropertyValues::SparseElement(map.values()),
469            Self::SparseProperty(map) => IndexPropertyValues::SparseProperty(map.values()),
470        }
471    }
472}
473
474impl<'a> IntoIterator for &'a IndexedProperties {
475    type IntoIter = IndexProperties<'a>;
476    type Item = (u32, PropertyDescriptor);
477    fn into_iter(self) -> Self::IntoIter {
478        self.iter()
479    }
480}
481
482/// A [`PropertyMap`] contains all the properties of an object.
483///
484/// The property values are stored in different data structures based on keys.
485#[derive(Default, Debug, Trace, Finalize)]
486pub struct PropertyMap {
487    /// Properties stored with integers as keys.
488    pub(crate) indexed_properties: IndexedProperties,
489
490    pub(crate) shape: Shape,
491    pub(crate) storage: ObjectStorage,
492}
493
494impl PropertyMap {
495    /// Create a new [`PropertyMap`].
496    #[must_use]
497    #[inline]
498    pub fn new(shape: Shape, indexed_properties: IndexedProperties) -> Self {
499        Self {
500            indexed_properties,
501            shape,
502            storage: Vec::default(),
503        }
504    }
505
506    /// Construct a [`PropertyMap`] with the given prototype with a unique [`Shape`].
507    #[must_use]
508    #[inline]
509    pub fn from_prototype_unique_shape(prototype: JsPrototype) -> Self {
510        Self {
511            indexed_properties: IndexedProperties::default(),
512            shape: UniqueShape::new(prototype, PropertyTableInner::default()).into(),
513            storage: Vec::default(),
514        }
515    }
516
517    /// Construct a [`PropertyMap`] with the given prototype with a shared shape [`Shape`].
518    #[must_use]
519    #[inline]
520    pub fn from_prototype_with_shared_shape(
521        root_shape: &RootShape,
522        prototype: JsPrototype,
523    ) -> Self {
524        let shape = root_shape.shape().change_prototype_transition(prototype);
525        Self {
526            indexed_properties: IndexedProperties::default(),
527            shape: shape.into(),
528            storage: Vec::default(),
529        }
530    }
531
532    /// Get the property with the given key from the [`PropertyMap`].
533    #[must_use]
534    pub fn get(&self, key: &PropertyKey) -> Option<PropertyDescriptor> {
535        if let PropertyKey::Index(index) = key {
536            return self.indexed_properties.get(index.get());
537        }
538        if let Some(slot) = self.shape.lookup(key) {
539            return Some(self.get_storage(slot));
540        }
541
542        None
543    }
544
545    /// Get the property with the given key from the [`PropertyMap`].
546    #[must_use]
547    pub(crate) fn get_with_slot(
548        &self,
549        key: &PropertyKey,
550        out_slot: &mut Slot,
551    ) -> Option<PropertyDescriptor> {
552        if let PropertyKey::Index(index) = key {
553            return self.indexed_properties.get(index.get());
554        }
555        if let Some(slot) = self.shape.lookup(key) {
556            out_slot.index = slot.index;
557
558            // Remove all descriptor attributes, but keep inline caching bits.
559            out_slot.attributes = (out_slot.attributes & SlotAttributes::INLINE_CACHE_BITS)
560                | slot.attributes
561                | SlotAttributes::FOUND;
562            return Some(self.get_storage(slot));
563        }
564
565        None
566    }
567
568    /// Get the property with the given key from the [`PropertyMap`].
569    #[must_use]
570    pub(crate) fn get_storage(&self, Slot { index, attributes }: Slot) -> PropertyDescriptor {
571        let index = index as usize;
572        let mut builder = PropertyDescriptor::builder()
573            .configurable(attributes.contains(SlotAttributes::CONFIGURABLE))
574            .enumerable(attributes.contains(SlotAttributes::ENUMERABLE));
575        if attributes.is_accessor_descriptor() {
576            if attributes.has_get() {
577                builder = builder.get(self.storage[index].clone());
578            }
579            if attributes.has_set() {
580                builder = builder.set(self.storage[index + 1].clone());
581            }
582        } else {
583            builder = builder.writable(attributes.contains(SlotAttributes::WRITABLE));
584            builder = builder.value(self.storage[index].clone());
585        }
586        builder.build()
587    }
588
589    /// Insert the given property descriptor with the given key [`PropertyMap`].
590    pub fn insert(&mut self, key: &PropertyKey, property: PropertyDescriptor) -> bool {
591        let mut dummy_slot = Slot::new();
592        self.insert_with_slot(key, property, &mut dummy_slot)
593    }
594
595    /// Insert the given property descriptor with the given key [`PropertyMap`].
596    pub(crate) fn insert_with_slot(
597        &mut self,
598        key: &PropertyKey,
599        property: PropertyDescriptor,
600        out_slot: &mut Slot,
601    ) -> bool {
602        if let PropertyKey::Index(index) = key {
603            return self.indexed_properties.insert(index.get(), property);
604        }
605
606        let attributes = property.to_slot_attributes();
607
608        if let Some(slot) = self.shape.lookup(key) {
609            let index = slot.index as usize;
610
611            if slot.attributes != attributes {
612                let key = TransitionKey {
613                    property_key: key.clone(),
614                    attributes,
615                };
616                let transition = self.shape.change_attributes_transition(key);
617                self.shape = transition.shape;
618                match transition.action {
619                    ChangeTransitionAction::Nothing => {}
620                    ChangeTransitionAction::Remove => {
621                        self.storage.remove(slot.index as usize + 1);
622                    }
623                    ChangeTransitionAction::Insert => {
624                        // insert after index which is (index + 1).
625                        self.storage.insert(index, JsValue::undefined());
626                    }
627                }
628            }
629
630            if attributes.is_accessor_descriptor() {
631                if attributes.has_get() {
632                    self.storage[index] = property
633                        .get()
634                        .cloned()
635                        .map(JsValue::new)
636                        .unwrap_or_default();
637                }
638                if attributes.has_set() {
639                    self.storage[index + 1] = property
640                        .set()
641                        .cloned()
642                        .map(JsValue::new)
643                        .unwrap_or_default();
644                }
645            } else {
646                self.storage[index] = property.expect_value().clone();
647            }
648            out_slot.index = slot.index;
649            out_slot.attributes =
650                (out_slot.attributes & SlotAttributes::INLINE_CACHE_BITS) | attributes;
651            return true;
652        }
653
654        let transition_key = TransitionKey {
655            property_key: key.clone(),
656            attributes,
657        };
658        self.shape = self.shape.insert_property_transition(transition_key);
659
660        // Make Sure that if we are inserting, it has the correct slot index.
661        debug_assert_eq!(
662            self.shape.lookup(key),
663            Some(Slot {
664                index: self.storage.len() as u32,
665                attributes
666            })
667        );
668
669        out_slot.index = self.storage.len() as u32;
670        out_slot.attributes =
671            (out_slot.attributes & SlotAttributes::INLINE_CACHE_BITS) | attributes;
672
673        if attributes.is_accessor_descriptor() {
674            self.storage.push(
675                property
676                    .get()
677                    .cloned()
678                    .map(JsValue::new)
679                    .unwrap_or_default(),
680            );
681            self.storage.push(
682                property
683                    .set()
684                    .cloned()
685                    .map(JsValue::new)
686                    .unwrap_or_default(),
687            );
688        } else {
689            self.storage
690                .push(property.value().cloned().unwrap_or_default());
691        }
692
693        false
694    }
695
696    /// Remove the property with the given key from the [`PropertyMap`].
697    pub fn remove(&mut self, key: &PropertyKey) -> bool {
698        if let PropertyKey::Index(index) = key {
699            return self.indexed_properties.remove(index.get());
700        }
701        if let Some(slot) = self.shape.lookup(key) {
702            // shift all elements when removing.
703            if slot.attributes.is_accessor_descriptor() {
704                self.storage.remove(slot.index as usize + 1);
705            }
706            self.storage.remove(slot.index as usize);
707
708            self.shape = self.shape.remove_property_transition(key);
709            return true;
710        }
711
712        false
713    }
714
715    /// Overrides all the indexed properties, setting it to dense storage.
716    pub(crate) fn override_indexed_properties(&mut self, properties: ThinVec<JsValue>) {
717        self.indexed_properties = IndexedProperties::DenseElement(properties);
718    }
719
720    pub(crate) fn get_dense_property(&self, index: u32) -> Option<JsValue> {
721        let index = index as usize;
722        match &self.indexed_properties {
723            IndexedProperties::DenseI32(properties) => {
724                properties.get(index).copied().map(JsValue::from)
725            }
726            IndexedProperties::DenseF64(properties) => {
727                properties.get(index).copied().map(JsValue::from)
728            }
729            IndexedProperties::DenseElement(properties) => properties.get(index).cloned(),
730            IndexedProperties::SparseProperty(_) | IndexedProperties::SparseElement(_) => None,
731        }
732    }
733
734    pub(crate) fn set_dense_property(&mut self, index: u32, value: &JsValue) -> bool {
735        let index = index as usize;
736
737        match &mut self.indexed_properties {
738            IndexedProperties::DenseI32(properties) => {
739                let Some(element) = properties.get_mut(index) else {
740                    return false;
741                };
742
743                // If it can fit in a i32 and the truncated version is
744                // equal to the original then it is an integer.
745                let is_rational_integer = |n: f64| n.to_bits() == f64::from(n as i32).to_bits();
746
747                let value = match value.variant() {
748                    JsVariant::Integer32(n) => n,
749                    JsVariant::Float64(n) if is_rational_integer(n) => n as i32,
750                    JsVariant::Float64(value) => {
751                        let mut properties = properties
752                            .iter()
753                            .copied()
754                            .map(f64::from)
755                            .collect::<ThinVec<_>>();
756                        properties[index] = value;
757                        self.indexed_properties = IndexedProperties::DenseF64(properties);
758
759                        return true;
760                    }
761                    _ => {
762                        let mut properties = properties
763                            .iter()
764                            .copied()
765                            .map(JsValue::from)
766                            .collect::<ThinVec<_>>();
767                        properties[index] = value.clone();
768                        self.indexed_properties = IndexedProperties::DenseElement(properties);
769
770                        return true;
771                    }
772                };
773
774                *element = value;
775                true
776            }
777            IndexedProperties::DenseF64(properties) => {
778                let Some(element) = properties.get_mut(index) else {
779                    return false;
780                };
781
782                let Some(value) = value.as_number() else {
783                    let mut properties = properties
784                        .iter()
785                        .copied()
786                        .map(JsValue::from)
787                        .collect::<ThinVec<_>>();
788                    properties[index] = value.clone();
789                    self.indexed_properties = IndexedProperties::DenseElement(properties);
790                    return true;
791                };
792
793                *element = value;
794                true
795            }
796            IndexedProperties::DenseElement(properties) => {
797                let Some(element) = properties.get_mut(index) else {
798                    return false;
799                };
800                *element = value.clone();
801                true
802            }
803            IndexedProperties::SparseProperty(_) | IndexedProperties::SparseElement(_) => false,
804        }
805    }
806
807    /// Returns the vec of dense indexed properties if they exist.
808    pub(crate) fn to_dense_indexed_properties(&self) -> Option<ThinVec<JsValue>> {
809        match &self.indexed_properties {
810            IndexedProperties::DenseI32(properties) => {
811                Some(properties.iter().copied().map(JsValue::from).collect())
812            }
813            IndexedProperties::DenseF64(properties) => {
814                Some(properties.iter().copied().map(JsValue::from).collect())
815            }
816            IndexedProperties::DenseElement(properties) => Some(properties.clone()),
817            IndexedProperties::SparseProperty(_) | IndexedProperties::SparseElement(_) => None,
818        }
819    }
820
821    /// Returns the vec of dense indexed properties if they exist.
822    pub(crate) fn dense_indexed_properties_mut(&mut self) -> Option<&mut ThinVec<JsValue>> {
823        if let IndexedProperties::DenseElement(properties) = &mut self.indexed_properties {
824            Some(properties)
825        } else {
826            None
827        }
828    }
829
830    /// An iterator visiting all indexed key-value pairs in arbitrary order. The iterator element type is `(&'a u32, &'a Property)`.
831    ///
832    /// This iterator does not recurse down the prototype chain.
833    #[inline]
834    #[must_use]
835    pub fn index_properties(&self) -> IndexProperties<'_> {
836        self.indexed_properties.iter()
837    }
838
839    /// An iterator visiting all index keys in arbitrary order. The iterator element type is `&'a u32`.
840    ///
841    /// This iterator does not recurse down the prototype chain.
842    #[inline]
843    #[must_use]
844    pub fn index_property_keys(&self) -> IndexPropertyKeys<'_> {
845        self.indexed_properties.keys()
846    }
847
848    /// An iterator visiting all index values in arbitrary order. The iterator element type is `&'a Property`.
849    ///
850    /// This iterator does not recurse down the prototype chain.
851    #[inline]
852    #[must_use]
853    pub fn index_property_values(&self) -> IndexPropertyValues<'_> {
854        self.indexed_properties.values()
855    }
856
857    /// Returns `true` if the given key is contained in the [`PropertyMap`].
858    #[inline]
859    #[must_use]
860    pub fn contains_key(&self, key: &PropertyKey) -> bool {
861        if let PropertyKey::Index(index) = key {
862            return self.indexed_properties.contains_key(index.get());
863        }
864        if self.shape.lookup(key).is_some() {
865            return true;
866        }
867
868        false
869    }
870}
871
872/// An iterator over the indexed property entries of an `Object`.
873#[derive(Debug, Clone)]
874pub enum IndexProperties<'a> {
875    /// An iterator over dense i32, Vec backed indexed property entries of an `Object`.
876    DenseI32(std::iter::Enumerate<std::slice::Iter<'a, i32>>),
877
878    /// An iterator over dense f64, Vec backed indexed property entries of an `Object`.
879    DenseF64(std::iter::Enumerate<std::slice::Iter<'a, f64>>),
880
881    /// An iterator over dense, Vec backed indexed property entries of an `Object`.
882    DenseElement(std::iter::Enumerate<std::slice::Iter<'a, JsValue>>),
883
884    /// An iterator over sparse, `HashMap` backed indexed property entries storing raw values.
885    SparseElement(hash_map::Iter<'a, u32, JsValue>),
886
887    /// An iterator over sparse, `HashMap` backed indexed property entries storing descriptors.
888    SparseProperty(hash_map::Iter<'a, u32, PropertyDescriptor>),
889}
890
891impl Iterator for IndexProperties<'_> {
892    type Item = (u32, PropertyDescriptor);
893
894    fn next(&mut self) -> Option<Self::Item> {
895        let (index, value) = match self {
896            Self::DenseI32(vec) => vec
897                .next()
898                .map(|(index, value)| (index, JsValue::from(*value)))?,
899            Self::DenseF64(vec) => vec
900                .next()
901                .map(|(index, value)| (index, JsValue::from(*value)))?,
902            Self::DenseElement(vec) => vec.next().map(|(index, value)| (index, value.clone()))?,
903            Self::SparseProperty(map) => {
904                return map.next().map(|(index, value)| (*index, value.clone()));
905            }
906            Self::SparseElement(map) => map
907                .next()
908                .map(|(index, value)| (*index as usize, value.clone()))?,
909        };
910
911        Some((
912            index as u32,
913            PropertyDescriptorBuilder::new()
914                .writable(true)
915                .configurable(true)
916                .enumerable(true)
917                .value(value.clone())
918                .build(),
919        ))
920    }
921
922    #[inline]
923    fn size_hint(&self) -> (usize, Option<usize>) {
924        match self {
925            Self::DenseI32(vec) => vec.size_hint(),
926            Self::DenseF64(vec) => vec.size_hint(),
927            Self::DenseElement(vec) => vec.size_hint(),
928            Self::SparseProperty(map) => map.size_hint(),
929            Self::SparseElement(map) => map.size_hint(),
930        }
931    }
932}
933
934impl ExactSizeIterator for IndexProperties<'_> {
935    #[inline]
936    fn len(&self) -> usize {
937        match self {
938            Self::DenseI32(vec) => vec.len(),
939            Self::DenseF64(vec) => vec.len(),
940            Self::DenseElement(vec) => vec.len(),
941            Self::SparseProperty(map) => map.len(),
942            Self::SparseElement(map) => map.len(),
943        }
944    }
945}
946
947impl FusedIterator for IndexProperties<'_> {}
948
949/// An iterator over the index keys (`u32`) of an `Object`.
950#[derive(Debug, Clone)]
951pub enum IndexPropertyKeys<'a> {
952    /// An iterator over dense, Vec backed indexed property entries of an `Object`.
953    Dense(std::ops::Range<u32>),
954
955    /// An iterator over sparse, `HashMap` backed indexed property entries of an `Object`.
956    SparseProperty(hash_map::Keys<'a, u32, PropertyDescriptor>),
957
958    /// An iterator over sparse, `HashMap` backed indexed property entries storing values.
959    SparseElement(hash_map::Keys<'a, u32, JsValue>),
960}
961
962impl Iterator for IndexPropertyKeys<'_> {
963    type Item = u32;
964
965    #[inline]
966    fn next(&mut self) -> Option<Self::Item> {
967        match self {
968            Self::Dense(vec) => vec.next(),
969            Self::SparseProperty(map) => map.next().copied(),
970            Self::SparseElement(map) => map.next().copied(),
971        }
972    }
973
974    #[inline]
975    fn size_hint(&self) -> (usize, Option<usize>) {
976        match self {
977            Self::Dense(vec) => vec.size_hint(),
978            Self::SparseProperty(map) => map.size_hint(),
979            Self::SparseElement(map) => map.size_hint(),
980        }
981    }
982}
983
984impl ExactSizeIterator for IndexPropertyKeys<'_> {
985    #[inline]
986    fn len(&self) -> usize {
987        match self {
988            Self::Dense(vec) => vec.len(),
989            Self::SparseProperty(map) => map.len(),
990            Self::SparseElement(map) => map.len(),
991        }
992    }
993}
994
995impl FusedIterator for IndexPropertyKeys<'_> {}
996
997/// An iterator over the index values (`Property`) of an `Object`.
998#[derive(Debug, Clone)]
999#[allow(variant_size_differences)]
1000pub enum IndexPropertyValues<'a> {
1001    /// An iterator over dense, Vec backed indexed property entries of an `Object`.
1002    DenseI32(std::slice::Iter<'a, i32>),
1003
1004    /// An iterator over dense, Vec backed indexed property entries of an `Object`.
1005    DenseF64(std::slice::Iter<'a, f64>),
1006
1007    /// An iterator over dense, Vec backed indexed property entries of an `Object`.
1008    DenseElement(std::slice::Iter<'a, JsValue>),
1009
1010    /// An iterator over sparse, `HashMap` backed indexed property entries of an `Object`.
1011    SparseProperty(hash_map::Values<'a, u32, PropertyDescriptor>),
1012
1013    /// An iterator over sparse, `HashMap` backed indexed property entries storing values.
1014    SparseElement(hash_map::Values<'a, u32, JsValue>),
1015}
1016
1017impl Iterator for IndexPropertyValues<'_> {
1018    type Item = PropertyDescriptor;
1019
1020    fn next(&mut self) -> Option<Self::Item> {
1021        let value = match self {
1022            Self::DenseI32(vec) => vec.next().copied()?.into(),
1023            Self::DenseF64(vec) => vec.next().copied()?.into(),
1024            Self::DenseElement(vec) => vec.next().cloned()?,
1025            Self::SparseProperty(map) => return map.next().cloned(),
1026            Self::SparseElement(map) => map.next().cloned()?,
1027        };
1028
1029        Some(
1030            PropertyDescriptorBuilder::new()
1031                .writable(true)
1032                .configurable(true)
1033                .enumerable(true)
1034                .value(value)
1035                .build(),
1036        )
1037    }
1038
1039    #[inline]
1040    fn size_hint(&self) -> (usize, Option<usize>) {
1041        match self {
1042            Self::DenseI32(vec) => vec.size_hint(),
1043            Self::DenseF64(vec) => vec.size_hint(),
1044            Self::DenseElement(vec) => vec.size_hint(),
1045            Self::SparseProperty(map) => map.size_hint(),
1046            Self::SparseElement(map) => map.size_hint(),
1047        }
1048    }
1049}
1050
1051impl ExactSizeIterator for IndexPropertyValues<'_> {
1052    #[inline]
1053    fn len(&self) -> usize {
1054        match self {
1055            Self::DenseI32(vec) => vec.len(),
1056            Self::DenseF64(vec) => vec.len(),
1057            Self::DenseElement(vec) => vec.len(),
1058            Self::SparseProperty(map) => map.len(),
1059            Self::SparseElement(map) => map.len(),
1060        }
1061    }
1062}