#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec::Vec};
use crate::Program;
use crate::dataflow::bytecode::{CfgContext, build_cfg, check_stack_depth_with_context};
use crate::dataflow::register::check_register_types_with_cfg;
use crate::dataflow::uninit::check_uninit_registers_with_cfg;
use super::error::VerifierError;
use super::scan::scan;
pub trait Phase {
type Error: Into<VerifierError>;
fn run(&self, program: &Program) -> Result<(), Self::Error>;
}
type PhaseBox = Box<dyn Fn(&Program) -> Result<(), VerifierError>>;
pub struct Verifier {
phases: Vec<PhaseBox>,
}
impl Verifier {
pub fn new() -> Self {
Self { phases: Vec::new() }
}
#[must_use]
pub fn with_phase<P>(mut self, phase: P) -> Self
where
P: Phase + 'static,
{
self.phases
.push(Box::new(move |prog| phase.run(prog).map_err(Into::into)));
self
}
pub fn run(&self, program: &Program) -> Result<(), VerifierError> {
for phase in &self.phases {
phase(program)?;
}
Ok(())
}
}
impl Default for Verifier {
fn default() -> Self {
Self::new()
.with_phase(CombinedPhase)
.with_phase(AllCfgPhases)
}
}
pub(super) struct CombinedPhase;
impl Phase for CombinedPhase {
type Error = VerifierError;
fn run(&self, program: &Program) -> Result<(), VerifierError> {
let (_table, _slots, err) = scan(program.code());
err.map_or(Ok(()), Err)
}
}
pub(super) struct AllCfgPhases;
impl Phase for AllCfgPhases {
type Error = VerifierError;
fn run(&self, program: &Program) -> Result<(), VerifierError> {
let code = program.code();
if code.is_empty() {
return Ok(());
}
let Some(ctx) = build_cfg(code, program.jump_table()) else {
return Ok(());
};
let CfgContext {
cfg,
effects,
loop_regions,
} = ctx;
check_register_types_with_cfg(program, &cfg)?;
check_uninit_registers_with_cfg(program, &cfg)?;
check_stack_depth_with_context(CfgContext {
cfg,
effects,
loop_regions,
})
}
}