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