nova_vm 1.0.0

Nova Virtual Machine
Documentation
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

mod data;

pub(crate) use data::*;

use hashbrown::HashTable;
use soavec::SoAVec;

use crate::{
    ecmascript::{
        Agent, InternalMethods, InternalSlots, OrdinaryObject, ProtoIntrinsics, Value,
        object_handle,
    },
    engine::Bindable,
    heap::{
        ArenaAccessSoA, ArenaAccessSoAMut, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep,
        HeapSweepWeakReference, PrimitiveHeapAccess, WorkQueues, arena_vec_access,
        {BaseIndex, HeapIndexHandle},
    },
};

/// ## [24.1 Map Objects](https://tc39.es/ecma262/#sec-map-objects)
///
/// Maps are collections of key/value pairs where both the keys and values may
/// be arbitrary ECMAScript language values. A distinct key value may only occur
/// in one key/value pair within the Map's collection. Distinct key values are
/// discriminated using the semantics of the SameValueZero comparison algorithm.
///
/// Maps must be implemented using either hash tables or other mechanisms that,
/// on average, provide access times that are sublinear on the number of
/// elements in the collection.
///
/// ### Example
///
/// ```javascript
/// new Map()
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Map<'a>(BaseIndex<'a, MapHeapData<'static>>);
object_handle!(Map);
arena_vec_access!(
    soa:
    Map,
    'a,
    MapHeapData,
    maps,
    MapHeapDataRef,
    MapHeapDataMut
);

impl<'gc> Map<'gc> {
    pub(crate) fn len(self, agent: &mut Agent) -> u32 {
        self.get(agent).size()
    }

    pub(crate) fn entries_len(self, agent: &Agent) -> u32 {
        self.get(agent).entries_len()
    }

    pub(crate) fn clear(self, agent: &mut Agent) {
        self.get_mut(agent).clear();
    }

    pub(crate) fn get_entries(
        self,
        agent: &Agent,
    ) -> (&[Option<Value<'gc>>], &[Option<Value<'gc>>]) {
        let data = self.get(agent);
        (data.keys, data.values)
    }

    pub(crate) fn get_map_data<'soa>(
        self,
        maps: &'soa mut SoAVec<MapHeapData<'static>>,
        arena: &impl PrimitiveHeapAccess,
    ) -> (
        &'soa HashTable<u32>,
        &'soa [Option<Value<'gc>>],
        &'soa [Option<Value<'gc>>],
    ) {
        let mut data = self.get_mut(maps);
        data.rehash_if_needed_mut(arena);
        let MapHeapDataMut {
            map_data,
            keys,
            values,
            ..
        } = data;
        (map_data.get_mut(), keys, values)
    }

    pub(crate) fn get_map_data_mut<'soa>(
        self,
        maps: &'soa mut SoAVec<MapHeapData<'static>>,
        arena: &impl PrimitiveHeapAccess,
    ) -> (
        &'soa mut HashTable<u32>,
        &'soa mut Vec<Option<Value<'static>>>,
        &'soa mut Vec<Option<Value<'static>>>,
    ) {
        let mut data = self.get_mut(maps);
        data.rehash_if_needed_mut(arena);
        let MapHeapDataMut {
            map_data,
            keys,
            values,
            ..
        } = data;
        (map_data.get_mut(), keys, values)
    }
}

impl<'a> InternalSlots<'a> for Map<'a> {
    const DEFAULT_PROTOTYPE: ProtoIntrinsics = ProtoIntrinsics::Map;

    #[inline(always)]
    fn get_backing_object(self, agent: &Agent) -> Option<OrdinaryObject<'static>> {
        self.get(agent).object_index.unbind()
    }

    fn set_backing_object(self, agent: &mut Agent, backing_object: OrdinaryObject<'static>) {
        assert!(
            self.get_mut(agent)
                .object_index
                .replace(backing_object.unbind())
                .is_none()
        );
    }
}

impl<'a> InternalMethods<'a> for Map<'a> {}

impl HeapMarkAndSweep for Map<'static> {
    fn mark_values(&self, queues: &mut WorkQueues) {
        queues.maps.push(*self);
    }

    fn sweep_values(&mut self, compactions: &CompactionLists) {
        compactions.maps.shift_index(&mut self.0);
    }
}

impl HeapSweepWeakReference for Map<'static> {
    fn sweep_weak_reference(self, compactions: &CompactionLists) -> Option<Self> {
        compactions.maps.shift_weak_index(self.0).map(Self)
    }
}

impl<'a> CreateHeapData<MapHeapData<'a>, Map<'a>> for Heap {
    fn create(&mut self, data: MapHeapData<'a>) -> Map<'a> {
        let i = self.maps.len();
        self.maps
            .push(data.unbind())
            .expect("Failed to allocate Map");
        self.alloc_counter += core::mem::size_of::<MapHeapData<'static>>();
        Map(BaseIndex::from_index_u32(i))
    }
}