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#[derive(Error, Debug)]
12#[error(transparent)]
13pub struct VmError {
14 kind: Box<VmErrorKind>,
15}
16
17impl VmError {
18 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 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 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 pub fn expected_any(actual: TypeInfo) -> Self {
53 Self::from(VmErrorKind::ExpectedAny { actual })
54 }
55
56 pub fn kind(&self) -> &VmErrorKind {
58 &*self.kind
59 }
60
61 pub fn into_kind(self) -> VmErrorKind {
63 *self.kind
64 }
65
66 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 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 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 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 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#[allow(missing_docs)]
149#[derive(Debug, Error)]
150pub enum VmErrorKind {
151 #[error("{kind} (at inst {ip})")]
156 Unwound {
157 kind: Box<VmErrorKind>,
159 unit: Arc<Unit>,
161 ip: usize,
163 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 #[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 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#[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}