runestick/
unit.rs

1//! A single execution unit in the runestick virtual machine.
2//!
3//! A unit consists of a sequence of instructions, and lookaside tables for
4//! metadata like function locations.
5
6use crate::collections::HashMap;
7use crate::{
8    Call, ConstValue, DebugInfo, Hash, Inst, Rtti, StaticString, VariantRtti, VmError, VmErrorKind,
9};
10use serde::{Deserialize, Serialize};
11use std::fmt;
12use std::sync::Arc;
13
14/// Instructions from a single source file.
15#[derive(Debug, Default, Serialize, Deserialize)]
16pub struct Unit {
17    /// The instructions contained in the source file.
18    instructions: Vec<Inst>,
19    /// Where functions are located in the collection of instructions.
20    functions: HashMap<Hash, UnitFn>,
21    /// A static string.
22    static_strings: Vec<Arc<StaticString>>,
23    /// A static byte string.
24    static_bytes: Vec<Vec<u8>>,
25    /// Slots used for object keys.
26    ///
27    /// This is used when an object is used in a pattern match, to avoid having
28    /// to send the collection of keys to the virtual machine.
29    ///
30    /// All keys are sorted with the default string sort.
31    static_object_keys: Vec<Box<[String]>>,
32    /// Runtime information for types.
33    rtti: HashMap<Hash, Arc<Rtti>>,
34    /// Runtime information for variants.
35    variant_rtti: HashMap<Hash, Arc<VariantRtti>>,
36    /// Debug info if available for unit.
37    debug: Option<Box<DebugInfo>>,
38    /// Named constants
39    constants: HashMap<Hash, ConstValue>,
40}
41
42impl Unit {
43    /// Construct a new unit with the given content.
44    #[allow(clippy::too_many_arguments)]
45    pub fn new(
46        instructions: Vec<Inst>,
47        functions: HashMap<Hash, UnitFn>,
48        static_strings: Vec<Arc<StaticString>>,
49        static_bytes: Vec<Vec<u8>>,
50        static_object_keys: Vec<Box<[String]>>,
51        rtti: HashMap<Hash, Arc<Rtti>>,
52        variant_rtti: HashMap<Hash, Arc<VariantRtti>>,
53        debug: Option<Box<DebugInfo>>,
54        constants: HashMap<Hash, ConstValue>,
55    ) -> Self {
56        Self {
57            instructions,
58            functions,
59            static_strings,
60            static_bytes,
61            static_object_keys,
62            rtti,
63            variant_rtti,
64            debug,
65            constants,
66        }
67    }
68
69    /// Access debug information for the given location if it is available.
70    pub fn debug_info(&self) -> Option<&DebugInfo> {
71        let debug = self.debug.as_ref()?;
72        Some(&**debug)
73    }
74
75    /// Get the instruction at the given instruction pointer.
76    pub fn instruction_at(&self, ip: usize) -> Option<&Inst> {
77        self.instructions.get(ip)
78    }
79
80    /// Iterate over all static strings in the unit.
81    pub fn iter_static_strings(&self) -> impl Iterator<Item = &Arc<StaticString>> + '_ {
82        self.static_strings.iter()
83    }
84
85    /// Iterate over all static object keys in the unit.
86    pub fn iter_static_object_keys(&self) -> impl Iterator<Item = (usize, &[String])> + '_ {
87        let mut it = self.static_object_keys.iter().enumerate();
88
89        std::iter::from_fn(move || {
90            let (n, s) = it.next()?;
91            Some((n, &s[..]))
92        })
93    }
94
95    /// Iterate over all instructions in order.
96    pub fn iter_instructions(&self) -> impl Iterator<Item = Inst> + '_ {
97        self.instructions.iter().copied()
98    }
99
100    /// Iterate over dynamic functions.
101    pub fn iter_functions(&self) -> impl Iterator<Item = (Hash, &UnitFn)> + '_ {
102        self.functions.iter().map(|(h, f)| (*h, f))
103    }
104
105    /// Lookup the static string by slot, if it exists.
106    pub fn lookup_string(&self, slot: usize) -> Result<&Arc<StaticString>, VmError> {
107        Ok(self
108            .static_strings
109            .get(slot)
110            .ok_or_else(|| VmErrorKind::MissingStaticString { slot })?)
111    }
112
113    /// Lookup the static byte string by slot, if it exists.
114    pub fn lookup_bytes(&self, slot: usize) -> Result<&[u8], VmError> {
115        Ok(self
116            .static_bytes
117            .get(slot)
118            .ok_or_else(|| VmErrorKind::MissingStaticString { slot })?
119            .as_ref())
120    }
121
122    /// Lookup the static object keys by slot, if it exists.
123    pub fn lookup_object_keys(&self, slot: usize) -> Option<&[String]> {
124        self.static_object_keys.get(slot).map(|keys| &keys[..])
125    }
126
127    /// Lookup runt-time information for the given type hash.
128    pub fn lookup_rtti(&self, hash: Hash) -> Option<&Arc<Rtti>> {
129        self.rtti.get(&hash)
130    }
131
132    /// Lookup variant runt-time information for the given variant hash.
133    pub fn lookup_variant_rtti(&self, hash: Hash) -> Option<&Arc<VariantRtti>> {
134        self.variant_rtti.get(&hash)
135    }
136
137    /// Lookup information of a function.
138    pub fn lookup(&self, hash: Hash) -> Option<UnitFn> {
139        self.functions.get(&hash).copied()
140    }
141
142    /// Read a constant value from the unit.
143    pub fn constant(&self, hash: Hash) -> Option<&ConstValue> {
144        self.constants.get(&hash)
145    }
146}
147
148/// The kind and necessary information on registered functions.
149#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
150pub enum UnitFn {
151    /// Offset to call a "real" function.
152    Offset {
153        /// Offset of the registered function.
154        offset: usize,
155        /// The way the function is called.
156        call: Call,
157        /// The number of arguments the function takes.
158        args: usize,
159    },
160    /// An empty constructor.
161    UnitStruct {
162        /// The type hash of the empty.
163        hash: Hash,
164    },
165    /// A tuple constructor.
166    TupleStruct {
167        /// The type hash of the tuple.
168        hash: Hash,
169        /// The number of arguments the tuple takes.
170        args: usize,
171    },
172    /// A empty variant constructor.
173    UnitVariant {
174        /// The type hash of the empty variant.
175        hash: Hash,
176    },
177    /// A tuple variant constructor.
178    TupleVariant {
179        /// The type hash of the variant.
180        hash: Hash,
181        /// The number of arguments the tuple takes.
182        args: usize,
183    },
184}
185
186impl fmt::Display for UnitFn {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        match self {
189            Self::Offset { offset, call, args } => {
190                write!(f, "offset {}, {}, {}", offset, call, args)?;
191            }
192            Self::UnitStruct { hash } => {
193                write!(f, "unit {}", hash)?;
194            }
195            Self::TupleStruct { hash, args } => {
196                write!(f, "tuple {}, {}", hash, args)?;
197            }
198            Self::UnitVariant { hash } => {
199                write!(f, "empty-variant {}", hash)?;
200            }
201            Self::TupleVariant { hash, args } => {
202                write!(f, "tuple-variant {}, {}", hash, args)?;
203            }
204        }
205
206        Ok(())
207    }
208}
209
210#[cfg(test)]
211static_assertions::assert_impl_all!(Unit: Send, Sync);