1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use std::collections::HashMap;
use cairo_lang_casm::ap_change::ApplyApChange;
use cairo_lang_casm::cell_expression::CellExpression;
use cairo_lang_casm::operand::{CellRef, Register};
use cairo_lang_sierra::ids::{ConcreteTypeId, VarId};
use cairo_lang_sierra::program::Function;
use thiserror::Error;
use {cairo_lang_casm, cairo_lang_sierra};
use crate::invocations::InvocationError;
use crate::type_sizes::TypeSizeMap;
#[derive(Error, Debug, Eq, PartialEq)]
pub enum ReferencesError {
#[error("Invalid function declaration.")]
InvalidFunctionDeclaration(Function),
#[error(
"One of the arguments does not match the expected type of the libfunc or return statement."
)]
InvalidReferenceTypeForArgument,
}
pub type StatementRefs = HashMap<VarId, ReferenceValue>;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ReferenceValue {
pub expression: ReferenceExpression,
pub ty: ConcreteTypeId,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ReferenceExpression {
pub cells: Vec<CellExpression>,
}
impl ReferenceExpression {
pub fn from_cell(cell_expr: CellExpression) -> Self {
Self { cells: vec![cell_expr] }
}
pub fn try_unpack<const SIZE: usize>(
&self,
) -> Result<&[CellExpression; SIZE], InvocationError> {
<&[CellExpression; SIZE]>::try_from(&self.cells[..])
.map_err(|_| InvocationError::InvalidReferenceExpressionForArgument)
}
pub fn try_unpack_single(&self) -> Result<&CellExpression, InvocationError> {
Ok(&self.try_unpack::<1>()?[0])
}
}
impl ApplyApChange for ReferenceExpression {
fn apply_known_ap_change(self, ap_change: usize) -> Option<Self> {
Some(ReferenceExpression {
cells: self
.cells
.into_iter()
.map(|cell_expr| cell_expr.apply_known_ap_change(ap_change))
.collect::<Option<Vec<_>>>()?,
})
}
fn can_apply_unknown(&self) -> bool {
self.cells.iter().all(|cell| cell.can_apply_unknown())
}
}
pub fn build_function_arguments_refs(
func: &Function,
type_sizes: &TypeSizeMap,
) -> Result<StatementRefs, ReferencesError> {
let mut refs = HashMap::with_capacity(func.params.len());
let mut offset = -3_i16;
for param in func.params.iter().rev() {
let size = type_sizes
.get(¶m.ty)
.ok_or_else(|| ReferencesError::InvalidFunctionDeclaration(func.clone()))?;
if refs
.insert(
param.id.clone(),
ReferenceValue {
expression: ReferenceExpression {
cells: ((offset - size + 1)..(offset + 1))
.map(|i| {
CellExpression::Deref(CellRef { register: Register::FP, offset: i })
})
.collect(),
},
ty: param.ty.clone(),
},
)
.is_some()
{
return Err(ReferencesError::InvalidFunctionDeclaration(func.clone()));
}
offset -= size;
}
Ok(refs)
}
pub fn check_types_match(
refs: &[ReferenceValue],
types: &[ConcreteTypeId],
) -> Result<(), ReferencesError> {
if itertools::equal(types.iter(), refs.iter().map(|r| &r.ty)) {
Ok(())
} else {
Err(ReferencesError::InvalidReferenceTypeForArgument)
}
}