Skip to main content

miden_processor/
executor.rs

1use miden_mast_package::debug_info::{DebugSourceNodeId, PackageDebugInfo};
2
3use crate::{
4    ExecutionError, ExecutionOptions, ExecutionOutput, FastProcessor, FutureMaybeSend, Host,
5    Program, StackInputs,
6    advice::{AdviceError, AdviceInputs},
7};
8
9// PROGRAM EXECUTOR
10// ================================================================================================
11
12/// A pluggable program executor used to run a [`Program`] against a [`Host`].
13///
14/// Defaults to [`FastProcessor`]. Alternative implementations can wrap execution in a debugger,
15/// add instrumentation, or redirect to a different backend, while leaving the surrounding
16/// executor wiring untouched.
17pub trait ProgramExecutor {
18    /// Creates a new executor configured with the provided inputs and options.
19    ///
20    /// In generic code (`E: ProgramExecutor`) this resolves normally. For the concrete
21    /// [`FastProcessor`] type, however, the inherent
22    /// [`FastProcessor::new`](crate::FastProcessor::new) (which takes only stack inputs)
23    /// shadows this trait method by name, so invoke the trait constructor with fully-qualified
24    /// syntax: `<FastProcessor as ProgramExecutor>::new(stack_inputs, advice_inputs, options)`.
25    fn new(
26        stack_inputs: StackInputs,
27        advice_inputs: AdviceInputs,
28        options: ExecutionOptions,
29    ) -> Result<Self, AdviceError>
30    where
31        Self: Sized;
32
33    /// Configures package-owned source and debug information for execution.
34    fn with_debug_info(self, package_debug_info: PackageDebugInfo) -> Self;
35
36    /// Configures the source node at which execution begins.
37    fn with_entrypoint_source_node(self, entrypoint_source_node: Option<DebugSourceNodeId>)
38    -> Self;
39
40    /// Executes the provided program against the given host.
41    fn execute<H: Host + Send>(
42        self,
43        program: &Program,
44        host: &mut H,
45    ) -> impl FutureMaybeSend<Result<ExecutionOutput, ExecutionError>>;
46}
47
48impl ProgramExecutor for FastProcessor {
49    fn new(
50        stack_inputs: StackInputs,
51        advice_inputs: AdviceInputs,
52        options: ExecutionOptions,
53    ) -> Result<Self, AdviceError> {
54        FastProcessor::new_with_options(stack_inputs, advice_inputs, options)
55    }
56
57    fn with_debug_info(mut self, package_debug_info: PackageDebugInfo) -> Self {
58        self.package_debug_info = Some(package_debug_info);
59        self
60    }
61
62    fn with_entrypoint_source_node(
63        mut self,
64        entrypoint_source_node: Option<DebugSourceNodeId>,
65    ) -> Self {
66        self.entrypoint_source_node = entrypoint_source_node;
67        self
68    }
69
70    fn execute<H: Host + Send>(
71        self,
72        program: &Program,
73        host: &mut H,
74    ) -> impl FutureMaybeSend<Result<ExecutionOutput, ExecutionError>> {
75        async move {
76            match (self.package_debug_info.clone(), self.entrypoint_source_node) {
77                (Some(package_debug_info), Some(entrypoint_source_node)) => {
78                    FastProcessor::execute_with_package_debug_info_at_source_node(
79                        self,
80                        program,
81                        &package_debug_info,
82                        entrypoint_source_node,
83                        host,
84                    )
85                    .await
86                },
87                (Some(package_debug_info), None) => {
88                    FastProcessor::execute_with_package_debug_info(
89                        self,
90                        program,
91                        &package_debug_info,
92                        host,
93                    )
94                    .await
95                },
96                (None, _) => FastProcessor::execute(self, program, host).await,
97            }
98        }
99    }
100}
101
102// TESTS
103// ================================================================================================
104
105#[cfg(test)]
106mod tests {
107    use miden_assembly::Assembler;
108
109    use super::*;
110    use crate::{DefaultHost, StackInputs};
111
112    #[tokio::test(flavor = "current_thread")]
113    async fn program_executor_default_impl_runs_via_trait() {
114        let program = Assembler::default()
115            .assemble_program("program", "begin push.3 swap drop end")
116            .unwrap()
117            .unwrap_program();
118
119        // Drive execution entirely through the trait, defaulting to `FastProcessor`.
120        let processor = <FastProcessor as ProgramExecutor>::new(
121            StackInputs::default(),
122            AdviceInputs::default(),
123            ExecutionOptions::default(),
124        )
125        .unwrap();
126        let output = <FastProcessor as ProgramExecutor>::execute(
127            processor,
128            &program,
129            &mut DefaultHost::default(),
130        )
131        .await
132        .unwrap();
133
134        // push.3 leaves 3 on top; `swap drop` restores the operand stack to its
135        // fixed depth of 16 so the program ends with a well-formed output stack.
136        assert_eq!(output.stack.get_element(0), Some(crate::Felt::from_u32(3)));
137    }
138
139    #[test]
140    fn program_executor_reports_invalid_advice_inputs() {
141        let advice_inputs =
142            AdviceInputs::default().with_map([(crate::Word::default(), vec![crate::Felt::ONE])]);
143        let options = ExecutionOptions::default().with_max_advice_size_bytes(0);
144
145        let result =
146            <FastProcessor as ProgramExecutor>::new(StackInputs::default(), advice_inputs, options);
147
148        assert!(result.is_err());
149    }
150
151    #[tokio::test(flavor = "current_thread")]
152    async fn program_executor_default_falls_back_when_no_source_node() {
153        let program = Assembler::default()
154            .assemble_program("program", "begin push.3 swap drop end")
155            .unwrap()
156            .unwrap_program();
157
158        // `execute_with_package_debug_info` overrides only to route to the package-debug path;
159        // without an entrypoint node it still executes and returns the same stack.
160        let processor = <FastProcessor as ProgramExecutor>::new(
161            StackInputs::default(),
162            AdviceInputs::default(),
163            ExecutionOptions::default(),
164        )
165        .unwrap();
166        let processor = <FastProcessor as ProgramExecutor>::with_debug_info(
167            processor,
168            PackageDebugInfo::default(),
169        );
170        let output = <FastProcessor as ProgramExecutor>::execute(
171            processor,
172            &program,
173            &mut DefaultHost::default(),
174        )
175        .await
176        .unwrap();
177
178        assert_eq!(output.stack.get_element(0), Some(crate::Felt::from_u32(3)));
179    }
180}