1use std::{num::ParseIntError, str::FromStr, sync::Arc};
2
3use cairo_lang_sierra::{
4 extensions::{
5 circuit::CircuitTypeConcrete,
6 core::{CoreLibfunc, CoreType, CoreTypeConcrete},
7 starknet::StarknetTypeConcrete,
8 },
9 ids::ConcreteTypeId,
10 program::{GenFunction, Program, StatementIdx},
11 program_registry::ProgramRegistry,
12};
13use debug::type_to_name;
14use starknet::StubSyscallHandler;
15
16pub use self::{dump::*, gas::BuiltinCosts, value::*, vm::VirtualMachine};
17
18mod debug;
19mod dump;
20mod gas;
21pub mod starknet;
22mod test_utils;
23mod utils;
24mod value;
25mod vm;
26
27#[derive(Clone, Debug)]
28pub enum EntryPoint {
29 Number(u64),
30 String(String),
31}
32
33impl FromStr for EntryPoint {
34 type Err = ParseIntError;
35
36 fn from_str(s: &str) -> Result<Self, Self::Err> {
37 Ok(match s.chars().next() {
38 Some(x) if x.is_numeric() => Self::Number(s.parse()?),
39 _ => Self::String(s.to_string()),
40 })
41 }
42}
43
44pub fn find_entry_point_by_idx(
45 program: &Program,
46 entry_point_idx: usize,
47) -> Option<&GenFunction<StatementIdx>> {
48 program
49 .funcs
50 .iter()
51 .find(|x| x.id.id == entry_point_idx as u64)
52}
53
54pub fn find_entry_point_by_name<'a>(
55 program: &'a Program,
56 name: &str,
57) -> Option<&'a GenFunction<StatementIdx>> {
58 program
59 .funcs
60 .iter()
61 .find(|x| x.id.debug_name.as_ref().map(|x| x.as_str()) == Some(name))
62}
63
64pub fn find_real_type(
68 registry: &ProgramRegistry<CoreType, CoreLibfunc>,
69 ty: &ConcreteTypeId,
70) -> ConcreteTypeId {
71 match registry.get_type(ty).unwrap() {
72 cairo_lang_sierra::extensions::core::CoreTypeConcrete::Box(info) => {
73 find_real_type(registry, &info.ty)
74 }
75 cairo_lang_sierra::extensions::core::CoreTypeConcrete::Uninitialized(info) => {
76 find_real_type(registry, &info.ty)
77 }
78 cairo_lang_sierra::extensions::core::CoreTypeConcrete::Span(info) => {
79 find_real_type(registry, &info.ty)
80 }
81 cairo_lang_sierra::extensions::core::CoreTypeConcrete::Snapshot(info) => {
82 find_real_type(registry, &info.ty)
83 }
84 _ => ty.clone(),
85 }
86}
87
88pub fn run_program(
89 program: Arc<Program>,
90 entry_point: EntryPoint,
91 args: Vec<String>,
92 available_gas: u64,
93) -> ProgramTrace {
94 let mut vm = VirtualMachine::new(program.clone());
95
96 let function = program
97 .funcs
98 .iter()
99 .find(|f| match &entry_point {
100 EntryPoint::Number(x) => f.id.id == *x,
101 EntryPoint::String(x) => f.id.debug_name.as_deref() == Some(x.as_str()),
102 })
103 .unwrap();
104
105 let mut iter = args.into_iter();
106 vm.push_frame(
107 function.id.clone(),
108 function
109 .signature
110 .param_types
111 .iter()
112 .map(|type_id| {
113 let type_info = vm.registry().get_type(type_id).unwrap();
114 match type_info {
115 CoreTypeConcrete::Felt252(_) => Value::parse_felt(&iter.next().unwrap()),
116 CoreTypeConcrete::GasBuiltin(_) => Value::U64(available_gas),
117 CoreTypeConcrete::RangeCheck(_)
118 | CoreTypeConcrete::RangeCheck96(_)
119 | CoreTypeConcrete::Bitwise(_)
120 | CoreTypeConcrete::Pedersen(_)
121 | CoreTypeConcrete::Poseidon(_)
122 | CoreTypeConcrete::SegmentArena(_)
123 | CoreTypeConcrete::Circuit(
124 CircuitTypeConcrete::AddMod(_) | CircuitTypeConcrete::MulMod(_),
125 ) => Value::Unit,
126 CoreTypeConcrete::Starknet(inner) => match inner {
127 StarknetTypeConcrete::System(_) => Value::Unit,
128 StarknetTypeConcrete::ClassHash(_)
129 | StarknetTypeConcrete::ContractAddress(_)
130 | StarknetTypeConcrete::StorageBaseAddress(_)
131 | StarknetTypeConcrete::StorageAddress(_) => {
132 Value::parse_felt(&iter.next().unwrap())
133 }
134 _ => todo!(),
135 },
136 CoreTypeConcrete::EcOp(_) => Value::Unit,
137 _ => todo!("{}", type_to_name(type_id, vm.registry())),
138 }
139 })
140 .collect::<Vec<_>>(),
141 );
142
143 let syscall_handler = &mut StubSyscallHandler::default();
144
145 vm.run_with_trace(syscall_handler)
146}