use crate::IType;
use crate::IValue;
use crate::{ast::TypeKind, interpreter::Instruction};
use std::{
error::Error,
fmt::{self, Display, Formatter},
num::TryFromIntError,
result::Result,
string::{self, ToString},
};
use it_lilo::lifter::LiError;
use it_lilo::lowerer::LoError;
use thiserror::Error as ThisError;
pub use fluence_it_types::WasmValueNativeCastError;
use it_memory_traits::MemoryAccessError;
pub type InstructionResult<T> = Result<T, InstructionError>;
pub type InterpreterResult<T> = Result<T, InstructionError>;
#[derive(Debug)]
pub struct InstructionError {
pub instruction: Instruction,
pub error_kind: InstructionErrorKind,
}
impl InstructionError {
pub(crate) fn from_error_kind(
instruction: Instruction,
error_kind: InstructionErrorKind,
) -> Self {
Self {
instruction,
error_kind,
}
}
pub(crate) fn from_li(instruction: Instruction, li: LiError) -> Self {
let error_kind = InstructionErrorKind::LiError(li);
Self::from_error_kind(instruction, error_kind)
}
pub(crate) fn from_lo(instruction: Instruction, lo: LoError) -> Self {
let error_kind = InstructionErrorKind::LoError(lo);
Self::from_error_kind(instruction, error_kind)
}
pub(crate) fn from_memory_access(
instruction: Instruction,
memory_access: MemoryAccessError,
) -> Self {
match memory_access {
MemoryAccessError::OutOfBounds {
offset,
size,
memory_size,
} => Self::from_error_kind(
instruction.clone(),
InstructionErrorKind::MemoryOutOfBoundsAccess {
index: offset + size,
length: memory_size,
},
),
}
}
}
impl Error for InstructionError {}
#[macro_export]
macro_rules! instr_error {
($instruction:expr, $error_kind:expr) => {
Err(crate::errors::InstructionError::from_error_kind(
$instruction,
$error_kind,
))
};
}
impl Display for InstructionError {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(
formatter,
"`{}` {}",
(&self.instruction).to_string(),
self.error_kind
)
}
}
#[derive(ThisError, Debug)]
pub enum InstructionErrorKind {
#[error("cannot access invocation inputs #{index} because it doesn't exist")]
InvocationInputIsMissing {
index: u32,
},
#[error("failed to cast the WIT value `{0}` to its native type")]
ToNative(#[from] WasmValueNativeCastError),
#[error("failed to cast `{from:?}` to `{to:?}`")]
LoweringLifting {
from: IType,
to: IType,
},
#[error("read a value `{expected_type:?}` from the stack, that can't be converted to `{received_value:?}`")]
InvalidValueOnTheStack {
expected_type: IType,
received_value: IValue,
},
#[error(
"needed to read `{needed}` value(s) from the stack, but it doesn't contain enough data"
)]
StackIsTooSmall {
needed: usize,
},
#[error("the local or import function `{function_index}` doesn't exist")]
LocalOrImportIsMissing {
function_index: u32,
},
#[error(
"the local or import function `{function_index}` has the signature\
`{:?} -> {:?}`\
but it received values of kind `{:?} -> {:?}`",
.expected.0, .expected.1, .received.0, .received.1,
)]
LocalOrImportSignatureMismatch {
function_index: u32,
expected: (Vec<IType>, Vec<IType>),
received: (Vec<IType>, Vec<IType>),
},
#[error("failed while calling the local or import function `{function_name}`: {reason}")]
LocalOrImportCall {
function_name: String,
#[source]
reason: anyhow::Error,
},
#[error("memory `{memory_index}` does not exist")]
MemoryIsMissing {
memory_index: usize,
},
#[error("read out of the memory bounds (index {index} > memory length {length})")]
MemoryOutOfBoundsAccess {
index: u32,
length: u32,
},
#[error("{0}")]
String(string::FromUtf8Error),
#[error("attempted to convert `{subject}`, but it appears to be a negative value")]
NegativeValue {
subject: &'static str,
},
#[error("the type `{type_index}` doesn't exist")]
TypeIsMissing {
type_index: u32,
},
#[error("type with `{record_type_id}` is missing in a Wasm binary")]
RecordTypeByNameIsMissing {
record_type_id: u64,
},
#[error("{0}")]
CorruptedArray(String),
#[error("{0}")]
CorruptedRecord(String),
#[error(
"read a type of kind `{received_kind:?}`,\
but the kind `{expected_kind:?}` was expected"
)]
InvalidTypeKind {
expected_kind: TypeKind,
received_kind: TypeKind,
},
#[error("serde error: {0}")]
SerdeError(String),
#[error("{0}")]
LiError(#[from] LiError),
#[error("{0}")]
LoError(#[from] LoError),
}
impl From<(TryFromIntError, &'static str)> for InstructionErrorKind {
fn from((_, subject): (TryFromIntError, &'static str)) -> Self {
InstructionErrorKind::NegativeValue { subject }
}
}