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 15ed452c..29316aa9 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/abi.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/abi.rs @@ -1,11 +1,7 @@ -use crate::{ - ContextBuilder, ExprErr, IntoExprErr, -}; +use crate::{ContextBuilder, ExprErr, IntoExprErr}; use graph::{ - nodes::{ - Builtin, ContextNode, ContextVar, ExprRet, - }, + nodes::{Builtin, ContextNode, ContextVar, ExprRet}, AnalyzerBackend, ContextEdge, Edge, Node, }; @@ -13,116 +9,102 @@ use solang_parser::pt::{Expression, Loc}; impl AbiCaller for T where T: AnalyzerBackend + Sized {} pub trait AbiCaller: AnalyzerBackend + Sized { - fn abi_call(&mut self, func_name: String, input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { + fn abi_call( + &mut self, + func_name: String, + input_exprs: &[Expression], + 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(expect_builtin) => { - match analyzer.node(expect_builtin) { - Node::Builtin(_) => { - let var = ContextVar::new_from_builtin( - *loc, - expect_builtin.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(()) - } - 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), - } - } - self.parse_ctx_expr(&input_exprs[1], ctx)?; - self.apply_to_edges(ctx, loc, &|analyzer, 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" => { - // currently we dont support concrete abi encoding, TODO - 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)); - 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( + "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(expect_builtin) => match analyzer.node(expect_builtin) { + Node::Builtin(_) => { + let var = ContextVar::new_from_builtin( + *loc, + expect_builtin.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(()) + } + 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), + } + } + self.parse_ctx_expr(&input_exprs[1], ctx)?; + self.apply_to_edges(ctx, loc, &|analyzer, 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" => { + // currently we dont support concrete abi encoding, TODO + 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)); + 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), - ) - )) - } - } -} \ No newline at end of file + ), + )), + } + } +} 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 af687a35..67fbf8d1 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs @@ -1,11 +1,7 @@ -use crate::{ - ExprErr, IntoExprErr, -}; +use crate::{ExprErr, IntoExprErr}; use graph::{ - nodes::{ - Builtin, ContextNode, ContextVar, ExprRet, - }, + nodes::{Builtin, ContextNode, ContextVar, ExprRet}, AnalyzerBackend, ContextEdge, Edge, Node, }; @@ -13,67 +9,67 @@ use solang_parser::pt::{Expression, Loc}; impl AddressCaller for T where T: AnalyzerBackend + Sized {} pub trait AddressCaller: AnalyzerBackend + Sized { - fn address_call(&mut self, func_name: String, _input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { + fn address_call( + &mut self, + func_name: String, + _input_exprs: &[Expression], + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { match &*func_name { - "delegatecall" | "staticcall" | "call" => { - // 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)); - ctx.add_var(bool_node.into(), self).into_expr_err(loc)?; - self.add_edge(bool_node, ctx, Edge::Context(ContextEdge::Variable)); + "delegatecall" | "staticcall" | "call" => { + // 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)); + 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)); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr( - ExprRet::Multi(vec![ - ExprRet::Single(bool_node), - ExprRet::Single(node), - ]), - self, - ) - .into_expr_err(loc)?; - Ok(()) - } - "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)); - 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(()) - } - "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(Node::ContextVar(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( + 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)); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr( + ExprRet::Multi(vec![ExprRet::Single(bool_node), ExprRet::Single(node)]), + self, + ) + .into_expr_err(loc)?; + Ok(()) + } + "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)); + 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(()) + } + "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(Node::ContextVar(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 builtin address function: \"{func_name}\", context: {}", ctx.path(self), - ) - )) - } - } -} \ No newline at end of file + ), + )), + } + } +} 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 c1ede935..58a93628 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/array.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/array.rs @@ -1,12 +1,8 @@ -use crate::{ - array::Array, ContextBuilder, ExprErr, IntoExprErr, ListAccess, -}; +use crate::{array::Array, ContextBuilder, ExprErr, IntoExprErr, ListAccess}; use graph::{ elem::*, - nodes::{ - Concrete, ContextNode, ContextVarNode, ExprRet, - }, + nodes::{Concrete, ContextNode, ContextVarNode, ExprRet}, AnalyzerBackend, }; @@ -15,7 +11,13 @@ use solang_parser::pt::{Expression, Loc}; impl ArrayCaller for T where T: AnalyzerBackend + Sized {} pub trait ArrayCaller: AnalyzerBackend + Sized { - fn array_call(&mut self, func_name: String, input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { + fn array_call( + &mut self, + func_name: String, + input_exprs: &[Expression], + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { match &*func_name { "push" => { if input_exprs.len() == 1 { @@ -23,13 +25,11 @@ pub trait ArrayCaller: AnalyzerBackend + S // empty element onto the expr ret stack self.parse_ctx_expr(&input_exprs[0], ctx)?; self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { - let Some(array) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(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(), + "array[].push(..) was not an array to push to".to_string(), )); }; @@ -38,8 +38,7 @@ pub trait ArrayCaller: AnalyzerBackend + S // get length let len = analyzer.tmp_length(arr, ctx, loc); - let len_as_idx = - len.as_tmp(loc, ctx, analyzer).into_expr_err(loc)?; + let len_as_idx = len.as_tmp(loc, ctx, analyzer).into_expr_err(loc)?; // set length as index analyzer.index_into_array_inner( ctx, @@ -53,13 +52,11 @@ pub trait ArrayCaller: AnalyzerBackend + S // array.push(value) self.parse_ctx_expr(&input_exprs[0], ctx)?; self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { - let Some(array) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(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(), + "array[].push(..) was not an array to push to".to_string(), )); }; if matches!(array, ExprRet::CtxKilled(_)) { @@ -68,14 +65,12 @@ pub trait ArrayCaller: AnalyzerBackend + S } analyzer.parse_ctx_expr(&input_exprs[1], ctx)?; analyzer.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { - let Some(new_elem) = ctx - .pop_expr_latest(loc, analyzer) - .into_expr_err(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(), + "array[].push(..) was not given an element to push".to_string(), )); }; @@ -85,21 +80,17 @@ pub trait ArrayCaller: AnalyzerBackend + S } let arr = array.expect_single().into_expr_err(loc)?; - let arr = - ContextVarNode::from(arr).latest_version(analyzer); + let arr = ContextVarNode::from(arr).latest_version(analyzer); // get length let len = analyzer.tmp_length(arr, ctx, loc); - let len_as_idx = - len.as_tmp(loc, ctx, analyzer).into_expr_err(loc)?; + let len_as_idx = len.as_tmp(loc, ctx, analyzer).into_expr_err(loc)?; // set length as index analyzer.index_into_array_inner( ctx, loc, ExprRet::Single(arr.latest_version(analyzer).into()), - ExprRet::Single( - len_as_idx.latest_version(analyzer).into(), - ), + ExprRet::Single(len_as_idx.latest_version(analyzer).into()), )?; let index = ctx .pop_expr_latest(loc, analyzer) @@ -135,9 +126,7 @@ pub trait ArrayCaller: AnalyzerBackend + S } self.parse_ctx_expr(&input_exprs[0], ctx)?; self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { - let Some(array) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { + 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(), @@ -154,9 +143,7 @@ pub trait ArrayCaller: AnalyzerBackend + S // get length analyzer.match_length(ctx, loc, array, false)?; - let Some(len) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { + let Some(len) = 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(), @@ -191,10 +178,8 @@ pub trait ArrayCaller: AnalyzerBackend + S 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 5757b0ff..b29f08c1 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/block.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/block.rs @@ -1,11 +1,7 @@ -use crate::{ - ContextBuilder, ExprErr, IntoExprErr, -}; +use crate::{ContextBuilder, ExprErr, IntoExprErr}; use graph::{ - nodes::{ - Builtin, ContextNode, ContextVar, ExprRet, - }, + nodes::{Builtin, ContextNode, ContextVar, ExprRet}, AnalyzerBackend, Node, }; @@ -13,43 +9,46 @@ use solang_parser::pt::{Expression, Loc}; impl BlockCaller for T where T: AnalyzerBackend + Sized {} pub trait BlockCaller: AnalyzerBackend + Sized { - fn block_call(&mut self, func_name: String, input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { - match &*func_name { - "blockhash" => { - self.parse_ctx_expr(&input_exprs[0], ctx)?; - self.apply_to_edges(ctx, loc, &|analyzer, 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, - ) - .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) - } - _ => Err(ExprErr::FunctionNotFound( - loc, - format!( - "Could not find builtin block function: \"{func_name}\", context: {}", - ctx.path(self), - ) - )) - } - } -} \ No newline at end of file + fn block_call( + &mut self, + func_name: String, + input_exprs: &[Expression], + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { + match &*func_name { + "blockhash" => { + self.parse_ctx_expr(&input_exprs[0], ctx)?; + self.apply_to_edges(ctx, loc, &|analyzer, 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, + ) + .into_expr_err(loc)?; + let cvar = analyzer.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(cvar), analyzer) + .into_expr_err(loc)?; + Ok(()) + }) + } + _ => Err(ExprErr::FunctionNotFound( + loc, + format!( + "Could not find builtin block function: \"{func_name}\", context: {}", + ctx.path(self), + ), + )), + } + } +} 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 bfa88e5e..86bed531 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/constructors.rs @@ -1,197 +1,201 @@ -use crate::{ - ContextBuilder, ExprErr, FuncCaller, IntoExprErr, -}; +use crate::{ContextBuilder, ExprErr, FuncCaller, IntoExprErr}; use graph::{ elem::*, - nodes::{ - ContextNode, ContextVar, ContextVarNode, ExprRet, - StructNode, - }, + nodes::{ContextNode, ContextVar, ContextVarNode, ExprRet, StructNode}, AnalyzerBackend, ContextEdge, Edge, Node, Range, VarType, }; use shared::NodeIdx; use solang_parser::pt::{Expression, Loc}; -impl ConstructorCaller for T where T: AnalyzerBackend + Sized {} +impl ConstructorCaller for T where + T: AnalyzerBackend + Sized +{ +} pub trait ConstructorCaller: AnalyzerBackend + Sized { - fn construct_array(&mut self, func_idx: NodeIdx, input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { - // create a new list - self.parse_ctx_expr(&input_exprs[0], ctx)?; - self.apply_to_edges(ctx, loc, &|analyzer, 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(()); - } - 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, - 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, - is_symbolic: true, - is_return: false, - ty: ContextVarNode::from(len_cvar) - .underlying(analyzer) - .into_expr_err(loc)? - .ty - .clone(), - }; - - 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)); - - // update the length - if let Some(r) = arr.ref_range(analyzer).into_expr_err(loc)? { - let min = r.evaled_range_min(analyzer).into_expr_err(loc)?; - let max = r.evaled_range_max(analyzer).into_expr_err(loc)?; - - if let Some(mut rd) = min.maybe_range_dyn() { - rd.len = Elem::from(len_cvar); - arr.set_range_min(analyzer, Elem::ConcreteDyn(Box::new(rd))) - .into_expr_err(loc)?; - } - - if let Some(mut rd) = max.maybe_range_dyn() { - rd.len = Elem::from(len_cvar); - arr.set_range_min(analyzer, Elem::ConcreteDyn(Box::new(rd))) - .into_expr_err(loc)?; - } - } - - ctx.push_expr(ExprRet::Single(arr.into()), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) - } - - fn construct_contract(&mut self, func_idx: NodeIdx, input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { - // construct a new contract - if !input_exprs.is_empty() { - self.parse_ctx_expr(&input_exprs[0], ctx)?; - } - self.apply_to_edges(ctx, loc, &|analyzer, 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_struct(&mut self, func_idx: NodeIdx, input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> 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)); - - self.parse_inputs(ctx, loc, input_exprs)?; - self.apply_to_edges(ctx, loc, &|analyzer, 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(), - )); - }; - - 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), - ); - 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(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) - }) - } -} \ No newline at end of file + fn construct_array( + &mut self, + func_idx: NodeIdx, + input_exprs: &[Expression], + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { + // create a new list + self.parse_ctx_expr(&input_exprs[0], ctx)?; + self.apply_to_edges(ctx, loc, &|analyzer, 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(()); + } + 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, + 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, + is_symbolic: true, + is_return: false, + ty: ContextVarNode::from(len_cvar) + .underlying(analyzer) + .into_expr_err(loc)? + .ty + .clone(), + }; + + 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)); + + // update the length + if let Some(r) = arr.ref_range(analyzer).into_expr_err(loc)? { + let min = r.evaled_range_min(analyzer).into_expr_err(loc)?; + let max = r.evaled_range_max(analyzer).into_expr_err(loc)?; + + if let Some(mut rd) = min.maybe_range_dyn() { + rd.len = Elem::from(len_cvar); + arr.set_range_min(analyzer, Elem::ConcreteDyn(Box::new(rd))) + .into_expr_err(loc)?; + } + + if let Some(mut rd) = max.maybe_range_dyn() { + rd.len = Elem::from(len_cvar); + arr.set_range_min(analyzer, Elem::ConcreteDyn(Box::new(rd))) + .into_expr_err(loc)?; + } + } + + ctx.push_expr(ExprRet::Single(arr.into()), analyzer) + .into_expr_err(loc)?; + Ok(()) + }) + } + + fn construct_contract( + &mut self, + func_idx: NodeIdx, + input_exprs: &[Expression], + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { + // construct a new contract + if !input_exprs.is_empty() { + self.parse_ctx_expr(&input_exprs[0], ctx)?; + } + self.apply_to_edges(ctx, loc, &|analyzer, 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_struct( + &mut self, + func_idx: NodeIdx, + input_exprs: &[Expression], + loc: Loc, + ctx: ContextNode, + ) -> 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)); + + self.parse_inputs(ctx, loc, input_exprs)?; + self.apply_to_edges(ctx, loc, &|analyzer, 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(), + )); + }; + + 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)); + 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(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) + }) + } +} 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 93e0d859..b0143eb2 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,212 +1,214 @@ -use crate::{ - ContextBuilder, ExprErr, IntoExprErr, -}; +use crate::{ContextBuilder, ExprErr, IntoExprErr}; use graph::{ - nodes::{ - Builtin, Concrete, ContextNode, ContextVarNode, ExprRet, - }, + nodes::{Builtin, Concrete, ContextNode, ContextVarNode, ExprRet}, AnalyzerBackend, Node, SolcRange, VarType, }; use solang_parser::pt::{Expression, Loc}; -impl DynBuiltinCaller for T where T: AnalyzerBackend + Sized {} +impl DynBuiltinCaller for T where T: AnalyzerBackend + Sized +{} pub trait DynBuiltinCaller: AnalyzerBackend + Sized { - fn dyn_builtin_call(&mut self, func_name: String, input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { - match &*func_name { - "concat" => self.concat(&loc, input_exprs, ctx), - _ => Err(ExprErr::FunctionNotFound( - loc, - format!( - "Could not find builtin dynamic builtin function: \"{func_name}\", context: {}", - ctx.path(self), - ) - )) - } - } - + fn dyn_builtin_call( + &mut self, + func_name: String, + input_exprs: &[Expression], + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { + match &*func_name { + "concat" => self.concat(&loc, input_exprs, ctx), + _ => Err(ExprErr::FunctionNotFound( + loc, + format!( + "Could not find builtin dynamic builtin function: \"{func_name}\", context: {}", + ctx.path(self), + ), + )), + } + } - #[tracing::instrument(level = "trace", skip_all)] - fn concat( - &mut self, - loc: &Loc, - input_exprs: &[Expression], - ctx: ContextNode, - ) -> Result<(), ExprErr> { - input_exprs[1..].iter().try_for_each(|expr| { - self.parse_ctx_expr(expr, ctx)?; - self.apply_to_edges(ctx, *loc, &|analyzer, 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) - }) - })?; + #[tracing::instrument(level = "trace", skip_all)] + fn concat( + &mut self, + loc: &Loc, + input_exprs: &[Expression], + ctx: ContextNode, + ) -> Result<(), ExprErr> { + input_exprs[1..].iter().try_for_each(|expr| { + self.parse_ctx_expr(expr, ctx)?; + self.apply_to_edges(ctx, *loc, &|analyzer, 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, &|analyzer, 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(ctx, loc, start.clone(), &inputs[1..], None) - } else { - analyzer.match_concat(ctx, loc, start.clone(), &[], None) - } - } - }) - } + self.apply_to_edges(ctx, *loc, &|analyzer, 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(ctx, loc, start.clone(), &inputs[1..], None) + } else { + analyzer.match_concat(ctx, loc, start.clone(), &[], None) + } + } + }) + } - fn match_concat( - &mut self, - ctx: ContextNode, - loc: Loc, - curr: ExprRet, - inputs: &[ExprRet], - accum_node: Option, - ) -> Result<(), ExprErr> { - if let Some(accum_node) = accum_node { - match curr.flatten() { - ExprRet::Single(var) | ExprRet::SingleLiteral(var) => { - self.concat_inner(loc, accum_node, ContextVarNode::from(var))?; - ctx.push_expr(ExprRet::Single(accum_node.into()), self) - .into_expr_err(loc)?; - Ok(()) - } - ExprRet::Null => { - ctx.push_expr(ExprRet::Single(accum_node.into()), self) - .into_expr_err(loc)?; - Ok(()) - } - ExprRet::Multi(inner) => inner - .into_iter() - .try_for_each(|i| self.match_concat(ctx, loc, i, inputs, Some(accum_node))), - ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), - } - } else { - match curr.flatten() { - ExprRet::Single(var) | ExprRet::SingleLiteral(var) => { - let acc = ContextVarNode::from(var) - .as_tmp(loc, ctx, self) - .into_expr_err(loc)?; - inputs - .iter() - .map(|i| self.match_concat(ctx, loc, i.clone(), inputs, Some(acc))) - .collect::, ExprErr>>()?; - ctx.push_expr(ExprRet::Single(acc.into()), self) - .into_expr_err(loc)?; - Ok(()) - } - ExprRet::Null => Err(ExprErr::NoRhs( - loc, - "No input provided to concat function".to_string(), - )), - ExprRet::Multi(inner) => inner - .into_iter() - .try_for_each(|i| self.match_concat(ctx, loc, i, inputs, None)), - ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), - } - } - } + fn match_concat( + &mut self, + ctx: ContextNode, + loc: Loc, + curr: ExprRet, + inputs: &[ExprRet], + accum_node: Option, + ) -> Result<(), ExprErr> { + if let Some(accum_node) = accum_node { + match curr.flatten() { + ExprRet::Single(var) | ExprRet::SingleLiteral(var) => { + self.concat_inner(loc, accum_node, ContextVarNode::from(var))?; + ctx.push_expr(ExprRet::Single(accum_node.into()), self) + .into_expr_err(loc)?; + Ok(()) + } + ExprRet::Null => { + ctx.push_expr(ExprRet::Single(accum_node.into()), self) + .into_expr_err(loc)?; + Ok(()) + } + ExprRet::Multi(inner) => inner + .into_iter() + .try_for_each(|i| self.match_concat(ctx, loc, i, inputs, Some(accum_node))), + ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), + } + } else { + match curr.flatten() { + ExprRet::Single(var) | ExprRet::SingleLiteral(var) => { + let acc = ContextVarNode::from(var) + .as_tmp(loc, ctx, self) + .into_expr_err(loc)?; + inputs + .iter() + .map(|i| self.match_concat(ctx, loc, i.clone(), inputs, Some(acc))) + .collect::, ExprErr>>()?; + ctx.push_expr(ExprRet::Single(acc.into()), self) + .into_expr_err(loc)?; + Ok(()) + } + ExprRet::Null => Err(ExprErr::NoRhs( + loc, + "No input provided to concat function".to_string(), + )), + ExprRet::Multi(inner) => inner + .into_iter() + .try_for_each(|i| self.match_concat(ctx, loc, i, inputs, None)), + ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), + } + } + } - fn concat_inner( - &mut self, - loc: Loc, - accum: ContextVarNode, - right: ContextVarNode, - ) -> Result<(), ExprErr> { - match ( - accum.ty(self).into_expr_err(loc)?, - right.ty(self).into_expr_err(loc)?, - ) { - (VarType::Concrete(accum_cnode), VarType::Concrete(right_cnode)) => { - let new_ty = match ( - accum_cnode.underlying(self).into_expr_err(loc)?, - right_cnode.underlying(self).into_expr_err(loc)?, - ) { - (accum_node @ Concrete::String(..), right_node @ Concrete::String(..)) => { - let new_val = accum_node.clone().concat(right_node).unwrap(); - let new_cnode = self.add_node(Node::Concrete(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)); - VarType::Concrete(new_cnode.into()) - } - (a, b) => { - // Invalid solidity - return Err(ExprErr::InvalidFunctionInput(loc, format!("Type mismatch: {a:?} for left hand side and type: {b:?} for right hand side"))); - } - }; - accum.underlying_mut(self).into_expr_err(loc)?.ty = new_ty; - Ok(()) - } - (VarType::Concrete(accum_cnode), VarType::BuiltIn(_bn, Some(r2))) => { - let underlying = accum_cnode.underlying(self).into_expr_err(loc)?; - // let val = match underlying { - // Concrete::String(val) => { - // val - // .chars() - // .enumerate() - // .map(|(i, v)| { - // let idx = Elem::from(Concrete::from(U256::from(i))); - // let mut bytes = [0x00; 32]; - // v.encode_utf8(&mut bytes[..]); - // let v = Elem::from(Concrete::Bytes(1, H256::from(bytes))); - // (idx, v) - // }) - // .collect::>() - // } - // Concrete::DynBytes(val) => { - // val - // .iter() - // .enumerate() - // .map(|(i, v)| { - // let idx = Elem::from(Concrete::from(U256::from(i))); - // let mut bytes = [0x00; 32]; - // bytes[0] = *v; - // let v = Elem::from(Concrete::Bytes(1, H256::from(bytes))); - // (idx, v) - // }) - // .collect::>() - // } - // b => return Err(ExprErr::InvalidFunctionInput(loc, format!("Type mismatch: expected String or Bytes for concat input but found: {b:?}"))) - // }; - // TODO: Extend with bn + fn concat_inner( + &mut self, + loc: Loc, + accum: ContextVarNode, + right: ContextVarNode, + ) -> Result<(), ExprErr> { + match ( + accum.ty(self).into_expr_err(loc)?, + right.ty(self).into_expr_err(loc)?, + ) { + (VarType::Concrete(accum_cnode), VarType::Concrete(right_cnode)) => { + let new_ty = match ( + accum_cnode.underlying(self).into_expr_err(loc)?, + right_cnode.underlying(self).into_expr_err(loc)?, + ) { + (accum_node @ Concrete::String(..), right_node @ Concrete::String(..)) => { + let new_val = accum_node.clone().concat(right_node).unwrap(); + let new_cnode = self.add_node(Node::Concrete(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)); + VarType::Concrete(new_cnode.into()) + } + (a, b) => { + // Invalid solidity + return Err(ExprErr::InvalidFunctionInput(loc, format!("Type mismatch: {a:?} for left hand side and type: {b:?} for right hand side"))); + } + }; + accum.underlying_mut(self).into_expr_err(loc)?.ty = new_ty; + Ok(()) + } + (VarType::Concrete(accum_cnode), VarType::BuiltIn(_bn, Some(r2))) => { + let underlying = accum_cnode.underlying(self).into_expr_err(loc)?; + // let val = match underlying { + // Concrete::String(val) => { + // val + // .chars() + // .enumerate() + // .map(|(i, v)| { + // let idx = Elem::from(Concrete::from(U256::from(i))); + // let mut bytes = [0x00; 32]; + // v.encode_utf8(&mut bytes[..]); + // let v = Elem::from(Concrete::Bytes(1, H256::from(bytes))); + // (idx, v) + // }) + // .collect::>() + // } + // Concrete::DynBytes(val) => { + // val + // .iter() + // .enumerate() + // .map(|(i, v)| { + // let idx = Elem::from(Concrete::from(U256::from(i))); + // let mut bytes = [0x00; 32]; + // bytes[0] = *v; + // let v = Elem::from(Concrete::Bytes(1, H256::from(bytes))); + // (idx, v) + // }) + // .collect::>() + // } + // b => return Err(ExprErr::InvalidFunctionInput(loc, format!("Type mismatch: expected String or Bytes for concat input but found: {b:?}"))) + // }; + // TODO: Extend with bn - let range = SolcRange::from(underlying.clone()).unwrap(); - let min = range.min.clone().concat(r2.min.clone()); - let max = range.max.clone().concat(r2.max.clone()); - accum.set_range_min(self, min).into_expr_err(loc)?; - accum.set_range_max(self, max).into_expr_err(loc)?; + let range = SolcRange::from(underlying.clone()).unwrap(); + let min = range.min.clone().concat(r2.min.clone()); + let max = range.max.clone().concat(r2.max.clone()); + accum.set_range_min(self, min).into_expr_err(loc)?; + accum.set_range_max(self, max).into_expr_err(loc)?; - let new_ty = - VarType::BuiltIn(self.builtin_or_add(Builtin::String).into(), Some(range)); - accum.underlying_mut(self).into_expr_err(loc)?.ty = new_ty; - Ok(()) - } - (VarType::BuiltIn(_bn, Some(r)), VarType::BuiltIn(_bn2, Some(r2))) => { - // TODO: improve length calculation here - let min = r.min.clone().concat(r2.min.clone()); - let max = r.max.clone().concat(r2.max.clone()); - accum.set_range_min(self, min).into_expr_err(loc)?; - accum.set_range_max(self, max).into_expr_err(loc)?; - Ok(()) - } - (_, _) => Ok(()), - } - } -} \ No newline at end of file + let new_ty = + VarType::BuiltIn(self.builtin_or_add(Builtin::String).into(), Some(range)); + accum.underlying_mut(self).into_expr_err(loc)?.ty = new_ty; + Ok(()) + } + (VarType::BuiltIn(_bn, Some(r)), VarType::BuiltIn(_bn2, Some(r2))) => { + // TODO: improve length calculation here + let min = r.min.clone().concat(r2.min.clone()); + let max = r.max.clone().concat(r2.max.clone()); + accum.set_range_min(self, min).into_expr_err(loc)?; + accum.set_range_max(self, max).into_expr_err(loc)?; + Ok(()) + } + (_, _) => Ok(()), + } + } +} 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 4ebd7f7d..2b57ba7c 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,31 +1,46 @@ use crate::{ - ContextBuilder, ExprErr, FuncCaller, IntoExprErr, intrinsic_call::{ - MsgCaller, AbiCaller, AddressCaller, ArrayCaller, - BlockCaller, DynBuiltinCaller, PrecompileCaller, - SolidityCaller, TypesCaller, ConstructorCaller - } + AbiCaller, AddressCaller, ArrayCaller, BlockCaller, ConstructorCaller, DynBuiltinCaller, + MsgCaller, PrecompileCaller, SolidityCaller, TypesCaller, + }, + ContextBuilder, ExprErr, FuncCaller, IntoExprErr, }; use graph::{ - nodes::{ - Builtin, ContextNode, ExprRet, - }, + nodes::{Builtin, ContextNode, ExprRet}, AnalyzerBackend, Node, }; -use shared::{NodeIdx}; +use shared::NodeIdx; use solang_parser::pt::{Expression, Loc}; -pub trait CallerParts: AbiCaller + AddressCaller + ArrayCaller + - BlockCaller + DynBuiltinCaller + PrecompileCaller + - SolidityCaller + TypesCaller + ConstructorCaller + MsgCaller {} - -impl CallerParts for T where T: AbiCaller + AddressCaller + ArrayCaller + - BlockCaller + DynBuiltinCaller + PrecompileCaller + - SolidityCaller + TypesCaller + ConstructorCaller + MsgCaller {} - +pub trait CallerParts: + AbiCaller + + AddressCaller + + ArrayCaller + + BlockCaller + + DynBuiltinCaller + + PrecompileCaller + + SolidityCaller + + TypesCaller + + ConstructorCaller + + MsgCaller +{ +} +impl CallerParts for T where + T: AbiCaller + + AddressCaller + + ArrayCaller + + BlockCaller + + DynBuiltinCaller + + PrecompileCaller + + SolidityCaller + + TypesCaller + + ConstructorCaller + + MsgCaller +{ +} impl IntrinsicFuncCaller for T where T: AnalyzerBackend + Sized + CallerParts @@ -68,13 +83,15 @@ pub trait IntrinsicFuncCaller: self.dyn_builtin_call(func_name.name.clone(), input_exprs, *loc, ctx) } // msg - "gasleft" => { - self.msg_call(func_name.name.clone(), input_exprs, *loc, ctx) - } + "gasleft" => self.msg_call(func_name.name.clone(), input_exprs, *loc, ctx), // precompiles - "sha256" | "ripemd160" | "ecrecover" => { - self.precompile_call(func_name.name.clone(), func_idx, input_exprs, *loc, ctx) - } + "sha256" | "ripemd160" | "ecrecover" => self.precompile_call( + func_name.name.clone(), + func_idx, + input_exprs, + *loc, + ctx, + ), // solidity "keccak256" | "addmod" | "mulmod" | "require" | "assert" => { self.solidity_call(func_name.name.clone(), input_exprs, *loc, ctx) @@ -148,7 +165,10 @@ pub trait IntrinsicFuncCaller: } }) } - e => Err(ExprErr::FunctionNotFound(*loc, format!("Unhandled function call type: {e:?}"))), + e => Err(ExprErr::FunctionNotFound( + *loc, + format!("Unhandled function call type: {e:?}"), + )), } } } diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/mod.rs b/crates/solc-expressions/src/func_call/intrinsic_call/mod.rs index d3a50e38..0345a08a 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/mod.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/mod.rs @@ -20,4 +20,4 @@ pub use intrinsic_caller::*; pub use msg::*; pub use precompile::*; pub use solidity::*; -pub use types::*; \ No newline at end of file +pub use types::*; 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 249679d8..cf3f371f 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/msg.rs @@ -1,11 +1,7 @@ -use crate::{ - ExprErr, IntoExprErr, -}; +use crate::{ExprErr, IntoExprErr}; use graph::{ - nodes::{ - Builtin, ContextNode, ContextVar, ExprRet, - }, + nodes::{Builtin, ContextNode, ContextVar, ExprRet}, AnalyzerBackend, Node, }; @@ -13,27 +9,33 @@ use solang_parser::pt::{Expression, Loc}; impl MsgCaller for T where T: AnalyzerBackend + Sized {} pub trait MsgCaller: AnalyzerBackend + Sized { - fn msg_call(&mut self, func_name: String, _input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { + fn msg_call( + &mut self, + func_name: String, + _input_exprs: &[Expression], + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { match &*func_name { - "gasleft" => { - let var = ContextVar::new_from_builtin( - loc, - self.builtin_or_add(Builtin::Uint(64)).into(), - self, - ) - .into_expr_err(loc)?; - let cvar = self.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), self) - .into_expr_err(loc)?; - Ok(()) - } - _ => Err(ExprErr::FunctionNotFound( + "gasleft" => { + let var = ContextVar::new_from_builtin( + loc, + self.builtin_or_add(Builtin::Uint(64)).into(), + self, + ) + .into_expr_err(loc)?; + let cvar = self.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(cvar), self) + .into_expr_err(loc)?; + Ok(()) + } + _ => Err(ExprErr::FunctionNotFound( loc, format!( "Could not find builtin msg function: \"{func_name}\", context: {}", ctx.path(self), - ) - )) - } - } -} \ No newline at end of file + ), + )), + } + } +} 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 0e8e1e0e..9ddfc467 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/precompile.rs @@ -1,218 +1,182 @@ -use crate::{ - ContextBuilder, ExprErr, FuncCaller, IntoExprErr, -}; +use crate::{ContextBuilder, ExprErr, FuncCaller, IntoExprErr}; use graph::{ - nodes::{ - Builtin, Context, ContextNode, ContextVar, ContextVarNode, ExprRet, - }, + nodes::{Builtin, Context, ContextNode, ContextVar, ContextVarNode, ExprRet}, AnalyzerBackend, ContextEdge, Edge, Node, }; -use shared::{NodeIdx}; +use shared::NodeIdx; use solang_parser::pt::{Expression, Loc}; -impl PrecompileCaller for T where T: AnalyzerBackend + Sized {} +impl PrecompileCaller for T where T: AnalyzerBackend + Sized +{} pub trait PrecompileCaller: AnalyzerBackend + Sized { - fn precompile_call(&mut self, func_name: String, func_idx: NodeIdx, input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { + fn precompile_call( + &mut self, + func_name: String, + func_idx: NodeIdx, + input_exprs: &[Expression], + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { match &*func_name { - "sha256" => { - self.parse_ctx_expr(&input_exprs[0], ctx)?; - self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { - let Some(input) = - 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!(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, - ) - .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) - } - "ripemd160" => { - self.parse_ctx_expr(&input_exprs[0], ctx)?; - self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { - let Some(input) = - 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!(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, - ) - .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) - } - "ecrecover" => { - self.parse_inputs(ctx, loc, input_exprs)?; + "sha256" => { + self.parse_ctx_expr(&input_exprs[0], ctx)?; + self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { + let Some(input) = 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!(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, + ) + .into_expr_err(loc)?; + let cvar = analyzer.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(cvar), analyzer) + .into_expr_err(loc)?; + Ok(()) + }) + } + "ripemd160" => { + self.parse_ctx_expr(&input_exprs[0], ctx)?; + self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { + let Some(input) = 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!(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, + ) + .into_expr_err(loc)?; + let cvar = analyzer.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(cvar), analyzer) + .into_expr_err(loc)?; + Ok(()) + }) + } + "ecrecover" => { + self.parse_inputs(ctx, loc, input_exprs)?; - self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { - let cctx = Context::new_subctx( - ctx, - None, - loc, - None, - Some(func_idx.into()), - true, - 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), - ); + self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { + let cctx = Context::new_subctx( + ctx, + None, + loc, + None, + Some(func_idx.into()), + true, + 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 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(), + )); + }; - if matches!(input, ExprRet::CtxKilled(_)) { - ctx.push_expr(input, analyzer).into_expr_err(loc)?; - return Ok(()); - } + if matches!(input, ExprRet::CtxKilled(_)) { + ctx.push_expr(input, analyzer).into_expr_err(loc)?; + return Ok(()); + } - 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 inner_name = - inner_vals.into_iter().collect::>().join(", "); - let mut var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(Builtin::Address).into(), - analyzer, - ) - .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 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 inner_name = inner_vals.into_iter().collect::>().join(", "); + let mut var = ContextVar::new_from_builtin( + loc, + analyzer.builtin_or_add(Builtin::Address).into(), + analyzer, + ) + .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 rctx = Context::new_subctx( - call_ctx.into(), - Some(ctx), - loc, - None, - None, - true, - 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)?; - analyzer.add_edge( - ret_ctx, - call_ctx, - Edge::Context(ContextEdge::Continue), - ); + let rctx = Context::new_subctx( + call_ctx.into(), + Some(ctx), + loc, + None, + None, + true, + 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)?; + analyzer.add_edge(ret_ctx, call_ctx, Edge::Context(ContextEdge::Continue)); - 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(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)); - ContextNode::from(ret_ctx) - .push_expr(ExprRet::Single(tmp_ret.into()), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) - } - _ => Err(ExprErr::FunctionNotFound( + ContextNode::from(ret_ctx) + .push_expr(ExprRet::Single(tmp_ret.into()), analyzer) + .into_expr_err(loc)?; + Ok(()) + }) + } + _ => Err(ExprErr::FunctionNotFound( loc, format!( "Could not find precompile function: \"{func_name}\", context: {}", ctx.path(self), - ) - )) - } - } + ), + )), + } + } } 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 4b5e7edb..28c48ddb 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/solidity.rs @@ -1,11 +1,7 @@ -use crate::{ - require::Require, ContextBuilder, ExprErr, FuncCaller, IntoExprErr, -}; +use crate::{require::Require, ContextBuilder, ExprErr, FuncCaller, IntoExprErr}; use graph::{ - nodes::{ - Builtin, ContextNode, ContextVar, ExprRet, - }, + nodes::{Builtin, ContextNode, ContextVar, ExprRet}, AnalyzerBackend, Node, }; @@ -13,79 +9,81 @@ use solang_parser::pt::{Expression, Loc}; impl SolidityCaller for T where T: AnalyzerBackend + Sized {} pub trait SolidityCaller: AnalyzerBackend + Sized { - fn solidity_call(&mut self, func_name: String, input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { + fn solidity_call( + &mut self, + func_name: String, + input_exprs: &[Expression], + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { match &*func_name { - "keccak256" => { - self.parse_ctx_expr(&input_exprs[0], ctx)?; - self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { - let Some(_input) = - 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(), - )); - }; - let var = ContextVar::new_from_builtin( - loc, - analyzer.builtin_or_add(Builtin::Bytes(32)).into(), - analyzer, - ) - .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 - self.parse_inputs(ctx, loc, input_exprs)?; + "keccak256" => { + self.parse_ctx_expr(&input_exprs[0], ctx)?; + self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { + let Some(_input) = 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(), + )); + }; + let var = ContextVar::new_from_builtin( + loc, + analyzer.builtin_or_add(Builtin::Bytes(32)).into(), + analyzer, + ) + .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 + self.parse_inputs(ctx, loc, input_exprs)?; - self.apply_to_edges(ctx, loc, &|analyzer, 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, - ) - .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) - } - "mulmod" => { - // TODO: actually calcuate this if possible - self.parse_inputs(ctx, loc, input_exprs)?; - self.apply_to_edges(ctx, loc, &|analyzer, 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, - ) - .into_expr_err(loc)?; - let cvar = analyzer.add_node(Node::ContextVar(var)); - ctx.push_expr(ExprRet::Single(cvar), analyzer) - .into_expr_err(loc)?; - Ok(()) - }) - } - "require" | "assert" => { - self.apply_to_edges(ctx, loc, &|analyzer, ctx, _loc| { - analyzer.handle_require(input_exprs, ctx) - }) - } - _ => Err(ExprErr::FunctionNotFound( + self.apply_to_edges(ctx, loc, &|analyzer, 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, + ) + .into_expr_err(loc)?; + let cvar = analyzer.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(cvar), analyzer) + .into_expr_err(loc)?; + Ok(()) + }) + } + "mulmod" => { + // TODO: actually calcuate this if possible + self.parse_inputs(ctx, loc, input_exprs)?; + self.apply_to_edges(ctx, loc, &|analyzer, 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, + ) + .into_expr_err(loc)?; + let cvar = analyzer.add_node(Node::ContextVar(var)); + ctx.push_expr(ExprRet::Single(cvar), analyzer) + .into_expr_err(loc)?; + Ok(()) + }) + } + "require" | "assert" => self.apply_to_edges(ctx, loc, &|analyzer, ctx, _loc| { + analyzer.handle_require(input_exprs, ctx) + }), + _ => Err(ExprErr::FunctionNotFound( loc, format!( "Could not find builtin solidity function: \"{func_name}\", context: {}", ctx.path(self), - ) - )) - } - } -} \ No newline at end of file + ), + )), + } + } +} 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 611d40d6..9751151f 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/types.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/types.rs @@ -1,21 +1,23 @@ -use crate::{ - ContextBuilder, ExprErr, FuncCaller, IntoExprErr, -}; +use crate::{ContextBuilder, ExprErr, FuncCaller, IntoExprErr}; use graph::{ elem::*, - nodes::{ - BuiltInNode, Builtin, ContextNode, ContextVar, ContextVarNode, ExprRet, TyNode, - }, + nodes::{BuiltInNode, Builtin, ContextNode, ContextVar, ContextVarNode, ExprRet, TyNode}, AnalyzerBackend, GraphBackend, Node, Range, SolcRange, VarType, }; -use shared::{NodeIdx}; +use shared::NodeIdx; use solang_parser::pt::{Expression, Loc}; impl TypesCaller for T where T: AnalyzerBackend + Sized {} pub trait TypesCaller: AnalyzerBackend + Sized { - fn types_call(&mut self, func_name: String, input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { + fn types_call( + &mut self, + func_name: String, + input_exprs: &[Expression], + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { match &*func_name { "type" => self.parse_ctx_expr(&input_exprs[0], ctx), "wrap" => { @@ -25,9 +27,7 @@ pub trait TypesCaller: AnalyzerBackend + S self.parse_inputs(ctx, loc, input_exprs)?; self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { - let Some(input) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { + 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(), @@ -36,16 +36,11 @@ pub trait TypesCaller: AnalyzerBackend + S 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 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 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), @@ -62,9 +57,7 @@ pub trait TypesCaller: AnalyzerBackend + S "unwrap" => { self.parse_inputs(ctx, loc, input_exprs)?; self.apply_to_edges(ctx, loc, &|analyzer, ctx, loc| { - let Some(input) = - ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - else { + 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(), @@ -95,8 +88,7 @@ pub trait TypesCaller: AnalyzerBackend + S .into_expr_err(loc)? ); - let cvar = - ContextVarNode::from(analyzer.add_node(Node::ContextVar(var))); + let cvar = ContextVarNode::from(analyzer.add_node(Node::ContextVar(var))); cvar.set_range_min(analyzer, Elem::from(to_be_unwrapped)) .into_expr_err(loc)?; cvar.set_range_max(analyzer, Elem::from(to_be_unwrapped)) @@ -119,12 +111,19 @@ pub trait TypesCaller: AnalyzerBackend + S format!( "Could not find builtin types function: \"{func_name}\", context: {}", ctx.path(self), - ) - )) + ), + )), } } - fn cast(&mut self, ty: Builtin, func_idx: NodeIdx, input_exprs: &[Expression], loc: Loc, ctx: ContextNode) -> Result<(), ExprErr> { + fn cast( + &mut self, + ty: Builtin, + func_idx: NodeIdx, + input_exprs: &[Expression], + loc: Loc, + ctx: ContextNode, + ) -> Result<(), ExprErr> { // it is a cast fn cast_match( ctx: ContextNode, @@ -135,9 +134,7 @@ pub trait TypesCaller: AnalyzerBackend + S func_idx: NodeIdx, ) -> Result<(), ExprErr> { match ret { - ExprRet::CtxKilled(kind) => { - ctx.kill(analyzer, loc, kind).into_expr_err(loc) - } + ExprRet::CtxKilled(kind) => ctx.kill(analyzer, loc, kind).into_expr_err(loc), ExprRet::Null => Ok(()), ExprRet::Single(cvar) | ExprRet::SingleLiteral(cvar) => { let new_var = ContextVarNode::from(cvar) @@ -151,8 +148,7 @@ pub trait TypesCaller: AnalyzerBackend + S .range(analyzer) .into_expr_err(loc)? { - let curr_range = - SolcRange::try_from_builtin(ty).expect("No default range"); + let curr_range = SolcRange::try_from_builtin(ty).expect("No default range"); let mut min = r .range_min() .into_owned() @@ -166,8 +162,7 @@ pub trait TypesCaller: AnalyzerBackend + S max.cache_maximize(analyzer).into_expr_err(loc)?; - let existing_max = - r.evaled_range_max(analyzer).into_expr_err(loc)?; + let existing_max = r.evaled_range_max(analyzer).into_expr_err(loc)?; // Check if the max value has changed once the cast is applied. // If it hasnt, then the cast had no effect and we should adjust the naming // to not muddle the CLI @@ -188,8 +183,7 @@ pub trait TypesCaller: AnalyzerBackend + S // cast the range exclusions - TODO: verify this is correct let mut exclusions = r.range_exclusions(); exclusions.iter_mut().for_each(|range| { - *range = - range.clone().cast(curr_range.range_min().into_owned()); + *range = range.clone().cast(curr_range.range_min().into_owned()); }); new_var .set_range_exclusions(analyzer, exclusions) @@ -220,4 +214,4 @@ pub trait TypesCaller: AnalyzerBackend + S cast_match(ctx, loc, analyzer, &ty, ret, func_idx) }) } -} \ No newline at end of file +}