1use std::fmt;
2
3use anyhow::Result;
4use num_bigint::BigInt;
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8use crate::debug_info::DebugInfo;
9use crate::extensions::NamedLibfunc;
10use crate::extensions::gas::{
11 BuiltinCostWithdrawGasLibfunc, RedepositGasLibfunc, WithdrawGasLibfunc,
12};
13use crate::ids::{
14 ConcreteLibfuncId, ConcreteTypeId, FunctionId, GenericLibfuncId, GenericTypeId, UserTypeId,
15 VarId,
16};
17
18#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
22#[serde(untagged)]
23pub enum VersionedProgram {
24 V1 {
25 version: Version<1>,
26 #[serde(flatten)]
27 program: ProgramArtifact,
28 },
29}
30
31impl VersionedProgram {
32 pub fn v1(program: ProgramArtifact) -> Self {
33 Self::V1 { program, version: Version::<1> }
34 }
35}
36
37#[derive(Clone, Debug, Eq, PartialEq)]
38pub struct Version<const V: u8>;
39
40#[derive(Debug, Error)]
41#[error("Unsupported Sierra program version")]
42struct VersionError;
43
44impl<const V: u8> Serialize for Version<V> {
45 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
46 where
47 S: serde::Serializer,
48 {
49 serializer.serialize_u8(V)
50 }
51}
52
53impl<'de, const V: u8> Deserialize<'de> for Version<V> {
54 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
55 where
56 D: serde::Deserializer<'de>,
57 {
58 let value = u8::deserialize(deserializer)?;
59 if value == V { Ok(Version::<V>) } else { Err(serde::de::Error::custom(VersionError)) }
60 }
61}
62
63impl From<ProgramArtifact> for VersionedProgram {
64 fn from(value: ProgramArtifact) -> Self {
65 VersionedProgram::v1(value)
66 }
67}
68
69impl VersionedProgram {
70 pub fn into_v1(self) -> Result<ProgramArtifact> {
71 match self {
72 VersionedProgram::V1 { program, .. } => Ok(program),
73 }
74 }
75}
76
77impl fmt::Display for VersionedProgram {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 match self {
80 VersionedProgram::V1 { program, .. } => fmt::Display::fmt(program, f),
81 }
82 }
83}
84
85#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
89pub struct ProgramArtifact {
90 #[serde(flatten)]
92 pub program: Program,
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub debug_info: Option<DebugInfo>,
96}
97
98impl ProgramArtifact {
99 pub fn stripped(program: Program) -> Self {
101 Self { program, debug_info: None }
102 }
103
104 pub fn with_debug_info(self, debug_info: DebugInfo) -> Self {
106 Self { program: self.program, debug_info: Some(debug_info) }
107 }
108}
109
110impl fmt::Display for ProgramArtifact {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 fmt::Display::fmt(&self.program, f)
113 }
114}
115
116#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
118pub struct Program {
119 pub type_declarations: Vec<TypeDeclaration>,
121 pub libfunc_declarations: Vec<LibfuncDeclaration>,
123 pub statements: Vec<Statement>,
125 pub funcs: Vec<Function>,
127}
128impl Program {
129 pub fn get_statement(&self, id: &StatementIdx) -> Option<&Statement> {
130 self.statements.get(id.0)
131 }
132
133 pub fn into_artifact(self) -> VersionedProgram {
135 ProgramArtifact::stripped(self).into()
136 }
137}
138
139#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
141pub struct TypeDeclaration {
142 pub id: ConcreteTypeId,
144 pub long_id: ConcreteTypeLongId,
145 pub declared_type_info: Option<DeclaredTypeInfo>,
146}
147
148#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
150pub struct DeclaredTypeInfo {
151 pub storable: bool,
153 pub droppable: bool,
155 pub duplicatable: bool,
157 pub zero_sized: bool,
159}
160
161#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
163pub struct ConcreteTypeLongId {
164 pub generic_id: GenericTypeId,
166 pub generic_args: Vec<GenericArg>,
168}
169
170#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
172pub struct LibfuncDeclaration {
173 pub id: ConcreteLibfuncId,
175 pub long_id: ConcreteLibfuncLongId,
176}
177
178#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
180pub struct ConcreteLibfuncLongId {
181 pub generic_id: GenericLibfuncId,
183 pub generic_args: Vec<GenericArg>,
185}
186
187#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
189pub struct FunctionSignature {
190 pub param_types: Vec<ConcreteTypeId>,
192 pub ret_types: Vec<ConcreteTypeId>,
194}
195
196#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
198pub struct GenFunction<StatementId> {
199 pub id: FunctionId,
201 pub signature: FunctionSignature,
203 pub params: Vec<Param>,
207 pub entry_point: StatementId,
209}
210
211impl<StatementId> GenFunction<StatementId> {
212 pub fn new(
213 id: FunctionId,
214 params: Vec<Param>,
215 ret_types: Vec<ConcreteTypeId>,
216 entry_point: StatementId,
217 ) -> Self {
218 let param_types: Vec<_> = params.iter().map(|Param { id: _, ty }| ty.clone()).collect();
219 GenFunction {
220 id,
221 signature: FunctionSignature { param_types, ret_types },
222 params,
223 entry_point,
224 }
225 }
226}
227
228#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
230pub struct Param {
231 pub id: VarId,
232 pub ty: ConcreteTypeId,
233}
234
235#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, PartialOrd, Ord)]
237pub struct StatementIdx(pub usize);
238impl StatementIdx {
239 pub fn next(&self, target: &BranchTarget) -> StatementIdx {
240 match target {
241 BranchTarget::Fallthrough => StatementIdx(self.0 + 1),
242 BranchTarget::Statement(id) => *id,
243 }
244 }
245}
246
247#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
249pub enum GenericArg {
250 UserType(UserTypeId),
251 Type(ConcreteTypeId),
252 Value(BigInt),
253 UserFunc(FunctionId),
254 Libfunc(ConcreteLibfuncId),
255}
256
257#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
259pub enum GenStatement<StatementId> {
260 Invocation(GenInvocation<StatementId>),
261 Return(Vec<VarId>),
262}
263impl<StatementId> GenStatement<StatementId> {
264 pub fn map<T>(self, f: impl Fn(StatementId) -> T) -> GenStatement<T> {
265 match self {
266 GenStatement::Invocation(invocation) => GenStatement::Invocation(GenInvocation {
267 libfunc_id: invocation.libfunc_id,
268 args: invocation.args.clone(),
269 branches: invocation
270 .branches
271 .into_iter()
272 .map(|branch| GenBranchInfo {
273 target: branch.target.map(&f),
274 results: branch.results.clone(),
275 })
276 .collect(),
277 }),
278 GenStatement::Return(results) => GenStatement::Return(results.clone()),
279 }
280 }
281}
282
283#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
285pub struct GenInvocation<StatementId> {
286 pub libfunc_id: ConcreteLibfuncId,
288 pub args: Vec<VarId>,
290 pub branches: Vec<GenBranchInfo<StatementId>>,
293}
294
295#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
297pub struct GenBranchInfo<StatementId> {
298 pub target: GenBranchTarget<StatementId>,
300 pub results: Vec<VarId>,
302}
303
304#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
305pub enum GenBranchTarget<StatementId> {
306 Fallthrough,
308 Statement(StatementId),
310}
311impl<StatementId> GenBranchTarget<StatementId> {
312 pub fn map<T>(self, f: impl Fn(StatementId) -> T) -> GenBranchTarget<T> {
313 match self {
314 GenBranchTarget::Fallthrough => GenBranchTarget::Fallthrough,
315 GenBranchTarget::Statement(id) => GenBranchTarget::Statement(f(id)),
316 }
317 }
318}
319
320pub type Function = GenFunction<StatementIdx>;
321pub type Statement = GenStatement<StatementIdx>;
322pub type Invocation = GenInvocation<StatementIdx>;
323pub type BranchInfo = GenBranchInfo<StatementIdx>;
324pub type BranchTarget = GenBranchTarget<StatementIdx>;
325
326impl Program {
327 pub fn requires_gas_counter(&self) -> bool {
331 self.libfunc_declarations.iter().any(|decl| {
332 matches!(
333 decl.long_id.generic_id.0.as_str(),
334 WithdrawGasLibfunc::STR_ID
335 | BuiltinCostWithdrawGasLibfunc::STR_ID
336 | RedepositGasLibfunc::STR_ID
337 )
338 })
339 }
340}