rune/compile/
pool.rs

1use core::cell::Cell;
2use core::fmt;
3
4use crate as rune;
5use crate::alloc::prelude::*;
6use crate::alloc::try_vec;
7use crate::alloc::{self, HashMap, Vec};
8#[cfg(feature = "emit")]
9use crate::compile::Location;
10use crate::compile::{self, Visibility};
11use crate::parse::{Parse, Parser};
12use crate::{Hash, Item, ItemBuf};
13
14/// The identifier of a module.
15#[derive(Default, TryClone, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16#[try_clone(copy)]
17#[repr(transparent)]
18pub(crate) struct ModId(u32);
19
20impl fmt::Display for ModId {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        self.0.fmt(f)
23    }
24}
25
26impl fmt::Debug for ModId {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        self.0.fmt(f)
29    }
30}
31
32/// The identifier of an item.
33#[derive(Default, TryClone, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34#[try_clone(copy)]
35#[repr(transparent)]
36pub struct ItemId(u32);
37
38impl ItemId {
39    /// The item corresponding to the root item.
40    pub(crate) const ROOT: ItemId = ItemId(0);
41}
42
43impl Parse for ItemId {
44    #[inline]
45    fn parse(_: &mut Parser<'_>) -> compile::Result<Self> {
46        Ok(ItemId::ROOT)
47    }
48}
49
50impl fmt::Debug for ItemId {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        self.0.fmt(f)
53    }
54}
55
56impl fmt::Display for ItemId {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        self.0.fmt(f)
59    }
60}
61
62/// Module, its item and its visibility.
63#[derive(Debug)]
64#[non_exhaustive]
65pub(crate) struct ModMeta {
66    /// The location of the module.
67    #[cfg(feature = "emit")]
68    pub(crate) location: Location,
69    /// The item of the module.
70    pub(crate) item: ItemId,
71    /// The visibility of the module.
72    pub(crate) visibility: Visibility,
73    /// The kind of the module.
74    pub(crate) parent: Option<ModId>,
75}
76
77impl ModMeta {
78    /// Test if the module recursively is public.
79    pub(crate) fn is_public(&self, pool: &Pool) -> bool {
80        let mut current = Some(self);
81
82        while let Some(m) = current.take() {
83            if !m.visibility.is_public() {
84                return false;
85            }
86
87            current = m.parent.map(|id| pool.module(id));
88        }
89
90        true
91    }
92}
93
94macro_rules! alloc_item {
95    ($self:expr, $item:expr) => {{
96        let item = $item;
97        let hash = Hash::type_hash(item);
98
99        match $self.hash_to_item.get(&hash) {
100            Some(id) => *id,
101            None => {
102                let id = ItemId(u32::try_from($self.items.len()).expect("ran out of item ids"));
103                let item = $item.try_to_owned()?;
104                $self.items.try_push(ItemStorage {
105                    hash,
106                    item,
107                    ids: Cell::new(0),
108                })?;
109                $self.hash_to_item.try_insert(hash, id)?;
110                id
111            }
112        }
113    }};
114}
115
116struct ItemStorage {
117    hash: Hash,
118    item: ItemBuf,
119    /// Anonymous ids allocated related to this item.
120    ids: Cell<usize>,
121}
122
123/// A pool of items.
124pub(crate) struct Pool {
125    modules: Vec<ModMeta>,
126    items: Vec<ItemStorage>,
127    item_to_mod: HashMap<ItemId, ModId>,
128    hash_to_item: HashMap<Hash, ItemId>,
129}
130
131impl Pool {
132    pub fn new() -> alloc::Result<Self> {
133        let root_hash: Hash = Hash::type_hash(Item::new());
134
135        Ok(Self {
136            modules: Vec::new(),
137            items: try_vec![ItemStorage {
138                hash: root_hash,
139                item: ItemBuf::new(),
140                ids: Cell::new(0),
141            }],
142            item_to_mod: HashMap::new(),
143            hash_to_item: HashMap::try_from_iter([(root_hash, ItemId(0))])?,
144        })
145    }
146
147    /// Get the next anonymous identifier associated with the current item.
148    pub(crate) fn next_id(&self, id: ItemId) -> usize {
149        let storage = self.item_storage(id);
150        let id = storage.ids.get();
151        storage.ids.set(id + 1);
152        id
153    }
154
155    /// Lookup an item by the given identifier.
156    pub(crate) fn item(&self, id: ItemId) -> &Item {
157        &self.item_storage(id).item
158    }
159
160    /// Look up the type hash of an item.
161    pub(crate) fn item_type_hash(&self, id: ItemId) -> Hash {
162        self.item_storage(id).hash
163    }
164
165    /// Lookup mod meta by the given identifier.
166    pub(crate) fn module(&self, ModId(id): ModId) -> &ModMeta {
167        let id = usize::try_from(id).expect("module id overflow");
168
169        match self.modules.get(id) {
170            Some(item) => item,
171            None => panic!("missing module by id {id}"),
172        }
173    }
174
175    /// Get the item associated with a module.
176    pub(crate) fn module_item(&self, id: ModId) -> &Item {
177        let id = self.module(id).item;
178        self.item(id)
179    }
180
181    /// Get the hash associated with a module item.
182    pub(crate) fn module_item_hash(&self, id: ModId) -> Hash {
183        let id = self.module(id).item;
184        self.item_type_hash(id)
185    }
186
187    /// Get by item id.
188    pub(crate) fn module_by_item(&self, id: ItemId) -> Option<&ModMeta> {
189        Some(self.module(*self.item_to_mod.get(&id)?))
190    }
191
192    /// Allocate or return an existing module identifier.
193    pub(crate) fn alloc_module(&mut self, item: ModMeta) -> alloc::Result<ModId> {
194        if let Some(id) = self.item_to_mod.get(&item.item) {
195            return Ok(*id);
196        }
197
198        let id = ModId(u32::try_from(self.modules.len()).expect("ran out of item ids"));
199        self.item_to_mod.try_insert(item.item, id)?;
200        self.modules.try_push(item)?;
201        Ok(id)
202    }
203
204    /// Allocate or return an existing item.
205    pub(crate) fn alloc_item<T>(&mut self, item: T) -> alloc::Result<ItemId>
206    where
207        T: AsRef<Item>,
208    {
209        Ok(alloc_item!(self, item.as_ref()))
210    }
211
212    /// Map a value into a new item.
213    pub(crate) fn try_map_alloc<M>(&mut self, id: ItemId, m: M) -> alloc::Result<Option<ItemId>>
214    where
215        M: FnOnce(&Item) -> Option<&Item>,
216    {
217        let Some(item) = m(self.item(id)) else {
218            return Ok(None);
219        };
220
221        Ok(Some(alloc_item!(self, item)))
222    }
223
224    fn item_storage(&self, ItemId(id): ItemId) -> &ItemStorage {
225        let id = usize::try_from(id).expect("item id overflow");
226
227        match self.items.get(id) {
228            Some(item) => item,
229            None => panic!("missing item by id {id}"),
230        }
231    }
232}