customasm/expr/
inspect.rs

1use 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}