use crate::{panic::BoxedPanic, CallFrame};
use crate::{
AccessError, Hash, Item, Key, Panic, Protocol, StackError, TypeInfo, TypeOf, Unit, Value,
VmHaltInfo,
};
use std::fmt;
use std::sync::Arc;
use thiserror::Error;
#[derive(Error, Debug)]
#[error(transparent)]
pub struct VmError {
kind: Box<VmErrorKind>,
}
impl VmError {
pub fn panic<D>(message: D) -> Self
where
D: BoxedPanic,
{
Self::from(VmErrorKind::Panic {
reason: Panic::custom(message),
})
}
pub fn bad_argument<T>(arg: usize, value: &Value) -> Result<Self, VmError>
where
T: TypeOf,
{
Ok(Self::from(VmErrorKind::BadArgumentAt {
arg,
expected: T::type_info(),
actual: value.type_info()?,
}))
}
pub fn expected<T>(actual: TypeInfo) -> Self
where
T: TypeOf,
{
Self::from(VmErrorKind::Expected {
expected: T::type_info(),
actual,
})
}
pub fn expected_any(actual: TypeInfo) -> Self {
Self::from(VmErrorKind::ExpectedAny { actual })
}
pub fn kind(&self) -> &VmErrorKind {
&*self.kind
}
pub fn into_kind(self) -> VmErrorKind {
*self.kind
}
pub fn into_unwinded(self, unit: &Arc<Unit>, ip: usize, frames: Vec<crate::CallFrame>) -> Self {
if let VmErrorKind::Unwound { .. } = &*self.kind {
return self;
}
Self::from(VmErrorKind::Unwound {
kind: self.kind,
unit: unit.clone(),
ip,
frames,
})
}
pub fn as_unwound<'a>(
&'a self,
) -> (
&'a VmErrorKind,
Option<(&'a Arc<Unit>, usize, Vec<CallFrame>)>,
) {
match &*self.kind {
VmErrorKind::Unwound {
kind,
unit,
ip,
frames,
} => (&*kind, Some((unit, *ip, frames.clone()))),
kind => (kind, None),
}
}
pub fn into_unwound(self) -> (Self, Option<(Arc<Unit>, usize, Vec<CallFrame>)>) {
match *self.kind {
VmErrorKind::Unwound {
kind,
unit,
ip,
frames,
} => {
let error = Self { kind };
(error, Some((unit, ip, frames)))
}
kind => (Self::from(kind), None),
}
}
pub fn unpack_critical(self) -> Result<Self, Self> {
if self.is_critical() {
Err(self)
} else {
Ok(self)
}
}
fn is_critical(&self) -> bool {
match &*self.kind {
VmErrorKind::Panic { .. } => true,
VmErrorKind::Unwound { .. } => true,
_ => false,
}
}
}
impl<E> From<E> for VmError
where
VmErrorKind: From<E>,
{
fn from(err: E) -> Self {
Self {
kind: Box::new(VmErrorKind::from(err)),
}
}
}
#[allow(missing_docs)]
#[derive(Debug, Error)]
pub enum VmErrorKind {
#[error("{kind} (at inst {ip})")]
Unwound {
kind: Box<VmErrorKind>,
unit: Arc<Unit>,
ip: usize,
frames: Vec<CallFrame>,
},
#[error("{error}")]
AccessError {
#[from]
error: AccessError,
},
#[error("panicked: {reason}")]
Panic { reason: Panic },
#[error("no running virtual machines")]
NoRunningVm,
#[error("halted for unexpected reason `{halt}`")]
Halted { halt: VmHaltInfo },
#[error("failed to format argument")]
FormatError,
#[error("stack error: {error}")]
StackError {
#[from]
error: StackError,
},
#[error("numerical overflow")]
Overflow,
#[error("numerical underflow")]
Underflow,
#[error("division by zero")]
DivideByZero,
#[error("missing constant with hash `{hash}`")]
MissingConst { hash: Hash },
#[error("missing entry `{item}` with hash `{hash}`")]
MissingEntry { item: Item, hash: Hash },
#[error("missing function with hash `{hash}`")]
MissingFunction { hash: Hash },
#[error("missing instance function `{hash}` for `{instance}`")]
MissingInstanceFunction { hash: Hash, instance: TypeInfo },
#[error("instruction pointer is out-of-bounds")]
IpOutOfBounds,
#[error("unsupported vm operation `{lhs} {op} {rhs}`")]
UnsupportedBinaryOperation {
op: &'static str,
lhs: TypeInfo,
rhs: TypeInfo,
},
#[error("unsupported vm operation `{op}{operand}`")]
UnsupportedUnaryOperation { op: &'static str, operand: TypeInfo },
#[error("`{actual}` does not implement the `{protocol}` protocol")]
MissingProtocol {
protocol: Protocol,
actual: TypeInfo,
},
#[error("static string slot `{slot}` does not exist")]
MissingStaticString { slot: usize },
#[error("static object keys slot `{slot}` does not exist")]
MissingStaticObjectKeys { slot: usize },
#[error("missing runtime information for variant with hash `{hash}`")]
MissingVariantRtti { hash: Hash },
#[error("missing runtime information for type with hash `{hash}`")]
MissingRtti { hash: Hash },
#[error("wrong number of arguments `{actual}`, expected `{expected}`")]
BadArgumentCount { actual: usize, expected: usize },
#[error("bad argument #{arg}, expected `{expected}` but got `{actual}`")]
BadArgumentAt {
arg: usize,
expected: TypeInfo,
actual: TypeInfo,
},
#[error("bad argument #{arg}: {error}")]
BadArgument {
#[source]
error: VmError,
arg: usize,
},
#[error("the index set operation `{target}[{index}] = {value}` is not supported")]
UnsupportedIndexSet {
target: TypeInfo,
index: TypeInfo,
value: TypeInfo,
},
#[error("the index get operation `{target}[{index}]` is not supported")]
UnsupportedIndexGet { target: TypeInfo, index: TypeInfo },
#[error("the tuple index get operation is not supported on `{target}`")]
UnsupportedTupleIndexGet { target: TypeInfo },
#[error("the tuple index set operation is not supported on `{target}`")]
UnsupportedTupleIndexSet { target: TypeInfo },
#[error("field not available on `{target}`")]
UnsupportedObjectSlotIndexGet { target: TypeInfo },
#[error("field not available on `{target}`")]
UnsupportedObjectSlotIndexSet { target: TypeInfo },
#[error("`{value} is {test_type}` is not supported")]
UnsupportedIs {
value: TypeInfo,
test_type: TypeInfo,
},
#[error("`{actual_type}` cannot be called since it's not a function")]
UnsupportedCallFn { actual_type: TypeInfo },
#[error("missing index by static string slot `{slot}` in object")]
ObjectIndexMissing { slot: usize },
#[error("`{target}` missing index `{index}`")]
MissingIndex {
target: TypeInfo,
index: VmIntegerRepr,
},
#[error("`{target}` missing index `{index:?}`")]
MissingIndexKey { target: TypeInfo, index: Key },
#[error("index out of bounds: the len is ${len} but the index is {index}")]
OutOfRange {
index: VmIntegerRepr,
len: VmIntegerRepr,
},
#[error("missing field `{field}` on `{target}`")]
MissingField { target: TypeInfo, field: String },
#[error("missing dynamic field for struct field `{target}::{name}`")]
MissingStructField {
target: &'static str,
name: &'static str,
},
#[error("missing dynamic index #{index} in tuple struct `{target}`")]
MissingTupleIndex { target: &'static str, index: usize },
#[error("expected result or option with value to unwrap, but got `{actual}`")]
UnsupportedUnwrap { actual: TypeInfo },
#[error("expected Some value, but got `None`")]
UnsupportedUnwrapNone,
#[error("expected Ok value, but got `Err({err})`")]
UnsupportedUnwrapErr { err: TypeInfo },
#[error("expected result or option as value, but got `{actual}`")]
UnsupportedIsValueOperand { actual: TypeInfo },
#[error("cannot resume a generator that has completed")]
GeneratorComplete,
#[error("expected `{expected}`, but found `{actual}`")]
Expected {
expected: TypeInfo,
actual: TypeInfo,
},
#[error("expected `Any` type, but found `{actual}`")]
ExpectedAny { actual: TypeInfo },
#[error("failed to convert value `{from}` to integer `{to}`")]
ValueToIntegerCoercionError {
from: VmIntegerRepr,
to: &'static str,
},
#[error("failed to convert integer `{from}` to value `{to}`")]
IntegerToValueCoercionError {
from: VmIntegerRepr,
to: &'static str,
},
#[error("expected a tuple of length `{expected}`, but found one with length `{actual}`")]
ExpectedTupleLength { actual: usize, expected: usize },
#[error("unexpectedly ran out of items to iterate over")]
IterationError,
#[error("missing variant name in runtime information")]
MissingVariantName,
#[error("no variant matching `{name}`")]
MissingVariant { name: Box<str> },
#[error("expected an enum variant, but got `{actual}`")]
ExpectedVariant { actual: TypeInfo },
#[error("{actual} can't be converted to a constant value")]
ConstNotSupported { actual: TypeInfo },
#[error("{actual} can't be converted to a hash key")]
KeyNotSupported { actual: TypeInfo },
#[error("missing interface environment")]
MissingInterfaceEnvironment,
#[error("index out of bounds")]
IndexOutOfBounds,
#[error("unsupported range")]
UnsupportedRange,
}
impl VmErrorKind {
pub fn as_unwound_ref(&self) -> (&Self, Option<(Arc<Unit>, usize, Vec<CallFrame>)>) {
match self {
VmErrorKind::Unwound {
kind,
unit,
ip,
frames,
} => (&*kind, Some((unit.clone(), *ip, frames.clone()))),
kind => (kind, None),
}
}
}
#[derive(Debug, Clone)]
pub struct VmIntegerRepr(num_bigint::BigInt);
impl<T> From<T> for VmIntegerRepr
where
num_bigint::BigInt: From<T>,
{
fn from(value: T) -> Self {
Self(num_bigint::BigInt::from(value))
}
}
impl fmt::Display for VmIntegerRepr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}