tinywasm 0.11.0

A tiny WebAssembly interpreter
Documentation
use super::*;

macro_rules! define_stable_dispatch {
    ($executor:ident, $instr_ptr:ident, $dispatch_next:ident, $dispatch_flow:ident;
     $($variant:ident $(($($arg:pat),*))? $({ $($field:ident),* })? => $body:expr),* $(,)?) => {
        #[inline(always)]
        fn exec_step($executor: &mut Self, $instr_ptr: usize) -> ExecResult<ExecFlow> {
            macro_rules! $dispatch_next {
                ($next_instr_ptr:expr) => {{
                    return Ok(ExecFlow::next($next_instr_ptr));
                }};
            }
            macro_rules! $dispatch_flow {
                ($flow:expr) => {{
                    return Ok($flow);
                }};
            }
            use tinywasm_types::Instruction::*;
            match &$executor.func.instructions[$instr_ptr] {
                $($variant $(($($arg),*))? $({ $($field),* })? => $body,)*
            }
            Ok(ExecFlow::next($instr_ptr + 1))
        }
    };
}

impl Executor<'_> {
    instruction_handlers!(define_stable_dispatch);

    #[inline(always)]
    pub(crate) fn run_to_completion(mut self) -> Result<()> {
        let mut instr_ptr = self.cf.instr_ptr;
        loop {
            match Self::exec_step(&mut self, instr_ptr)?.next_instr_ptr() {
                Some(next_instr_ptr) => instr_ptr = next_instr_ptr,
                None => return cold!(Ok(())),
            }
        }
    }

    #[cfg(feature = "std")]
    #[inline(always)]
    pub(crate) fn run_with_time_budget(mut self, time_budget: core::time::Duration) -> Result<ExecState> {
        use crate::std::time::Instant;

        if time_budget.is_zero() {
            return Ok(ExecState::Suspended(self.cf));
        }
        let start = Instant::now();
        let mut instr_ptr = self.cf.instr_ptr;
        loop {
            for _ in 0..CHECKPOINT_INTERVAL {
                match Self::exec_step(&mut self, instr_ptr)?.next_instr_ptr() {
                    Some(next_instr_ptr) => instr_ptr = next_instr_ptr,
                    None => return Ok(ExecState::Completed),
                }
            }

            if start.elapsed() >= time_budget {
                self.cf.instr_ptr = instr_ptr;
                return Ok(ExecState::Suspended(self.cf));
            }
        }
    }

    #[inline(always)]
    pub(crate) fn run_with_fuel(mut self, fuel: u32) -> Result<ExecState> {
        self.fuel_metered = true;
        self.store.execution_fuel = fuel;
        if self.store.execution_fuel == 0 {
            return Ok(ExecState::Suspended(self.cf));
        }

        let mut instr_ptr = self.cf.instr_ptr;
        loop {
            for _ in 0..CHECKPOINT_INTERVAL {
                match Self::exec_step(&mut self, instr_ptr)?.next_instr_ptr() {
                    Some(next_instr_ptr) => instr_ptr = next_instr_ptr,
                    None => return Ok(ExecState::Completed),
                }
            }

            self.store.execution_fuel = self.store.execution_fuel.saturating_sub(CHECKPOINT_INTERVAL);
            if self.store.execution_fuel == 0 {
                self.cf.instr_ptr = instr_ptr;
                return Ok(ExecState::Suspended(self.cf));
            }
        }
    }
}