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::mode::Binary;
14#[cfg(feature = "musli")]
15use musli::{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(Encode, Decode))]
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(Encode, Decode))]
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}
93
94impl<S> Unit<S> {
95    /// Constructs a new unit from a pair of data and debug info.
96    #[inline]
97    pub fn from_parts(data: Logic<S>, debug: Option<DebugInfo>) -> alloc::Result<Self> {
98        Ok(Self {
99            logic: data,
100            debug: debug.map(Box::try_new).transpose()?,
101        })
102    }
103
104    /// Construct a new unit with the given content.
105    #[allow(clippy::too_many_arguments)]
106    #[inline]
107    pub(crate) fn new(
108        storage: S,
109        functions: hash::Map<UnitFn>,
110        static_strings: Vec<Arc<StaticString>>,
111        static_bytes: Vec<Vec<u8>>,
112        static_object_keys: Vec<Box<[String]>>,
113        drop_sets: Vec<Arc<[Address]>>,
114        rtti: hash::Map<Arc<Rtti>>,
115        debug: Option<Box<DebugInfo>>,
116        constants: hash::Map<ConstValue>,
117    ) -> Self {
118        Self {
119            logic: Logic {
120                storage,
121                functions,
122                static_strings,
123                static_bytes,
124                static_object_keys,
125                drop_sets,
126                rtti,
127                constants,
128            },
129            debug,
130        }
131    }
132
133    /// Access unit data.
134    #[inline]
135    pub fn logic(&self) -> &Logic<S> {
136        &self.logic
137    }
138
139    /// Access debug information for the given location if it is available.
140    #[inline]
141    pub fn debug_info(&self) -> Option<&DebugInfo> {
142        Some(&**self.debug.as_ref()?)
143    }
144
145    /// Get raw underlying instructions storage.
146    #[inline]
147    pub(crate) fn instructions(&self) -> &S {
148        &self.logic.storage
149    }
150
151    /// Iterate over all static strings in the unit.
152    #[cfg(feature = "cli")]
153    #[inline]
154    pub(crate) fn iter_static_strings(&self) -> impl Iterator<Item = &Arc<StaticString>> + '_ {
155        self.logic.static_strings.iter()
156    }
157
158    /// Iterate over all static bytes in the unit.
159    #[cfg(feature = "cli")]
160    #[inline]
161    pub(crate) fn iter_static_bytes(&self) -> impl Iterator<Item = &[u8]> + '_ {
162        self.logic.static_bytes.iter().map(|v| &**v)
163    }
164
165    /// Iterate over all available drop sets.
166    #[cfg(feature = "cli")]
167    #[inline]
168    pub(crate) fn iter_static_drop_sets(&self) -> impl Iterator<Item = &[Address]> + '_ {
169        self.logic.drop_sets.iter().map(|v| &**v)
170    }
171
172    /// Iterate over all constants in the unit.
173    #[cfg(feature = "cli")]
174    #[inline]
175    pub(crate) fn iter_constants(&self) -> impl Iterator<Item = (&Hash, &ConstValue)> + '_ {
176        self.logic.constants.iter()
177    }
178
179    /// Iterate over all static object keys in the unit.
180    #[cfg(feature = "cli")]
181    #[inline]
182    pub(crate) fn iter_static_object_keys(&self) -> impl Iterator<Item = (usize, &[String])> + '_ {
183        use core::iter;
184
185        let mut it = self.logic.static_object_keys.iter().enumerate();
186
187        iter::from_fn(move || {
188            let (n, s) = it.next()?;
189            Some((n, &s[..]))
190        })
191    }
192
193    /// Iterate over dynamic functions.
194    #[cfg(feature = "cli")]
195    #[inline]
196    pub(crate) fn iter_functions(&self) -> impl Iterator<Item = (Hash, &UnitFn)> + '_ {
197        self.logic.functions.iter().map(|(h, f)| (*h, f))
198    }
199
200    /// Lookup the static string by slot, if it exists.
201    #[inline]
202    pub(crate) fn lookup_string(&self, slot: usize) -> Option<&Arc<StaticString>> {
203        self.logic.static_strings.get(slot)
204    }
205
206    /// Lookup the static byte string by slot, if it exists.
207    #[inline]
208    pub(crate) fn lookup_bytes(&self, slot: usize) -> Option<&[u8]> {
209        Some(self.logic.static_bytes.get(slot)?)
210    }
211
212    /// Lookup the static object keys by slot, if it exists.
213    #[inline]
214    pub(crate) fn lookup_object_keys(&self, slot: usize) -> Option<&[String]> {
215        Some(self.logic.static_object_keys.get(slot)?)
216    }
217
218    #[inline]
219    pub(crate) fn lookup_drop_set(&self, set: usize) -> Option<&[Address]> {
220        Some(self.logic.drop_sets.get(set)?)
221    }
222
223    /// Lookup run-time information for the given type hash.
224    #[inline]
225    pub(crate) fn lookup_rtti(&self, hash: &Hash) -> Option<&Arc<Rtti>> {
226        self.logic.rtti.get(hash)
227    }
228
229    /// Lookup a function in the unit.
230    #[inline]
231    pub(crate) fn function(&self, hash: &Hash) -> Option<&UnitFn> {
232        self.logic.functions.get(hash)
233    }
234
235    /// Lookup a constant from the unit.
236    #[inline]
237    pub(crate) fn constant(&self, hash: &Hash) -> Option<&ConstValue> {
238        self.logic.constants.get(hash)
239    }
240}
241
242impl<S> Unit<S>
243where
244    S: UnitStorage,
245{
246    #[inline]
247    pub(crate) fn translate(&self, jump: usize) -> Result<usize, BadJump> {
248        self.logic.storage.translate(jump)
249    }
250
251    /// Get the instruction at the given instruction pointer.
252    #[inline]
253    pub(crate) fn instruction_at(
254        &self,
255        ip: usize,
256    ) -> Result<Option<(Inst, usize)>, BadInstruction> {
257        self.logic.storage.get(ip)
258    }
259
260    /// Iterate over all instructions in order.
261    #[cfg(feature = "emit")]
262    #[inline]
263    pub(crate) fn iter_instructions(&self) -> impl Iterator<Item = (usize, Inst)> + '_ {
264        self.logic.storage.iter()
265    }
266}
267
268/// The kind and necessary information on registered functions.
269#[derive(Debug, Clone, Copy)]
270#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
271#[cfg_attr(feature = "musli", derive(Encode, Decode))]
272#[non_exhaustive]
273pub(crate) enum UnitFn {
274    /// Instruction offset of a function inside of the unit.
275    Offset {
276        /// Offset of the registered function.
277        offset: usize,
278        /// The way the function is called.
279        call: Call,
280        /// The number of arguments the function takes.
281        args: usize,
282        /// If the offset is a closure, this indicates the number of captures in
283        /// the first argument.
284        captures: Option<usize>,
285    },
286    /// An empty constructor of the type identified by the given hash.
287    EmptyStruct {
288        /// The type hash of the empty.
289        hash: Hash,
290    },
291    /// A tuple constructor of the type identified by the given hash.
292    TupleStruct {
293        /// The type hash of the tuple.
294        hash: Hash,
295        /// The number of arguments the tuple takes.
296        args: usize,
297    },
298}
299
300impl TryClone for UnitFn {
301    #[inline]
302    fn try_clone(&self) -> alloc::Result<Self> {
303        Ok(*self)
304    }
305}
306
307impl fmt::Display for UnitFn {
308    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309        match self {
310            Self::Offset {
311                offset,
312                call,
313                args,
314                captures,
315            } => {
316                write!(
317                    f,
318                    "offset offset={offset}, call={call}, args={args}, captures={captures:?}"
319                )?;
320            }
321            Self::EmptyStruct { hash } => {
322                write!(f, "unit hash={hash}")?;
323            }
324            Self::TupleStruct { hash, args } => {
325                write!(f, "tuple hash={hash}, args={args}")?;
326            }
327        }
328
329        Ok(())
330    }
331}
332
333#[cfg(test)]
334static_assertions::assert_impl_all!(Unit: Send, Sync);