miden_testing/
executor.rs

1#[cfg(test)]
2use miden_processor::DefaultHost;
3use miden_processor::fast::{ExecutionOutput, FastProcessor};
4use miden_processor::{AdviceInputs, AsyncHost, ExecutionError, Program, StackInputs};
5
6// CODE EXECUTOR
7// ================================================================================================
8
9/// Helper for executing arbitrary code within arbitrary hosts.
10pub(crate) struct CodeExecutor<H> {
11    host: H,
12    stack_inputs: Option<StackInputs>,
13    advice_inputs: AdviceInputs,
14}
15
16impl<H: AsyncHost> CodeExecutor<H> {
17    // CONSTRUCTOR
18    // --------------------------------------------------------------------------------------------
19    pub(crate) fn new(host: H) -> Self {
20        Self {
21            host,
22            stack_inputs: None,
23            advice_inputs: AdviceInputs::default(),
24        }
25    }
26
27    pub fn extend_advice_inputs(mut self, advice_inputs: AdviceInputs) -> Self {
28        self.advice_inputs.extend(advice_inputs);
29        self
30    }
31
32    pub fn stack_inputs(mut self, stack_inputs: StackInputs) -> Self {
33        self.stack_inputs = Some(stack_inputs);
34        self
35    }
36
37    /// Compiles and runs the desired code in the host and returns the [`Process`] state.
38    ///
39    /// To improve the error message quality, convert the returned [`ExecutionError`] into a
40    /// [`Report`](miden_objects::assembly::diagnostics::Report).
41    #[cfg(test)]
42    pub async fn run(self, code: &str) -> Result<ExecutionOutput, ExecutionError> {
43        use alloc::borrow::ToOwned;
44        use alloc::sync::Arc;
45
46        use miden_lib::transaction::TransactionKernel;
47        use miden_objects::assembly::debuginfo::{SourceLanguage, Uri};
48        use miden_objects::assembly::{DefaultSourceManager, SourceManagerSync};
49
50        let source_manager: Arc<dyn SourceManagerSync> = Arc::new(DefaultSourceManager::default());
51        let assembler = TransactionKernel::with_kernel_library(source_manager.clone());
52
53        // Virtual file name should be unique.
54        let virtual_source_file =
55            source_manager.load(SourceLanguage::Masm, Uri::new("_user_code"), code.to_owned());
56        let program = assembler.assemble_program(virtual_source_file).unwrap();
57
58        self.execute_program(program).await
59    }
60
61    /// Executes the provided [`Program`] and returns the [`Process`] state.
62    ///
63    /// To improve the error message quality, convert the returned [`ExecutionError`] into a
64    /// [`Report`](miden_objects::assembly::diagnostics::Report).
65    pub async fn execute_program(
66        mut self,
67        program: Program,
68    ) -> Result<ExecutionOutput, ExecutionError> {
69        // This reverses the stack inputs (even though it doesn't look like it does) because the
70        // fast processor expects the reverse order.
71        //
72        // Once we use the FastProcessor for execution and proving, we can change the way these
73        // inputs are constructed in TransactionKernel::prepare_inputs.
74        let stack_inputs =
75            StackInputs::new(self.stack_inputs.unwrap_or_default().iter().copied().collect())
76                .unwrap();
77
78        let processor = FastProcessor::new_debug(stack_inputs.as_slice(), self.advice_inputs);
79
80        let execution_output = processor.execute(&program, &mut self.host).await?;
81
82        Ok(execution_output)
83    }
84}
85
86#[cfg(test)]
87impl CodeExecutor<DefaultHost> {
88    pub fn with_default_host() -> Self {
89        use miden_lib::transaction::TransactionKernel;
90
91        let mut host = DefaultHost::default();
92
93        let test_lib = TransactionKernel::library();
94        host.load_library(test_lib.mast_forest()).unwrap();
95
96        CodeExecutor::new(host)
97    }
98}