Skip to main content

rune/runtime/
unit.rs

1//! A single execution unit in the rune virtual machine.
2//!
3//! A unit consists of a sequence of instructions, and lookaside tables for
4//! metadata like function locations.
5
6#[cfg(feature = "byte-code")]
7mod byte_code;
8mod storage;
9
10use core::fmt;
11
12#[cfg(feature = "musli")]
13use musli_core::mode::Binary;
14#[cfg(feature = "musli")]
15use musli_core::{Decode, Encode};
16#[cfg(feature = "serde")]
17use serde::de::DeserializeOwned;
18#[cfg(feature = "serde")]
19use serde::{Deserialize, Serialize};
20
21use crate as rune;
22use crate::alloc::prelude::*;
23use crate::alloc::{self, Box, String, Vec};
24use crate::hash;
25use crate::runtime::{Address, Call, ConstValue, DebugInfo, Inst, Rtti, StaticString};
26use crate::sync::Arc;
27use crate::Hash;
28
29pub use self::storage::{ArrayUnit, EncodeError, UnitEncoder, UnitStorage};
30pub(crate) use self::storage::{BadInstruction, BadJump};
31
32#[cfg(feature = "byte-code")]
33pub use self::byte_code::ByteCodeUnit;
34
35/// Default storage implementation to use.
36#[cfg(not(rune_byte_code))]
37pub type DefaultStorage = ArrayUnit;
38/// Default storage implementation to use.
39#[cfg(rune_byte_code)]
40pub type DefaultStorage = ByteCodeUnit;
41
42/// Instructions and debug info from a single compilation.
43///
44/// See [`rune::prepare`] for more.
45#[derive(Debug, TryClone, Default)]
46#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
47#[cfg_attr(feature = "serde", serde(bound = "S: Serialize + DeserializeOwned"))]
48#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
49#[cfg_attr(feature = "musli", musli(Binary, bound = {S: Encode<Binary>}, decode_bound<'de, A> = {S: Decode<'de, Binary, A>}))]
50#[try_clone(bound = {S: TryClone})]
51pub struct Unit<S = DefaultStorage> {
52    /// The information needed to execute the program.
53    #[cfg_attr(feature = "serde", serde(flatten))]
54    logic: Logic<S>,
55    /// Debug info if available for unit.
56    debug: Option<Box<DebugInfo>>,
57}
58
59assert_impl!(Unit<DefaultStorage>: Send + Sync);
60
61/// Instructions from a single source file.
62#[derive(Debug, TryClone, Default)]
63#[cfg_attr(
64    feature = "serde",
65    derive(Serialize, Deserialize),
66    serde(rename = "Unit")
67)]
68#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
69#[try_clone(bound = {S: TryClone})]
70pub struct Logic<S = DefaultStorage> {
71    /// Storage for the unit.
72    storage: S,
73    /// Where functions are located in the collection of instructions.
74    functions: hash::Map<UnitFn>,
75    /// Static strings.
76    static_strings: Vec<Arc<StaticString>>,
77    /// A static byte string.
78    static_bytes: Vec<Vec<u8>>,
79    /// Slots used for object keys.
80    ///
81    /// This is used when an object is used in a pattern match, to avoid having
82    /// to send the collection of keys to the virtual machine.
83    ///
84    /// All keys are sorted with the default string sort.
85    static_object_keys: Vec<Box<[String]>>,
86    /// Drop sets.
87    drop_sets: Vec<Arc<[Address]>>,
88    /// Runtime information for types.
89    rtti: hash::Map<Arc<Rtti>>,
90    /// Named constants
91    constants: hash::Map<ConstValue>,
92    /// Initializers for statics declared in this unit, indexed by slot.
93    ///
94    /// A slot without an initializer has to be assigned before it can be read.
95    globals: Vec<Option<ConstValue>>,
96    /// Maps the type hash of a static item to the slot it has been assigned.
97    globals_rev: hash::Map<usize>,
98}
99
100impl<S> Unit<S> {
101    /// Constructs a new unit from a pair of data and debug info.
102    #[inline]
103    pub fn from_parts(data: Logic<S>, debug: Option<DebugInfo>) -> alloc::Result<Self> {
104        Ok(Self {
105            logic: data,
106            debug: debug.map(Box::try_new).transpose()?,
107        })
108    }
109
110    /// Construct a new unit with the given content.
111    #[allow(clippy::too_many_arguments)]
112    #[inline]
113    pub(crate) fn new(
114        storage: S,
115        functions: hash::Map<UnitFn>,
116        static_strings: Vec<Arc<StaticString>>,
117        static_bytes: Vec<Vec<u8>>,
118        static_object_keys: Vec<Box<[String]>>,
119        drop_sets: Vec<Arc<[Address]>>,
120        rtti: hash::Map<Arc<Rtti>>,
121        debug: Option<Box<DebugInfo>>,
122        constants: hash::Map<ConstValue>,
123        globals: Vec<Option<ConstValue>>,
124        globals_rev: hash::Map<usize>,
125    ) -> Self {
126        Self {
127            logic: Logic {
128                storage,
129                functions,
130                static_strings,
131                static_bytes,
132                static_object_keys,
133                drop_sets,
134                rtti,
135                constants,
136                globals,
137                globals_rev,
138            },
139            debug,
140        }
141    }
142
143    /// Access unit data.
144    #[inline]
145    pub fn logic(&self) -> &Logic<S> {
146        &self.logic
147    }
148
149    /// Access debug information for the given location if it is available.
150    #[inline]
151    pub fn debug_info(&self) -> Option<&DebugInfo> {
152        Some(&**self.debug.as_ref()?)
153    }
154
155    /// Get raw underlying instructions storage.
156    #[inline]
157    pub(crate) fn instructions(&self) -> &S {
158        &self.logic.storage
159    }
160
161    /// Iterate over all static strings in the unit.
162    #[cfg(feature = "cli")]
163    #[inline]
164    pub(crate) fn iter_static_strings(&self) -> impl Iterator<Item = &Arc<StaticString>> + '_ {
165        self.logic.static_strings.iter()
166    }
167
168    /// Iterate over all static bytes in the unit.
169    #[cfg(feature = "cli")]
170    #[inline]
171    pub(crate) fn iter_static_bytes(&self) -> impl Iterator<Item = &[u8]> + '_ {
172        self.logic.static_bytes.iter().map(|v| &**v)
173    }
174
175    /// Iterate over all available drop sets.
176    #[cfg(feature = "cli")]
177    #[inline]
178    pub(crate) fn iter_static_drop_sets(&self) -> impl Iterator<Item = &[Address]> + '_ {
179        self.logic.drop_sets.iter().map(|v| &**v)
180    }
181
182    /// Iterate over all constants in the unit.
183    #[cfg(feature = "cli")]
184    #[inline]
185    pub(crate) fn iter_constants(&self) -> impl Iterator<Item = (&Hash, &ConstValue)> + '_ {
186        self.logic.constants.iter()
187    }
188
189    /// Iterate over all static object keys in the unit.
190    #[cfg(feature = "cli")]
191    #[inline]
192    pub(crate) fn iter_static_object_keys(&self) -> impl Iterator<Item = (usize, &[String])> + '_ {
193        use core::iter;
194
195        let mut it = self.logic.static_object_keys.iter().enumerate();
196
197        iter::from_fn(move || {
198            let (n, s) = it.next()?;
199            Some((n, &s[..]))
200        })
201    }
202
203    /// Iterate over dynamic functions.
204    #[cfg(feature = "cli")]
205    #[inline]
206    pub(crate) fn iter_functions(&self) -> impl Iterator<Item = (Hash, &UnitFn)> + '_ {
207        self.logic.functions.iter().map(|(h, f)| (*h, f))
208    }
209
210    /// Lookup the static string by slot, if it exists.
211    #[inline]
212    pub(crate) fn lookup_string(&self, slot: usize) -> Option<&Arc<StaticString>> {
213        self.logic.static_strings.get(slot)
214    }
215
216    /// Lookup the static byte string by slot, if it exists.
217    #[inline]
218    pub(crate) fn lookup_bytes(&self, slot: usize) -> Option<&[u8]> {
219        Some(self.logic.static_bytes.get(slot)?)
220    }
221
222    /// Lookup the static object keys by slot, if it exists.
223    #[inline]
224    pub(crate) fn lookup_object_keys(&self, slot: usize) -> Option<&[String]> {
225        Some(self.logic.static_object_keys.get(slot)?)
226    }
227
228    #[inline]
229    pub(crate) fn lookup_drop_set(&self, set: usize) -> Option<&[Address]> {
230        Some(self.logic.drop_sets.get(set)?)
231    }
232
233    /// Lookup run-time information for the given type hash.
234    #[inline]
235    pub(crate) fn lookup_rtti(&self, hash: &Hash) -> Option<&Arc<Rtti>> {
236        self.logic.rtti.get(hash)
237    }
238
239    /// Lookup a function in the unit.
240    #[inline]
241    pub(crate) fn function(&self, hash: &Hash) -> Option<&UnitFn> {
242        self.logic.functions.get(hash)
243    }
244
245    /// Lookup a constant from the unit.
246    #[inline]
247    pub(crate) fn constant(&self, hash: &Hash) -> Option<&ConstValue> {
248        self.logic.constants.get(hash)
249    }
250
251    /// The number of static slots declared in this unit.
252    ///
253    /// This is the size that a [`Globals`] storage constructed for this unit
254    /// will have.
255    ///
256    /// [`Globals`]: crate::runtime::Globals
257    #[inline]
258    pub fn globals_len(&self) -> usize {
259        self.logic.globals.len()
260    }
261
262    /// Lookup the slot assigned to the static item with the given type hash.
263    ///
264    /// This is how a caller addresses a static by item without having to know
265    /// its slot.
266    #[inline]
267    pub fn global_slot(&self, hash: &Hash) -> Option<usize> {
268        self.logic.globals_rev.get(hash).copied()
269    }
270
271    /// Lookup the initializer for the given static slot, if it has one.
272    #[inline]
273    pub(crate) fn global_init(&self, slot: usize) -> Option<&ConstValue> {
274        self.logic.globals.get(slot)?.as_ref()
275    }
276
277    /// Iterate over all static slots in the unit.
278    #[cfg(feature = "cli")]
279    #[inline]
280    pub(crate) fn iter_globals(&self) -> impl Iterator<Item = (usize, Option<&ConstValue>)> + '_ {
281        self.logic
282            .globals
283            .iter()
284            .enumerate()
285            .map(|(slot, init)| (slot, init.as_ref()))
286    }
287}
288
289impl<S> Unit<S>
290where
291    S: UnitStorage,
292{
293    #[inline]
294    pub(crate) fn translate(&self, jump: usize) -> Result<usize, BadJump> {
295        self.logic.storage.translate(jump)
296    }
297
298    /// Get the instruction at the given instruction pointer.
299    #[inline]
300    pub(crate) fn instruction_at(
301        &self,
302        ip: usize,
303    ) -> Result<Option<(Inst, usize)>, BadInstruction> {
304        self.logic.storage.get(ip)
305    }
306
307    /// Iterate over all instructions in order.
308    #[cfg(feature = "emit")]
309    #[inline]
310    pub(crate) fn iter_instructions(&self) -> impl Iterator<Item = (usize, Inst)> + '_ {
311        self.logic.storage.iter()
312    }
313}
314
315/// The kind and necessary information on registered functions.
316#[derive(Debug, Clone, Copy)]
317#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
318#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
319pub(crate) enum UnitFn {
320    /// Instruction offset of a function inside of the unit.
321    Offset {
322        /// Offset of the registered function.
323        offset: usize,
324        /// The way the function is called.
325        call: Call,
326        /// The number of arguments the function takes.
327        args: usize,
328        /// If the offset is a closure, this indicates the number of captures in
329        /// the first argument.
330        captures: Option<usize>,
331    },
332    /// An empty constructor of the type identified by the given hash.
333    EmptyStruct {
334        /// The type hash of the empty.
335        hash: Hash,
336    },
337    /// A tuple constructor of the type identified by the given hash.
338    TupleStruct {
339        /// The type hash of the tuple.
340        hash: Hash,
341        /// The number of arguments the tuple takes.
342        args: usize,
343    },
344}
345
346impl TryClone for UnitFn {
347    #[inline]
348    fn try_clone(&self) -> alloc::Result<Self> {
349        Ok(*self)
350    }
351}
352
353impl fmt::Display for UnitFn {
354    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355        match self {
356            Self::Offset {
357                offset,
358                call,
359                args,
360                captures,
361            } => {
362                write!(
363                    f,
364                    "offset offset={offset}, call={call}, args={args}, captures={captures:?}"
365                )?;
366            }
367            Self::EmptyStruct { hash } => {
368                write!(f, "unit hash={hash}")?;
369            }
370            Self::TupleStruct { hash, args } => {
371                write!(f, "tuple hash={hash}, args={args}")?;
372            }
373        }
374
375        Ok(())
376    }
377}
378
379#[cfg(test)]
380static_assertions::assert_impl_all!(Unit: Send, Sync);