From 164d47ec88d3026925002ae2c2f3f231b8a591ab Mon Sep 17 00:00:00 2001 From: brock elmore Date: Thu, 18 Jul 2024 17:01:12 -0700 Subject: [PATCH] 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(()) + } }) } }