essential_vm/
total_control_flow.rs1use crate::{
2 error::{OpError, OpResult, StackError, TotalControlFlowError},
3 Gas, Stack,
4};
5use essential_types::convert::bool_from_word;
6
7#[cfg(test)]
8mod tests;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub enum ProgramControlFlow {
13 Pc(usize),
15 Halt,
17 ComputeEnd,
19 ComputeResult((usize, Gas, bool)),
22}
23
24pub fn jump_if(stack: &mut Stack, pc: usize) -> OpResult<Option<ProgramControlFlow>> {
25 let [dist, cond] = stack.pop2()?;
26 let cond = bool_from_word(cond).ok_or(TotalControlFlowError::InvalidJumpForwardIfCondition)?;
27 if cond {
28 let neg = dist < 0;
29 let dist = usize::try_from(dist.abs()).map_err(|_| StackError::IndexOutOfBounds)?;
30 if dist == 0 {
31 return Err(TotalControlFlowError::JumpedToSelf.into());
32 }
33 if neg {
34 let pc = pc.checked_sub(dist).ok_or(OpError::PcOverflow)?;
35 Ok(Some(ProgramControlFlow::Pc(pc)))
36 } else {
37 let pc = pc.checked_add(dist).ok_or(OpError::PcOverflow)?;
38 Ok(Some(ProgramControlFlow::Pc(pc)))
39 }
40 } else {
41 Ok(None)
42 }
43}
44
45pub fn halt_if(stack: &mut Stack) -> OpResult<Option<ProgramControlFlow>> {
46 let cond = stack.pop()?;
47 let cond = bool_from_word(cond).ok_or(TotalControlFlowError::InvalidHaltIfCondition)?;
48 if cond {
49 Ok(Some(ProgramControlFlow::Halt))
50 } else {
51 Ok(None)
52 }
53}
54
55pub fn panic_if(stack: &mut Stack) -> OpResult<()> {
57 let cond = stack.pop()?;
58 let cond = bool_from_word(cond).ok_or(TotalControlFlowError::InvalidPanicIfCondition)?;
59 if cond {
60 let stack = stack.iter().copied().collect();
61 Err(TotalControlFlowError::Panic(stack).into())
62 } else {
63 Ok(())
64 }
65}