rune/runtime/unit/
storage.rs

1use core::fmt;
2use core::iter;
3use core::mem::size_of;
4use core::slice;
5
6use crate as rune;
7use crate::alloc::prelude::*;
8use crate::alloc::{self, Vec};
9
10#[cfg(feature = "byte-code")]
11use musli::storage;
12use serde::{Deserialize, Serialize};
13
14use crate::runtime::Inst;
15
16mod sealed {
17    pub trait Sealed {}
18
19    #[cfg(feature = "byte-code")]
20    impl Sealed for crate::runtime::unit::ByteCodeUnit {}
21    impl Sealed for crate::runtime::unit::ArrayUnit {}
22}
23
24/// Builder trait for unit storage.
25pub trait UnitEncoder: self::sealed::Sealed {
26    /// Current offset in storage, which also corresponds to the instruction
27    /// pointer being built.
28    #[doc(hidden)]
29    fn offset(&self) -> usize;
30
31    /// Encode an instruction into the current storage.
32    #[doc(hidden)]
33    fn encode(&mut self, inst: Inst) -> Result<(), EncodeError>;
34
35    /// Indicate that the given number of offsets have been added.
36    #[doc(hidden)]
37    fn extend_offsets(&mut self, extra: usize) -> alloc::Result<usize>;
38
39    /// Mark that the given offset index is at the current offset.
40    #[doc(hidden)]
41    fn mark_offset(&mut self, index: usize);
42
43    /// Calculate label jump.
44    #[doc(hidden)]
45    fn label_jump(&self, base: usize, offset: usize, jump: usize) -> usize;
46}
47
48/// Instruction storage used by a [`Unit`][super::Unit].
49pub trait UnitStorage: self::sealed::Sealed + fmt::Debug + Default {
50    /// Iterator over instructions and their corresponding instruction offsets.
51    #[doc(hidden)]
52    type Iter<'this>: Iterator<Item = (usize, Inst)>
53    where
54        Self: 'this;
55
56    /// Size of unit storage. This can be seen as the instruction pointer which
57    /// is just beyond the last instruction.
58    #[doc(hidden)]
59    fn end(&self) -> usize;
60
61    /// Get the number of bytes which is used to store unit bytecode.
62    #[doc(hidden)]
63    fn bytes(&self) -> usize;
64
65    /// Iterate over all instructions.
66    #[doc(hidden)]
67    fn iter(&self) -> Self::Iter<'_>;
68
69    /// Get the instruction at the given instruction pointer.
70    #[doc(hidden)]
71    fn get(&self, ip: usize) -> Result<Option<(Inst, usize)>, BadInstruction>;
72
73    /// Translate the given jump offset.
74    #[doc(hidden)]
75    fn translate(&self, jump: usize) -> Result<usize, BadJump>;
76}
77
78/// Unit stored as array of instructions.
79#[derive(Debug, TryClone, Default, Serialize, Deserialize)]
80pub struct ArrayUnit {
81    instructions: Vec<Inst>,
82}
83
84impl UnitEncoder for ArrayUnit {
85    #[inline]
86    fn offset(&self) -> usize {
87        self.instructions.len()
88    }
89
90    #[inline]
91    fn encode(&mut self, inst: Inst) -> Result<(), EncodeError> {
92        self.instructions.try_push(inst)?;
93        Ok(())
94    }
95
96    #[inline]
97    fn extend_offsets(&mut self, _: usize) -> alloc::Result<usize> {
98        Ok(self.instructions.len())
99    }
100
101    #[inline]
102    fn mark_offset(&mut self, _: usize) {}
103
104    #[inline]
105    fn label_jump(&self, base: usize, offset: usize, _: usize) -> usize {
106        base.wrapping_add(offset)
107    }
108}
109
110impl UnitStorage for ArrayUnit {
111    type Iter<'this> = iter::Enumerate<iter::Copied<slice::Iter<'this, Inst>>>;
112
113    #[inline]
114    fn end(&self) -> usize {
115        self.instructions.len()
116    }
117
118    #[inline]
119    fn bytes(&self) -> usize {
120        self.instructions.len().wrapping_mul(size_of::<Inst>())
121    }
122
123    #[inline]
124    fn iter(&self) -> Self::Iter<'_> {
125        self.instructions.iter().copied().enumerate()
126    }
127
128    #[inline]
129    fn get(&self, ip: usize) -> Result<Option<(Inst, usize)>, BadInstruction> {
130        let Some(inst) = self.instructions.get(ip) else {
131            return Ok(None);
132        };
133
134        Ok(Some((*inst, 1)))
135    }
136
137    #[inline]
138    fn translate(&self, jump: usize) -> Result<usize, BadJump> {
139        Ok(jump)
140    }
141}
142
143/// Error indicating that encoding failed.
144#[derive(Debug)]
145#[doc(hidden)]
146pub struct EncodeError {
147    kind: EncodeErrorKind,
148}
149
150#[cfg(feature = "byte-code")]
151impl From<storage::Error> for EncodeError {
152    #[inline]
153    fn from(error: storage::Error) -> Self {
154        Self {
155            kind: EncodeErrorKind::StorageError { error },
156        }
157    }
158}
159
160impl From<alloc::Error> for EncodeError {
161    #[inline]
162    fn from(error: alloc::Error) -> Self {
163        Self {
164            kind: EncodeErrorKind::AllocError { error },
165        }
166    }
167}
168
169impl fmt::Display for EncodeError {
170    #[inline]
171    fn fmt(
172        &self,
173        #[cfg_attr(not(feature = "byte-code"), allow(unused))] f: &mut fmt::Formatter<'_>,
174    ) -> fmt::Result {
175        match &self.kind {
176            #[cfg(feature = "byte-code")]
177            EncodeErrorKind::StorageError { .. } => write!(f, "Storage error"),
178            EncodeErrorKind::AllocError { error } => error.fmt(f),
179        }
180    }
181}
182
183impl core::error::Error for EncodeError {
184    #[inline]
185    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
186        match &self.kind {
187            #[cfg(all(feature = "byte-code", feature = "std"))]
188            EncodeErrorKind::StorageError { error } => Some(error),
189            _ => None,
190        }
191    }
192}
193
194#[derive(Debug)]
195enum EncodeErrorKind {
196    #[cfg(feature = "byte-code")]
197    StorageError {
198        #[cfg_attr(not(feature = "std"), allow(dead_code))]
199        error: storage::Error,
200    },
201    AllocError {
202        error: alloc::Error,
203    },
204}
205
206/// Error indicating that a bad instruction was located at the given instruction
207/// pointer.
208#[derive(Debug)]
209#[cfg_attr(test, derive(PartialEq))]
210pub struct BadInstruction {
211    pub(crate) ip: usize,
212}
213
214impl fmt::Display for BadInstruction {
215    #[inline]
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        write!(f, "Bad instruction at instruction {}", self.ip)
218    }
219}
220
221impl core::error::Error for BadInstruction {}
222
223/// Error indicating that a bad instruction was located at the given instruction
224/// pointer.
225#[derive(Debug)]
226#[cfg_attr(test, derive(PartialEq))]
227pub struct BadJump {
228    pub(crate) jump: usize,
229}
230
231impl fmt::Display for BadJump {
232    #[inline]
233    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234        write!(f, "Bad jump index {}", self.jump)
235    }
236}
237
238impl core::error::Error for BadJump {}