runestick/
vm_error.rs

1use crate::{panic::BoxedPanic, CallFrame};
2use crate::{
3    AccessError, Hash, Item, Key, Panic, Protocol, StackError, TypeInfo, TypeOf, Unit, Value,
4    VmHaltInfo,
5};
6use std::fmt;
7use std::sync::Arc;
8use thiserror::Error;
9
10/// Errors raised by the execution of the virtual machine.
11#[derive(Error, Debug)]
12#[error(transparent)]
13pub struct VmError {
14    kind: Box<VmErrorKind>,
15}
16
17impl VmError {
18    /// Return an error encapsulating a panic.
19    pub fn panic<D>(message: D) -> Self
20    where
21        D: BoxedPanic,
22    {
23        Self::from(VmErrorKind::Panic {
24            reason: Panic::custom(message),
25        })
26    }
27
28    /// Bad argument.
29    pub fn bad_argument<T>(arg: usize, value: &Value) -> Result<Self, VmError>
30    where
31        T: TypeOf,
32    {
33        Ok(Self::from(VmErrorKind::BadArgumentAt {
34            arg,
35            expected: T::type_info(),
36            actual: value.type_info()?,
37        }))
38    }
39
40    /// Construct an expected error.
41    pub fn expected<T>(actual: TypeInfo) -> Self
42    where
43        T: TypeOf,
44    {
45        Self::from(VmErrorKind::Expected {
46            expected: T::type_info(),
47            actual,
48        })
49    }
50
51    /// Construct an expected any error.
52    pub fn expected_any(actual: TypeInfo) -> Self {
53        Self::from(VmErrorKind::ExpectedAny { actual })
54    }
55
56    /// Access the underlying error kind.
57    pub fn kind(&self) -> &VmErrorKind {
58        &*self.kind
59    }
60
61    /// Access the underlying error kind while consuming the error.
62    pub fn into_kind(self) -> VmErrorKind {
63        *self.kind
64    }
65
66    /// Convert into an unwinded vm error.
67    pub fn into_unwinded(self, unit: &Arc<Unit>, ip: usize, frames: Vec<crate::CallFrame>) -> Self {
68        if let VmErrorKind::Unwound { .. } = &*self.kind {
69            return self;
70        }
71
72        Self::from(VmErrorKind::Unwound {
73            kind: self.kind,
74            unit: unit.clone(),
75            ip,
76            frames,
77        })
78    }
79
80    /// Unpack an unwinded error, if it is present.
81    pub fn as_unwound<'a>(
82        &'a self,
83    ) -> (
84        &'a VmErrorKind,
85        Option<(&'a Arc<Unit>, usize, Vec<CallFrame>)>,
86    ) {
87        match &*self.kind {
88            VmErrorKind::Unwound {
89                kind,
90                unit,
91                ip,
92                frames,
93            } => (&*kind, Some((unit, *ip, frames.clone()))),
94            kind => (kind, None),
95        }
96    }
97
98    /// Unpack an unwinded error, if it is present.
99    pub fn into_unwound(self) -> (Self, Option<(Arc<Unit>, usize, Vec<CallFrame>)>) {
100        match *self.kind {
101            VmErrorKind::Unwound {
102                kind,
103                unit,
104                ip,
105                frames,
106            } => {
107                let error = Self { kind };
108                (error, Some((unit, ip, frames)))
109            }
110            kind => (Self::from(kind), None),
111        }
112    }
113
114    /// Unsmuggles the vm error, returning Ok(Self) in case the error is
115    /// critical and should be propagated unaltered.
116    pub fn unpack_critical(self) -> Result<Self, Self> {
117        if self.is_critical() {
118            Err(self)
119        } else {
120            Ok(self)
121        }
122    }
123
124    /// Test if the error is critical and should be propagated unaltered or not.
125    ///
126    /// Returns `true` if the error should be propagated.
127    fn is_critical(&self) -> bool {
128        match &*self.kind {
129            VmErrorKind::Panic { .. } => true,
130            VmErrorKind::Unwound { .. } => true,
131            _ => false,
132        }
133    }
134}
135
136impl<E> From<E> for VmError
137where
138    VmErrorKind: From<E>,
139{
140    fn from(err: E) -> Self {
141        Self {
142            kind: Box::new(VmErrorKind::from(err)),
143        }
144    }
145}
146
147/// The kind of error encountered.
148#[allow(missing_docs)]
149#[derive(Debug, Error)]
150pub enum VmErrorKind {
151    /// A vm error that was propagated from somewhere else.
152    ///
153    /// In order to represent this, we need to preserve the instruction pointer
154    /// and eventually unit from where the error happened.
155    #[error("{kind} (at inst {ip})")]
156    Unwound {
157        /// The wrapper error.
158        kind: Box<VmErrorKind>,
159        /// Associated unit.
160        unit: Arc<Unit>,
161        /// The instruction pointer of where the original error happened.
162        ip: usize,
163        /// All lower call frames before the unwind trigger point
164        frames: Vec<CallFrame>,
165    },
166    #[error("{error}")]
167    AccessError {
168        #[from]
169        error: AccessError,
170    },
171    #[error("panicked: {reason}")]
172    Panic { reason: Panic },
173    #[error("no running virtual machines")]
174    NoRunningVm,
175    #[error("halted for unexpected reason `{halt}`")]
176    Halted { halt: VmHaltInfo },
177    #[error("failed to format argument")]
178    FormatError,
179    #[error("stack error: {error}")]
180    StackError {
181        #[from]
182        error: StackError,
183    },
184    #[error("numerical overflow")]
185    Overflow,
186    #[error("numerical underflow")]
187    Underflow,
188    #[error("division by zero")]
189    DivideByZero,
190    #[error("missing constant with hash `{hash}`")]
191    MissingConst { hash: Hash },
192    #[error("missing entry `{item}` with hash `{hash}`")]
193    MissingEntry { item: Item, hash: Hash },
194    #[error("missing function with hash `{hash}`")]
195    MissingFunction { hash: Hash },
196    #[error("missing instance function `{hash}` for `{instance}`")]
197    MissingInstanceFunction { hash: Hash, instance: TypeInfo },
198    #[error("instruction pointer is out-of-bounds")]
199    IpOutOfBounds,
200    #[error("unsupported vm operation `{lhs} {op} {rhs}`")]
201    UnsupportedBinaryOperation {
202        op: &'static str,
203        lhs: TypeInfo,
204        rhs: TypeInfo,
205    },
206    #[error("unsupported vm operation `{op}{operand}`")]
207    UnsupportedUnaryOperation { op: &'static str, operand: TypeInfo },
208    #[error("`{actual}` does not implement the `{protocol}` protocol")]
209    MissingProtocol {
210        protocol: Protocol,
211        actual: TypeInfo,
212    },
213    #[error("static string slot `{slot}` does not exist")]
214    MissingStaticString { slot: usize },
215    #[error("static object keys slot `{slot}` does not exist")]
216    MissingStaticObjectKeys { slot: usize },
217    #[error("missing runtime information for variant with hash `{hash}`")]
218    MissingVariantRtti { hash: Hash },
219    #[error("missing runtime information for type with hash `{hash}`")]
220    MissingRtti { hash: Hash },
221    #[error("wrong number of arguments `{actual}`, expected `{expected}`")]
222    BadArgumentCount { actual: usize, expected: usize },
223    #[error("bad argument #{arg}, expected `{expected}` but got `{actual}`")]
224    BadArgumentAt {
225        arg: usize,
226        expected: TypeInfo,
227        actual: TypeInfo,
228    },
229    #[error("bad argument #{arg}: {error}")]
230    BadArgument {
231        #[source]
232        error: VmError,
233        arg: usize,
234    },
235    #[error("the index set operation `{target}[{index}] = {value}` is not supported")]
236    UnsupportedIndexSet {
237        target: TypeInfo,
238        index: TypeInfo,
239        value: TypeInfo,
240    },
241    #[error("the index get operation `{target}[{index}]` is not supported")]
242    UnsupportedIndexGet { target: TypeInfo, index: TypeInfo },
243    #[error("the tuple index get operation is not supported on `{target}`")]
244    UnsupportedTupleIndexGet { target: TypeInfo },
245    #[error("the tuple index set operation is not supported on `{target}`")]
246    UnsupportedTupleIndexSet { target: TypeInfo },
247    #[error("field not available on `{target}`")]
248    UnsupportedObjectSlotIndexGet { target: TypeInfo },
249    #[error("field not available on `{target}`")]
250    UnsupportedObjectSlotIndexSet { target: TypeInfo },
251    #[error("`{value} is {test_type}` is not supported")]
252    UnsupportedIs {
253        value: TypeInfo,
254        test_type: TypeInfo,
255    },
256    #[error("`{actual_type}` cannot be called since it's not a function")]
257    UnsupportedCallFn { actual_type: TypeInfo },
258    #[error("missing index by static string slot `{slot}` in object")]
259    ObjectIndexMissing { slot: usize },
260    #[error("`{target}` missing index `{index}`")]
261    MissingIndex {
262        target: TypeInfo,
263        index: VmIntegerRepr,
264    },
265    #[error("`{target}` missing index `{index:?}`")]
266    MissingIndexKey { target: TypeInfo, index: Key },
267    #[error("index out of bounds: the len is ${len} but the index is {index}")]
268    OutOfRange {
269        index: VmIntegerRepr,
270        len: VmIntegerRepr,
271    },
272    #[error("missing field `{field}` on `{target}`")]
273    MissingField { target: TypeInfo, field: String },
274    #[error("missing dynamic field for struct field `{target}::{name}`")]
275    MissingStructField {
276        target: &'static str,
277        name: &'static str,
278    },
279    #[error("missing dynamic index #{index} in tuple struct `{target}`")]
280    MissingTupleIndex { target: &'static str, index: usize },
281    #[error("expected result or option with value to unwrap, but got `{actual}`")]
282    UnsupportedUnwrap { actual: TypeInfo },
283    #[error("expected Some value, but got `None`")]
284    UnsupportedUnwrapNone,
285    #[error("expected Ok value, but got `Err({err})`")]
286    UnsupportedUnwrapErr { err: TypeInfo },
287    #[error("expected result or option as value, but got `{actual}`")]
288    UnsupportedIsValueOperand { actual: TypeInfo },
289    /// Trying to resume a generator that has completed.
290    #[error("cannot resume a generator that has completed")]
291    GeneratorComplete,
292    #[error("expected `{expected}`, but found `{actual}`")]
293    Expected {
294        expected: TypeInfo,
295        actual: TypeInfo,
296    },
297    #[error("expected `Any` type, but found `{actual}`")]
298    ExpectedAny { actual: TypeInfo },
299    #[error("failed to convert value `{from}` to integer `{to}`")]
300    ValueToIntegerCoercionError {
301        from: VmIntegerRepr,
302        to: &'static str,
303    },
304    #[error("failed to convert integer `{from}` to value `{to}`")]
305    IntegerToValueCoercionError {
306        from: VmIntegerRepr,
307        to: &'static str,
308    },
309    #[error("expected a tuple of length `{expected}`, but found one with length `{actual}`")]
310    ExpectedTupleLength { actual: usize, expected: usize },
311    #[error("unexpectedly ran out of items to iterate over")]
312    IterationError,
313    #[error("missing variant name in runtime information")]
314    MissingVariantName,
315    #[error("no variant matching `{name}`")]
316    MissingVariant { name: Box<str> },
317    #[error("expected an enum variant, but got `{actual}`")]
318    ExpectedVariant { actual: TypeInfo },
319    #[error("{actual} can't be converted to a constant value")]
320    ConstNotSupported { actual: TypeInfo },
321    #[error("{actual} can't be converted to a hash key")]
322    KeyNotSupported { actual: TypeInfo },
323    #[error("missing interface environment")]
324    MissingInterfaceEnvironment,
325    #[error("index out of bounds")]
326    IndexOutOfBounds,
327    #[error("unsupported range")]
328    UnsupportedRange,
329}
330
331impl VmErrorKind {
332    /// Unpack an unwound error, if it is present.
333    pub fn as_unwound_ref(&self) -> (&Self, Option<(Arc<Unit>, usize, Vec<CallFrame>)>) {
334        match self {
335            VmErrorKind::Unwound {
336                kind,
337                unit,
338                ip,
339                frames,
340            } => (&*kind, Some((unit.clone(), *ip, frames.clone()))),
341            kind => (kind, None),
342        }
343    }
344}
345
346/// A type-erased rust number.
347#[derive(Debug, Clone)]
348pub struct VmIntegerRepr(num_bigint::BigInt);
349
350impl<T> From<T> for VmIntegerRepr
351where
352    num_bigint::BigInt: From<T>,
353{
354    fn from(value: T) -> Self {
355        Self(num_bigint::BigInt::from(value))
356    }
357}
358
359impl fmt::Display for VmIntegerRepr {
360    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361        write!(f, "{}", self.0)
362    }
363}