wasmi 2.0.0-beta.2

WebAssembly interpreter
Documentation
#[cfg_attr(feature = "portable-dispatch", path = "backend/loop.rs")]
#[cfg_attr(not(feature = "portable-dispatch"), path = "backend/tail.rs")]
#[macro_use]
pub mod backend;

pub use self::backend::{Done, Handler, execute_until_done, op_code_to_handler};
use super::state::Ip;
use crate::{
    Error,
    TrapCode,
    engine::{ResumableHostTrapError, ResumableOutOfFuelError},
};
use core::ops::ControlFlow;

#[inline(always)]
pub fn control_break<T>() -> Control<T> {
    Control::Break(Break::WithReason)
}

#[allow(unused)]
#[inline(always)]
fn decode_op_code(ip: Ip) -> crate::ir::OpCode {
    let (_, op_code) = unsafe { ip.decode::<crate::ir::OpCode>() };
    op_code
}

#[allow(unused)]
#[inline(always)]
fn decode_handler(ip: Ip) -> Handler {
    use core::{mem, ptr};
    let (_, addr) = unsafe { ip.decode::<usize>() };
    unsafe { mem::transmute::<*const (), Handler>(ptr::with_exposed_provenance(addr)) }
}

#[derive(Debug)]
pub enum ExecutionOutcome {
    Host(ResumableHostTrapError),
    OutOfFuel(ResumableOutOfFuelError),
    Error(Error),
}

impl From<ExecutionOutcome> for Error {
    fn from(error: ExecutionOutcome) -> Self {
        match error {
            ExecutionOutcome::Host(error) => error.into(),
            ExecutionOutcome::OutOfFuel(error) => error.into(),
            ExecutionOutcome::Error(error) => error,
        }
    }
}

impl From<ResumableHostTrapError> for ExecutionOutcome {
    #[cold]
    #[inline]
    fn from(error: ResumableHostTrapError) -> Self {
        Self::Host(error)
    }
}

impl From<ResumableOutOfFuelError> for ExecutionOutcome {
    #[cold]
    #[inline]
    fn from(error: ResumableOutOfFuelError) -> Self {
        Self::OutOfFuel(error)
    }
}

impl From<TrapCode> for ExecutionOutcome {
    #[cold]
    #[inline]
    fn from(error: TrapCode) -> Self {
        Self::Error(error.into())
    }
}

impl From<Error> for ExecutionOutcome {
    #[cold]
    #[inline]
    fn from(error: Error) -> Self {
        Self::Error(error)
    }
}

impl ExecutionOutcome {
    /// Converts resumable [`ExecutionOutcome::Host`] and [`ExecutionOutcome::OutOfFuel`] into non-resumable errors.
    #[inline]
    pub fn into_non_resumable(self) -> Error {
        match self {
            Self::Host(error) => error.into_error(),
            Self::OutOfFuel(_error) => Error::from(TrapCode::OutOfFuel),
            Self::Error(error) => error,
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub enum Break {
    UnreachableCodeReached = TrapCode::UnreachableCodeReached as _,
    MemoryOutOfBounds = TrapCode::MemoryOutOfBounds as _,
    TableOutOfBounds = TrapCode::TableOutOfBounds as _,
    IndirectCallToNull = TrapCode::IndirectCallToNull as _,
    IntegerDivisionByZero = TrapCode::IntegerDivisionByZero as _,
    IntegerOverflow = TrapCode::IntegerOverflow as _,
    BadConversionToInteger = TrapCode::BadConversionToInteger as _,
    StackOverflow = TrapCode::StackOverflow as _,
    BadSignature = TrapCode::BadSignature as _,
    OutOfFuel = TrapCode::OutOfFuel as _,
    GrowthOperationLimited = TrapCode::GrowthOperationLimited as _,
    OutOfSystemMemory = TrapCode::OutOfSystemMemory as _,
    /// Signals that there must be a reason stored externally supplying the caller with more information.
    WithReason,
}

impl From<TrapCode> for Break {
    #[inline]
    fn from(trap_code: TrapCode) -> Self {
        match trap_code {
            TrapCode::UnreachableCodeReached => Self::UnreachableCodeReached,
            TrapCode::MemoryOutOfBounds => Self::MemoryOutOfBounds,
            TrapCode::TableOutOfBounds => Self::TableOutOfBounds,
            TrapCode::IndirectCallToNull => Self::IndirectCallToNull,
            TrapCode::IntegerDivisionByZero => Self::IntegerDivisionByZero,
            TrapCode::IntegerOverflow => Self::IntegerOverflow,
            TrapCode::BadConversionToInteger => Self::BadConversionToInteger,
            TrapCode::StackOverflow => Self::StackOverflow,
            TrapCode::BadSignature => Self::BadSignature,
            TrapCode::OutOfFuel => Self::OutOfFuel,
            TrapCode::GrowthOperationLimited => Self::GrowthOperationLimited,
            TrapCode::OutOfSystemMemory => Self::OutOfSystemMemory,
        }
    }
}

impl Break {
    #[inline]
    pub fn trap_code(self) -> Option<TrapCode> {
        let trap_code = match self {
            Self::UnreachableCodeReached => TrapCode::UnreachableCodeReached,
            Self::MemoryOutOfBounds => TrapCode::MemoryOutOfBounds,
            Self::TableOutOfBounds => TrapCode::TableOutOfBounds,
            Self::IndirectCallToNull => TrapCode::IndirectCallToNull,
            Self::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
            Self::IntegerOverflow => TrapCode::IntegerOverflow,
            Self::BadConversionToInteger => TrapCode::BadConversionToInteger,
            Self::StackOverflow => TrapCode::StackOverflow,
            Self::BadSignature => TrapCode::BadSignature,
            Self::OutOfFuel => TrapCode::OutOfFuel,
            Self::GrowthOperationLimited => TrapCode::GrowthOperationLimited,
            Self::OutOfSystemMemory => TrapCode::OutOfSystemMemory,
            _ => return None,
        };
        Some(trap_code)
    }
}

pub type Control<C = (), B = Break> = ControlFlow<B, C>;