tycho_vm/
error.rs

1use tycho_types::error::Error;
2
3use crate::stack::StackValueType;
4
5/// Result of VM-related stuff.
6pub type VmResult<T> = Result<T, Box<VmError>>;
7
8/// VM execution error.
9#[derive(Debug, thiserror::Error)]
10pub enum VmError {
11    #[error("stack underflow at depth {0}")]
12    StackUnderflow(usize),
13    #[error("too many arguments copied into a closure continuation: {0}")]
14    TooManyArguments(usize),
15    #[error("expected integer in range {min}..={max}, found {actual}")]
16    IntegerOutOfRange {
17        min: isize,
18        max: isize,
19        actual: String,
20    },
21    #[error("control register index out of range: {0}")]
22    ControlRegisterOutOfRange(usize),
23    #[error("control register redefined")]
24    ControlRegisterRedefined,
25    #[error("integer overflow")]
26    IntegerOverflow,
27    #[error("invalid opcode")]
28    InvalidOpcode,
29    #[error(
30        "expected type {}, found {}",
31        StackValueType::display_raw(*expected),
32        StackValueType::display_raw(*actual),
33    )]
34    InvalidType { expected: u8, actual: u8 },
35    #[error("out of gas")]
36    OutOfGas,
37    #[error(transparent)]
38    CellError(#[from] Error),
39    #[error("dict error")]
40    DictError,
41    #[error("unknown error. {0}")]
42    Unknown(String),
43}
44
45impl VmError {
46    pub fn is_out_of_gas(&self) -> bool {
47        matches!(self, Self::OutOfGas | Self::CellError(Error::Cancelled))
48    }
49
50    pub fn as_exception(&self) -> VmException {
51        match self {
52            Self::StackUnderflow(_) => VmException::StackUnderflow,
53            Self::TooManyArguments(_) => VmException::StackOverflow,
54            Self::IntegerOutOfRange { .. } => VmException::RangeCheck,
55            Self::ControlRegisterOutOfRange(_) => VmException::RangeCheck,
56            Self::ControlRegisterRedefined => VmException::TypeCheck,
57            Self::IntegerOverflow => VmException::IntOverflow,
58            Self::InvalidOpcode => VmException::InvalidOpcode,
59            Self::InvalidType { .. } => VmException::TypeCheck,
60            Self::OutOfGas => VmException::OutOfGas,
61            Self::Unknown(_) => VmException::Unknown,
62            Self::CellError(e) => match e {
63                Error::CellUnderflow => VmException::CellUnderflow,
64                Error::CellOverflow => VmException::CellOverflow,
65                Error::UnexpectedExoticCell | Error::UnexpectedOrdinaryCell => {
66                    VmException::VirtError
67                }
68                Error::Cancelled => VmException::OutOfGas, // ?
69                Error::IntOverflow => VmException::IntOverflow,
70                _ => VmException::Fatal, // ?
71            },
72            Self::DictError => VmException::DictError,
73        }
74    }
75}
76
77impl From<Error> for Box<VmError> {
78    #[inline]
79    fn from(e: Error) -> Self {
80        Box::new(VmError::CellError(e))
81    }
82}
83
84/// Opcode dump result.
85#[cfg(feature = "dump")]
86pub type DumpResult = Result<(), DumpError>;
87
88/// Opcode dump error.
89#[cfg(feature = "dump")]
90#[derive(thiserror::Error, Debug)]
91pub enum DumpError {
92    #[error(transparent)]
93    InvalidCode(#[from] tycho_types::error::Error),
94    #[error("invalid opcode")]
95    InvalidOpcode,
96    #[error("unexpected cell")]
97    CellMismatch,
98    #[error(transparent)]
99    WriterError(#[from] std::fmt::Error),
100}
101
102/// A code for an execution error.
103#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
104#[repr(u8)]
105pub enum VmException {
106    Ok = 0,
107    Alternative = 1,
108    StackUnderflow = 2,
109    StackOverflow = 3,
110    IntOverflow = 4,
111    RangeCheck = 5,
112    InvalidOpcode = 6,
113    TypeCheck = 7,
114    CellOverflow = 8,
115    CellUnderflow = 9,
116    DictError = 10,
117    Unknown = 11,
118    Fatal = 12,
119    OutOfGas = 13,
120    VirtError = 14,
121}
122
123impl VmException {
124    pub const fn as_exit_code(&self) -> i32 {
125        !(*self as i32)
126    }
127}
128
129impl std::fmt::Display for VmException {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        f.write_str(match self {
132            Self::Ok => "normal termination",
133            Self::Alternative => "alternative termination",
134            Self::StackUnderflow => "stack underflow",
135            Self::StackOverflow => "stack overflow",
136            Self::IntOverflow => "integer overflow",
137            Self::RangeCheck => "integer out of range",
138            Self::InvalidOpcode => "invalid opcode",
139            Self::TypeCheck => "type check error",
140            Self::CellOverflow => "cell overflow",
141            Self::CellUnderflow => "cell underflow",
142            Self::DictError => "dictionary error",
143            Self::Unknown => "unknown error",
144            Self::Fatal => "fatal error",
145            Self::OutOfGas => "out of gas",
146            Self::VirtError => "virtualization error",
147        })
148    }
149}