cairo_lang_sierra/simulation/
mod.rs1use std::collections::HashMap;
2
3use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
4use itertools::izip;
5use thiserror::Error;
6
7use self::value::CoreValue;
8use crate::edit_state::{EditStateError, put_results, take_args};
9use crate::extensions::core::{CoreConcreteLibfunc, CoreLibfunc, CoreType};
10use crate::ids::{FunctionId, VarId};
11use crate::program::{Program, Statement, StatementIdx};
12use crate::program_registry::{ProgramRegistry, ProgramRegistryError};
13
14pub mod core;
15#[cfg(test)]
16mod test;
17pub mod value;
18
19#[derive(Error, Debug, Eq, PartialEq)]
21pub enum LibfuncSimulationError {
22 #[error("Expected different number of arguments")]
23 WrongNumberOfArgs,
24 #[error("Expected a different type of an argument")]
25 WrongArgType,
26 #[error("Could not resolve requested symbol value")]
27 UnresolvedStatementGasInfo,
28 #[error("Error occurred during user function call")]
29 FunctionSimulationError(FunctionId, Box<SimulationError>),
30}
31
32#[derive(Error, Debug, Eq, PartialEq)]
34pub enum SimulationError {
35 #[error("error from the program registry")]
36 ProgramRegistryError(#[from] Box<ProgramRegistryError>),
37 #[error("error from editing a variable state")]
38 EditStateError(EditStateError, StatementIdx),
39 #[error("error from simulating a libfunc")]
40 LibfuncSimulationError(LibfuncSimulationError, StatementIdx),
41 #[error("jumped out of bounds during simulation")]
42 StatementOutOfBounds(StatementIdx),
43 #[error("unexpected number of arguments to function")]
44 FunctionArgumentCountMismatch { function_id: FunctionId, expected: usize, actual: usize },
45 #[error("identifiers left at function return")]
46 FunctionDidNotConsumeAllArgs(FunctionId, StatementIdx),
47}
48
49pub fn run(
51 program: &Program,
52 statement_gas_info: &HashMap<StatementIdx, i64>,
53 function_id: &FunctionId,
54 inputs: Vec<CoreValue>,
55) -> Result<Vec<CoreValue>, SimulationError> {
56 let context = SimulationContext {
57 program,
58 statement_gas_info,
59 registry: &ProgramRegistry::new(program)?,
60 };
61 context.simulate_function(function_id, inputs)
62}
63
64struct SimulationContext<'a> {
66 pub program: &'a Program,
67 pub statement_gas_info: &'a HashMap<StatementIdx, i64>,
68 pub registry: &'a ProgramRegistry<CoreType, CoreLibfunc>,
69}
70impl SimulationContext<'_> {
71 fn simulate_function(
73 &self,
74 function_id: &FunctionId,
75 inputs: Vec<CoreValue>,
76 ) -> Result<Vec<CoreValue>, SimulationError> {
77 let func = self.registry.get_function(function_id)?;
78 let mut current_statement_id = func.entry_point;
79 if func.params.len() != inputs.len() {
80 return Err(SimulationError::FunctionArgumentCountMismatch {
81 function_id: func.id.clone(),
82 expected: func.params.len(),
83 actual: inputs.len(),
84 });
85 }
86 let mut state = OrderedHashMap::<VarId, CoreValue>::from_iter(
87 izip!(func.params.iter(), inputs).map(|(param, input)| (param.id.clone(), input)),
88 );
89 loop {
90 let statement = self
91 .program
92 .get_statement(¤t_statement_id)
93 .ok_or(SimulationError::StatementOutOfBounds(current_statement_id))?;
94 match statement {
95 Statement::Return(ids) => {
96 let (remaining, outputs) = take_args(state, ids.iter()).map_err(|error| {
97 SimulationError::EditStateError(error, current_statement_id)
98 })?;
99 return if remaining.is_empty() {
100 Ok(outputs)
101 } else {
102 Err(SimulationError::FunctionDidNotConsumeAllArgs(
103 func.id.clone(),
104 current_statement_id,
105 ))
106 };
107 }
108 Statement::Invocation(invocation) => {
109 let (remaining, inputs) =
110 take_args(state, invocation.args.iter()).map_err(|error| {
111 SimulationError::EditStateError(error, current_statement_id)
112 })?;
113 let libfunc = self.registry.get_libfunc(&invocation.libfunc_id)?;
114 let (outputs, chosen_branch) = self.simulate_libfunc(
115 ¤t_statement_id,
116 libfunc,
117 inputs,
118 current_statement_id,
119 )?;
120 let branch_info = &invocation.branches[chosen_branch];
121 state = put_results(remaining, izip!(branch_info.results.iter(), outputs))
122 .map_err(|error| {
123 SimulationError::EditStateError(error, current_statement_id)
124 })?;
125 current_statement_id = current_statement_id.next(&branch_info.target);
126 }
127 }
128 }
129 }
130 fn simulate_libfunc(
133 &self,
134 idx: &StatementIdx,
135 libfunc: &CoreConcreteLibfunc,
136 inputs: Vec<CoreValue>,
137 current_statement_id: StatementIdx,
138 ) -> Result<(Vec<CoreValue>, usize), SimulationError> {
139 core::simulate(
140 libfunc,
141 inputs,
142 || self.statement_gas_info.get(idx).copied(),
143 |function_id, inputs| {
144 self.simulate_function(function_id, inputs).map_err(|error| {
145 LibfuncSimulationError::FunctionSimulationError(
146 function_id.clone(),
147 Box::new(error),
148 )
149 })
150 },
151 )
152 .map_err(|error| SimulationError::LibfuncSimulationError(error, current_statement_id))
153 }
154}