customasm/expr/
inspect.rs1use crate::*;
2
3
4pub struct StaticallyKnownProvider<'a>
5{
6 pub locals: std::collections::HashMap<String, StaticallyKnownLocal>,
7 pub query_variable: &'a dyn Fn(&StaticallyKnownVariableQuery) -> bool,
8 pub query_function: &'a dyn Fn(&StaticallyKnownFunctionQuery) -> bool,
9}
10
11
12pub struct StaticallyKnownVariableQuery<'a>
13{
14 pub hierarchy_level: usize,
15 pub hierarchy: &'a Vec<String>,
16}
17
18
19pub struct StaticallyKnownFunctionQuery<'a>
20{
21 pub func: &'a str,
22 pub args: &'a Vec<expr::Expr>,
23}
24
25
26impl<'a> StaticallyKnownProvider<'a>
27{
28 pub fn new() -> StaticallyKnownProvider<'a>
29 {
30 StaticallyKnownProvider {
31 locals: std::collections::HashMap::new(),
32 query_variable: &|_| false,
33 query_function: &|_| false,
34 }
35 }
36}
37
38
39pub struct StaticallyKnownLocal
40{
41 pub size: Option<usize>,
42 pub value_known: bool,
43}
44
45
46impl StaticallyKnownLocal
47{
48 pub fn new() -> StaticallyKnownLocal
49 {
50 StaticallyKnownLocal {
51 size: None,
52 value_known: false,
53 }
54 }
55}
56
57
58impl expr::Expr
59{
60 pub fn get_static_size(
61 &self,
62 provider: &StaticallyKnownProvider)
63 -> Option<usize>
64 {
65 match self
66 {
67 expr::Expr::Variable(_, hierarchy_level, ref hierarchy) =>
68 {
69 if *hierarchy_level != 0 ||
70 hierarchy.len() != 1
71 {
72 return None;
73 }
74
75 if let Some(StaticallyKnownLocal { size: Some(size), .. }) =
76 provider.locals.get(&hierarchy[0])
77 {
78 return Some(*size);
79 }
80
81 None
82 }
83
84 expr::Expr::Literal(_, expr::Value::Integer(util::BigInt { size: Some(size), .. })) =>
85 Some(*size),
86
87 expr::Expr::Literal(..) => None,
88
89 expr::Expr::UnaryOp(..) => None,
90
91 expr::Expr::BinaryOp(_, _, expr::BinaryOp::Concat, ref lhs, ref rhs) =>
92 {
93 let lhs_size = lhs.get_static_size(provider)?;
94 let rhs_size = rhs.get_static_size(provider)?;
95
96 Some(lhs_size + rhs_size)
97 }
98
99 expr::Expr::BinaryOp(..) => None,
100
101 expr::Expr::Slice(_, _, left_expr, right_expr, _) =>
102 {
103 let left = left_expr.try_eval_usize()? + 1;
104 let right = right_expr.try_eval_usize()?;
105
106 if right > left
107 {
108 return None;
109 }
110
111 Some(left - right)
112 }
113
114 expr::Expr::SliceShort(_, _, size_expr, _) =>
115 {
116 let size = size_expr.try_eval_usize()?;
117
118 Some(size)
119 }
120
121 expr::Expr::TernaryOp(_, _, ref true_branch, ref false_branch) =>
122 {
123 let true_size = true_branch.get_static_size(provider)?;
124 let false_size = false_branch.get_static_size(provider)?;
125
126 if true_size == false_size
127 {
128 Some(true_size)
129 }
130 else
131 {
132 None
133 }
134 }
135
136 expr::Expr::Block(_, ref exprs) =>
137 exprs.last()?.get_static_size(provider),
138
139 expr::Expr::Call(_, func, args) =>
140 {
141 if let expr::Expr::Variable(_, 0, ref names) = *func.as_ref()
142 {
143 if names.len() == 1
144 {
145 expr::get_static_size_builtin_fn(
146 &names[0],
147 provider,
148 &args)
149 }
150 else
151 {
152 None
153 }
154 }
155 else
156 {
157 None
158 }
159 }
160
161 expr::Expr::Asm(..) => None,
162 }
163 }
164
165
166 pub fn is_value_statically_known(
167 &self,
168 provider: &StaticallyKnownProvider)
169 -> bool
170 {
171 match self
172 {
173 expr::Expr::Variable(_, hierarchy_level, ref hierarchy) =>
174 {
175 if *hierarchy_level == 0 && hierarchy.len() == 1
176 {
177 if let Some(var) = provider.locals.get(&hierarchy[0])
178 {
179 return var.value_known;
180 }
181 }
182
183 let query = StaticallyKnownVariableQuery {
184 hierarchy,
185 hierarchy_level: *hierarchy_level,
186 };
187
188 (provider.query_variable)(&query)
189 }
190
191 expr::Expr::Literal(_, _) => true,
192
193 expr::Expr::UnaryOp(..) => false,
194
195 expr::Expr::BinaryOp(_, _, _, ref lhs, ref rhs) =>
196 {
197 let lhs_known = lhs.is_value_statically_known(provider);
198 let rhs_known = rhs.is_value_statically_known(provider);
199
200 lhs_known && rhs_known
201 }
202
203 expr::Expr::Slice(_, _, ref left_expr, ref right_expr, ref expr) =>
204 left_expr.is_value_statically_known(provider) &&
205 right_expr.is_value_statically_known(provider) &&
206 expr.is_value_statically_known(provider),
207
208 expr::Expr::SliceShort(_, _, ref size_expr, ref expr) =>
209 size_expr.is_value_statically_known(provider) &&
210 expr.is_value_statically_known(provider),
211
212 expr::Expr::TernaryOp(_, ref condition, ref true_branch, ref false_branch) =>
213 {
214 let condition_known = condition.is_value_statically_known(provider);
215 let true_known = true_branch.is_value_statically_known(provider);
216 let false_known = false_branch.is_value_statically_known(provider);
217
218 condition_known && true_known && false_known
219 }
220
221 expr::Expr::Block(_, ref exprs) =>
222 {
223 for expr in exprs
224 {
225 if !expr.is_value_statically_known(provider)
226 {
227 return false;
228 }
229 }
230
231 true
232 }
233
234 expr::Expr::Call(_, func, args) =>
235 {
236 if let expr::Expr::Variable(_, 0, ref names) = *func.as_ref()
237 {
238 for arg in args
239 {
240 if !arg.is_value_statically_known(provider)
241 {
242 return false;
243 }
244 }
245
246 if names.len() == 1
247 {
248 if expr::get_statically_known_value_builtin_fn(
249 &names[0],
250 &args)
251 {
252 return true;
253 }
254
255 let query = StaticallyKnownFunctionQuery {
256 func: &names[0],
257 args,
258 };
259
260 (provider.query_function)(&query)
261 }
262 else
263 {
264 false
265 }
266 }
267 else
268 {
269 false
270 }
271 }
272
273 expr::Expr::Asm(..) => false,
274 }
275 }
276
277
278 pub fn returned_value_span(&self) -> diagn::Span
279 {
280 match self
281 {
282 &expr::Expr::Block(span, ref exprs) =>
283 {
284 match exprs.last()
285 {
286 None => span,
287 Some(expr) => expr.returned_value_span()
288 }
289 }
290
291 _ => self.span()
292 }
293 }
294}