use tycho_types::error::Error;
use crate::stack::StackValueType;
pub type VmResult<T> = Result<T, Box<VmError>>;
#[derive(Debug, thiserror::Error)]
pub enum VmError {
#[error("stack underflow at depth {0}")]
StackUnderflow(usize),
#[error("too many arguments copied into a closure continuation: {0}")]
TooManyArguments(usize),
#[error("expected integer in range {min}..={max}, found {actual}")]
IntegerOutOfRange {
min: isize,
max: isize,
actual: String,
},
#[error("control register index out of range: {0}")]
ControlRegisterOutOfRange(usize),
#[error("control register redefined")]
ControlRegisterRedefined,
#[error("integer overflow")]
IntegerOverflow,
#[error("invalid opcode")]
InvalidOpcode,
#[error(
"expected type {}, found {}",
StackValueType::display_raw(*expected),
StackValueType::display_raw(*actual),
)]
InvalidType { expected: u8, actual: u8 },
#[error("out of gas")]
OutOfGas,
#[error(transparent)]
CellError(#[from] Error),
#[error("dict error")]
DictError,
#[error("unknown error. {0}")]
Unknown(String),
}
impl VmError {
pub fn is_out_of_gas(&self) -> bool {
matches!(self, Self::OutOfGas | Self::CellError(Error::Cancelled))
}
pub fn as_exception(&self) -> VmException {
match self {
Self::StackUnderflow(_) => VmException::StackUnderflow,
Self::TooManyArguments(_) => VmException::StackOverflow,
Self::IntegerOutOfRange { .. } => VmException::RangeCheck,
Self::ControlRegisterOutOfRange(_) => VmException::RangeCheck,
Self::ControlRegisterRedefined => VmException::TypeCheck,
Self::IntegerOverflow => VmException::IntOverflow,
Self::InvalidOpcode => VmException::InvalidOpcode,
Self::InvalidType { .. } => VmException::TypeCheck,
Self::OutOfGas => VmException::OutOfGas,
Self::Unknown(_) => VmException::Unknown,
Self::CellError(e) => match e {
Error::CellUnderflow => VmException::CellUnderflow,
Error::CellOverflow => VmException::CellOverflow,
Error::UnexpectedExoticCell | Error::UnexpectedOrdinaryCell => {
VmException::VirtError
}
Error::Cancelled => VmException::OutOfGas, Error::IntOverflow => VmException::IntOverflow,
Error::InvalidCell => VmException::CellOverflow,
_ => VmException::Fatal, },
Self::DictError => VmException::DictError,
}
}
}
impl From<Error> for Box<VmError> {
#[inline]
fn from(e: Error) -> Self {
Box::new(VmError::CellError(e))
}
}
#[cfg(feature = "dump")]
pub type DumpResult = Result<(), DumpError>;
#[cfg(feature = "dump")]
#[derive(thiserror::Error, Debug)]
pub enum DumpError {
#[error(transparent)]
InvalidCode(#[from] tycho_types::error::Error),
#[error("invalid opcode")]
InvalidOpcode,
#[error("unexpected cell")]
CellMismatch,
#[error(transparent)]
WriterError(#[from] std::fmt::Error),
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
#[repr(u8)]
pub enum VmException {
Ok = 0,
Alternative = 1,
StackUnderflow = 2,
StackOverflow = 3,
IntOverflow = 4,
RangeCheck = 5,
InvalidOpcode = 6,
TypeCheck = 7,
CellOverflow = 8,
CellUnderflow = 9,
DictError = 10,
Unknown = 11,
Fatal = 12,
OutOfGas = 13,
VirtError = 14,
}
impl VmException {
pub const fn as_exit_code(&self) -> i32 {
!(*self as i32)
}
}
impl std::fmt::Display for VmException {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Ok => "normal termination",
Self::Alternative => "alternative termination",
Self::StackUnderflow => "stack underflow",
Self::StackOverflow => "stack overflow",
Self::IntOverflow => "integer overflow",
Self::RangeCheck => "integer out of range",
Self::InvalidOpcode => "invalid opcode",
Self::TypeCheck => "type check error",
Self::CellOverflow => "cell overflow",
Self::CellUnderflow => "cell underflow",
Self::DictError => "dictionary error",
Self::Unknown => "unknown error",
Self::Fatal => "fatal error",
Self::OutOfGas => "out of gas",
Self::VirtError => "virtualization error",
})
}
}