forge/brutalizer/
visitor.rs1use std::ops::ControlFlow;
2
3use solar::ast::{CallArgsKind, Expr, ExprKind, ItemFunction, SourceUnit, Type, visit::Visit};
4
5use super::{
6 assembly::assembly_transforms,
7 transform::{Transform, span_text},
8 value::{brutalize_cast, brutalize_payable_address, deterministic_mask},
9};
10
11pub(super) fn collect_transforms<'ast>(
12 source: &str,
13 ast: &'ast SourceUnit<'ast>,
14) -> Vec<Transform> {
15 let mut visitor = BrutalizerVisitor::new(source);
16 let _ = visitor.visit_source_unit(ast);
17 visitor.transforms
18}
19
20struct BrutalizerVisitor<'src> {
21 transforms: Vec<Transform>,
22 source: &'src str,
23}
24
25impl<'src> BrutalizerVisitor<'src> {
26 const fn new(source: &'src str) -> Self {
27 Self { transforms: Vec::new(), source }
28 }
29}
30
31impl<'ast, 'src> Visit<'ast> for BrutalizerVisitor<'src> {
32 type BreakValue = ();
33
34 fn visit_expr(&mut self, expr: &'ast Expr<'ast>) -> ControlFlow<Self::BreakValue> {
35 if let Some((ty, arg_text)) = cast_call(self.source, expr) {
36 let mask = deterministic_mask(expr.span);
37 if let Some(replacement) = brutalize_cast(ty, arg_text, &mask) {
38 self.transforms.push(Transform::Replace { span: expr.span, replacement });
39 return ControlFlow::Continue(());
40 }
41 }
42 if let Some(arg_text) = payable_call(self.source, expr) {
43 let mask = deterministic_mask(expr.span);
44 let replacement = brutalize_payable_address(arg_text, &mask);
45 self.transforms.push(Transform::Replace { span: expr.span, replacement });
46 return ControlFlow::Continue(());
47 }
48
49 self.walk_expr(expr)
50 }
51
52 fn visit_item_function(
53 &mut self,
54 func: &'ast ItemFunction<'ast>,
55 ) -> ControlFlow<Self::BreakValue> {
56 self.transforms.extend(assembly_transforms(func));
57 self.walk_item_function(func)
58 }
59}
60
61fn payable_call<'src>(source: &'src str, expr: &Expr<'_>) -> Option<&'src str> {
62 let ExprKind::Payable(call_args) = &expr.kind else { return None };
63 let CallArgsKind::Unnamed(args_exprs) = &call_args.kind else { return None };
64 let arg_text = span_text(source, args_exprs.first()?.span)?;
65 (!arg_text.is_empty()).then_some(arg_text)
66}
67
68fn cast_call<'ast, 'src>(
69 source: &'src str,
70 expr: &'ast Expr<'ast>,
71) -> Option<(&'ast Type<'ast>, &'src str)> {
72 let ExprKind::Call(callee, call_args) = &expr.kind else { return None };
73 let ExprKind::Type(ty) = &callee.kind else { return None };
74 let CallArgsKind::Unnamed(args_exprs) = &call_args.kind else { return None };
75 let arg_text = span_text(source, args_exprs.first()?.span)?;
76 (!arg_text.is_empty()).then_some((ty, arg_text))
77}